效果android
主要步驟:app
1. 在xml佈局裏擺放內容. include
2. 在自定義ViewGroup裏, 進行measure測量, layout佈局
3. 響應用戶的觸摸事件
4. int scrollX = (int) (downX - moveX);
5. getScrollX()獲取當前滾動到的位置
6. 平滑動畫ide
先看佈局佈局
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_contentspa
<?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_maincode
<?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.xmlxml
layout_left的狀態選擇器blog
<?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); } }