效果

主要步骤:

1. 在xml布局里摆放内容. include
    2. 在自定义ViewGroup里, 进行measure测量, layout布局
    3. 响应用户的触摸事件
    4. int scrollX = (int) (downX - moveX);
    5. getScrollX()获取当前滚动到的位置
    6. 平滑动画

先看布局

layout_left

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="240dp"
android:layout_height="match_parent"> <LinearLayout
android:layout_width="240dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/menu_bg">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/selector_bg"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:text="新闻"
android:clickable="true"
android:textColor="#ADCFD6"
android:drawableLeft="@drawable/tab_news"
android:textSize="18sp"/> </LinearLayout>
</ScrollView>

layout_content

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/top_bar_bg">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/main_back"
android:background="@null"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@drawable/top_bar_divider"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="每日新闻"
android:textSize="25sp"
android:layout_gravity="center"/> </LinearLayout> </LinearLayout>

activity_main

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.xw.mystudeydemo.MainActivity"> <com.example.mystudydemo.SlideMeun
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/layout_left"/>
<include layout="@layout/layoit_content"/>
</com.example.mystudydemo.SlideMeun>
</RelativeLayout>

布局中需要注意的是,layout_content中我们把整体布局背景设置成了android:background="#FFFFFF",这是因为我们需要他完全遮住下面的那层layout_left.xml

layout_left的状态选择器

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@color/bg_pressed"/>
<item android:drawable="@android:color/transparent"/>
</selector>

二,写自定义控件

package com.example.mystudydemo;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller; /**
* Created by xw on 2016/8/6.
*/
public class SlideMeun extends ViewGroup{
private float downX;
private float moveX;
private Scroller scroller;
/**
* 侧滑面板控件, 抽屉面板.
* @author poplar
*
* 测量 摆放 绘制
measure -> layout -> draw
| | |
onMeasure -> onLayout -> onDraw 重写这些方法, 实现自定义控件 View流程
onMeasure() (在这个方法里指定自己的宽高) -> onDraw() (绘制自己的内容) ViewGroup流程
onMeasure() (指定自己的宽高, 所有子View的宽高)-> onLayout() (摆放所有子View) -> onDraw() (绘制内容)
*
*/ public SlideMeun(Context context) {
super(context);
init(context);
} public SlideMeun(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
} public SlideMeun(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
// 初始化滚动器
scroller=new Scroller(context);
} /**
* 测量并设置 所有子View的宽高
* widthMeasureSpec: 当前控件的宽度测量规则
* heightMeasureSpec: 当前控件的高度测量规则
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 指定左面板的宽高
View leftMenu=getChildAt(0);
leftMenu.measure(leftMenu.getLayoutParams().width,heightMeasureSpec);
// 指定主面板的宽高
View mainMenu=getChildAt(1);
mainMenu.measure(mainMenu.getLayoutParams().width,heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 摆放内容, 左面板,隐藏
View leftMenu=getChildAt(0);
leftMenu.layout(-leftMenu.getMeasuredWidth(),0,0,b); // 主面板
View mainMenu=getChildAt(1);
mainMenu.layout(l,t,r,b); } @Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
downX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
moveX =event.getX(); // 将要发生的偏移量/变化量
int scrollX=(int)(downX-moveX);
// 计算将要滚动到的位置, 判断是否会超出去, 超出去了.不执行scrollBy
// getScrollX() 当前滚动到的位置
int newposition=getScrollX()+scrollX;
if(newposition<-getChildAt(0).getMeasuredWidth()){ // 限定左边界
scrollTo(-getChildAt(0).getMeasuredWidth(), 0);
}
else if(newposition>0){ // 限定右边界
scrollTo(0, 0);
}
else{
// 让变化量生效
scrollBy(scrollX,0);
}
downX=moveX;
break;
case MotionEvent.ACTION_UP: int leftcenter=-(int)(getChildAt(0).getMeasuredWidth()/2.0f);
int startX=getScrollX();
int dx=0;
// 根据当前滚动到的位置, 和左面板的一半进行比较
if(startX<leftcenter){
// 打开, 切换成菜单面板
dx=-getChildAt(0).getMeasuredWidth()-startX; }
else{
// 关闭, 切换成主面板
dx=0-startX; }
int duration=Math.abs(dx*10);
scroller.startScroll(startX, 0, dx, 0, duration);
invalidate(); //重绘界面 -> drawChild() -> computeScroll();
break; default:
break;
} return true;//消费事件
} @Override ////2. 维持动画的继续
public void computeScroll() { super.computeScroll();
if(scroller.computeScrollOffset()){
int currX=scroller.getCurrX();
scrollTo(currX, 0);
invalidate();
}
}
}

三, MainActivity

package com.example.mystudydemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main); } }

自定义控件学习——仿qq侧滑栏的更多相关文章

  1. android开发学习 ------- 仿QQ侧滑效果的实现

    需要做一个仿QQ侧滑删除的一个效果: 一开始是毫无头绪,百度找思路,找到  https://blog.csdn.net/xiaxiazaizai01/article/details/53036994  ...

  2. iOS仿QQ侧滑菜单、登录按钮动画、仿斗鱼直播APP、城市选择器、自动布局等源码

    iOS精选源码 QQ侧滑菜单,右滑菜单,QQ展开菜单,QQ好友分组 登录按钮 3分钟快捷创建高性能轮播图 ScrollView嵌套ScrolloView(UITableView .UICollecti ...

  3. Android学习总结——DrawerLayout 侧滑栏点击事件穿透

    使用DrawerLayout实现侧滑栏功能时,点击侧滑栏空白处时,主界面会获得事件. 解决方法:侧滑栏布局添加 android:clickable="true"

  4. 仿QQ侧滑菜单<大自然的搬运工-代码不是我的>

    1.记录下效果图 2.二个工具类 package myapplication.com.myapplicationfortest.utils; import android.util.Log; /** ...

  5. OC仿QQ侧滑

    之前做侧滑用的控件的DDMenu,总感觉好像差了点什么,自己尝试写了一个,三层叠加,感觉效果不理想,偶然间看到了一篇博客,与大家分享,再次申明,该代码不是我写的,只是为了给自己留一个查找资料的机会 下 ...

  6. 小程序仿QQ侧滑例子

    缩放:wxml <!--page/one/index.wxml--> <view class="page"> <view class="pa ...

  7. 如鹏网仿QQ侧滑菜单:ResideMenu组件的使用笔记整理+Demo

    ResideMenu菜单 课堂笔记: https://github.com/SpecialCyCi/AndroidResideMenu Github:如何使用开源组件1. 下载 下载方式: 1. 项目 ...

  8. 手势仿QQ侧滑---秀清

    // // SlideViewController.h // qqcehua // // Created by 张秀清 on 15/5/25. // Copyright (c) 2015年 张秀清. ...

  9. 史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。

    重要的话 开头说,not for the RecyclerView or ListView, for the Any ViewGroup. 本控件不依赖任何父布局,不是针对 RecyclerView. ...

随机推荐

  1. ARM官方《CMSIS-RTOS教程》之线程Threads

    创建线程Creating Threads 一旦RTOS开始运行,就会有很多系统调用来管理和控制活跃的线程.默认情况下,main()函数自动被创建为第一个可运行的线程.在第一个例子里我们使用main() ...

  2. 以Append方式打开文件,设置偏移量无效

    #include<stdio.h> int main() { FILE * fd = fopen("btoo1.c", "ab+"); fpos_t ...

  3. 手动创建DataTable并添加数据

    DataTable dt=new DataTable(); DataColumn dc=dt.Columns.Add("OBJECTID",Type.GetType("S ...

  4. [Recompose] Create Stream Behaviors to Push Props in React Components with mapPropsStream

    Rather than using Components to push streams into other Components, mapPropsStream allows you to cre ...

  5. Find problem in eXtremeDB

    class table1 { char<8>    f1; char<80>  f2; uint4        f3; uint4        f4; double     ...

  6. js实现小时钟,js中Date对象的使用?

    介绍一下js中Date对象的使用 dateObj = new Date() dateObj = new Date(dateValue) dateObj = new Date(year,month,da ...

  7. 浏览器Console创建canvas base64 png图片

    火狐中运行:console.log var canvas = document.createElement('canvas'); canvas.width =1 canvas.height =1 ca ...

  8. Centos7 zabbix3.4.6的安装部署 (一)

    部署zabbix主要为了监控日常主机.服务器.Web服务器.数据库.路由器.交换机等日常设备,功能强大,稳定性好 现在通过使用虚拟机VM搭建的Centos7部署zabbix服务 实现简单监控功能 本章 ...

  9. export和source的区别

    1.执行脚本是在一个子shell环境运行的,脚本执行完后该子shell自动退出. 2.执行脚本中的系统环境变量(用export定义的变量)才会被复制到子shell中. 3.一个shell中的系统环境变 ...

  10. 请允许我成为你的夏季——shiro、jdbcInsertall

    这两天总是觉得自己被关进了一个大笼子,日子拮据.生活不就是这样吗,一边觉得自己很差劲,一边又想成为一个更好的自己.可那又有什么办法呢,万物皆有裂痕,但那又怎样,那是光照进来的地方啊. 开始学习shir ...