效果
主要步骤:
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); } }
原文:http://www.cnblogs.com/xurui1995/p/5745111.html