自定义android侧滑菜单

版权所有,禁止匿名转载;禁止商业使用。

这里实现两种侧滑菜单效果,第一种拖拽内容部分,菜单像是被拖出来的感觉的这种效果,第二种是拖拽内容部分,菜单在内容后面不动,感觉有一种层次感的效果,如下

20141119160504532.png

qMj2An.png

第一种效果的代码实现如下:

package com.tenghu.customsideslip.menu.view;
import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
/**
 * Created by Arvin_Li on 2014/11/19.
 */
public class ScrollSideslipMenu extends LinearLayout{
  private static final int SNAP_VELOCITY=200;//速度阈值
  private View mMenu;//菜单布局
  private View mContent;//内容布局
  private int screenWidth;//屏幕宽度
  private int menuWidth;//菜单宽度
  private int leftEdge;//左边界
  private int rightEdge=0;//右边界,值恒为0
  private float xUp;//手指抬起时记录横坐标
  private float xDown;//手指按下时记录横坐标
  private float xMove;//手指移动时记录横坐标
  private int toRightPaddingWidth=50;//菜单完全显示时,留给内容的宽度
  private LayoutParams menuParams;//菜单布局参数
  private boolean once=false;//初始化数据只加载一次
  private boolean isShowMenu;//是否显示菜单
  private VelocityTracker velocityTracker;//速度跟踪器
  public ScrollSideslipMenu(Context context) {
    super(context);
    initWindowWidth(context);
  }
  public ScrollSideslipMenu(Context context, AttributeSet attrs) {
    super(context, attrs);
    initWindowWidth(context);
  }
  /**
   * 初始化获取屏幕宽度
   */
  private void initWindowWidth(Context context){
    WindowManager windowManager= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics displayMetrics=new DisplayMetrics();
    windowManager.getDefaultDisplay().getMetrics(displayMetrics);
    //获取屏幕宽度
    screenWidth=displayMetrics.widthPixels;
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if(!once){
      mMenu=this.getChildAt(0);//获取菜单布局
      mContent=this.getChildAt(1);//获取内容布局
      menuParams= (LayoutParams) mMenu.getLayoutParams();//获取菜单布局参数
      menuWidth=menuParams.width=screenWidth-toRightPaddingWidth;//设置菜单宽度
      mContent.getLayoutParams().width=screenWidth;//设置内容宽度
      leftEdge=-menuWidth;//左边界
      menuParams.leftMargin=leftEdge;//默认菜单不显示
      once=true;
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    createVelocityTracker(event);
    switch (event.getAction()){
      //按下
      case MotionEvent.ACTION_DOWN:
        xDown=event.getRawX();//记录按下时的横坐标
        break;
      //移动
      case MotionEvent.ACTION_MOVE:
        //记录移动时的横坐标
        xMove=event.getRawX();
        //计算移动时与按下时的距离
        int moveDistanceX= (int) (xMove-xDown);
        if(isShowMenu){
          menuParams.leftMargin=moveDistanceX;
        }else{
          menuParams.leftMargin=leftEdge+moveDistanceX;
        }
        if(menuParams.leftMargin<leftEdge){
          menuParams.leftMargin=leftEdge;
        }
        if(menuParams.leftMargin>rightEdge){
          menuParams.leftMargin=rightEdge;
        }
        mMenu.setLayoutParams(menuParams);//设置参数
        break;
      //抬起
      case MotionEvent.ACTION_UP:
        //记录抬起时的横坐标
        xUp=event.getRawX();
        //计算抬起时与按下时的距离
        int upDistanceX= (int) (xUp-xDown);
        if(upDistanceX>0&&!isShowMenu){
          if(upDistanceX>menuWidth/2||getScrollVelocity()>SNAP_VELOCITY){
            scrollToMenu();
          }else{
            scrollToContent();
          }
        }else if(upDistanceX<0&&isShowMenu){
          if(Math.abs(upDistanceX)>menuWidth/2||getScrollVelocity()>SNAP_VELOCITY){
            scrollToContent();
          }else{
            scrollToMenu();
          }
        }
        mMenu.setLayoutParams(menuParams);
        break;
    }
    return true;
  }
  /**
   * 滚动内容部分
   */
  private void scrollToContent(){
    new ScrollTask().execute(-30);
  }
  /**
   * 滚动菜单部分
   */
  private void scrollToMenu(){
    new ScrollTask().execute(30);
  }
  /**
   * 创建速度阈值
   */
  private void createVelocityTracker(MotionEvent event){
    if(null==velocityTracker){
      velocityTracker=VelocityTracker.obtain();
    }
    velocityTracker.addMovement(event);
  }
  /**
   * 获取滚动时的速度
   * @return
   */
  private int getScrollVelocity(){
    velocityTracker.computeCurrentVelocity(1000);
    int velocity= (int) velocityTracker.getXVelocity();//获取横向速度
    return Math.abs(velocity);
  }
  /**
   * 创建一个异步滚动任务
   */
  class ScrollTask extends AsyncTask<Integer,Integer,Integer>{
    @Override
    protected Integer doInBackground(Integer... params) {
      int leftMargin=menuParams.leftMargin;
      while(true){
        leftMargin=leftMargin+params[0];
        if(leftMargin<leftEdge){
          leftMargin=leftEdge;
          break;
        }
        if(leftMargin>rightEdge){
          leftMargin=rightEdge;
          break;
        }
        publishProgress(leftMargin);
        sleep(20);
      }
      if(params[0]>0){
        isShowMenu=true;
      }else{
        isShowMenu=false;
      }
      return leftMargin;
    }
    /**
     * 执行结束
     * @param integer
     */
    @Override
    protected void onPostExecute(Integer integer) {
      menuParams.leftMargin=integer;
      mMenu.setLayoutParams(menuParams);
    }
    /**
     * 执行doInBackground中的publishProgress调用该方法
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
      menuParams.leftMargin=values[0];
      mMenu.setLayoutParams(menuParams);
    }
  }
  /**
   * 当前线程睡眠多少毫秒
   * @param millis
   */
  private void sleep(long millis){
    try {
      Thread.sleep(millis);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>

<com.tenghu.customsideslip.menu.view.ScrollSideslipMenu xmlns:android="
http://schemas.android.com/apk/res/android
"

  android:layout_width="match_parent"

  android:layout_height="match_parent"

  android:orientation="horizontal"

  android:background="@drawable/bg_01">

  <!--引用菜单布局文件-->

  <include layout="@layout/left_menu"/>

  <LinearLayout

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="@drawable/bg_02"></LinearLayout>

</com.tenghu.customsideslip.menu.view.ScrollSideslipMenu>

第二种效果实现:

package com.tenghu.customsideslip.menu.view;
import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.widget.RelativeLayout;
/**
 * Created by Arvin_Li on 2014/11/19.
 */
public class CustomSideslipMenu extends RelativeLayout {
  private static final int SNAP_VELOCITY=200;//手势滑动的速度
  //屏幕宽度
  private int mScreenWidth;
  //菜单布局
  private View mMenu;
  //主体内容部分
  private View mContent;
  //声明菜单宽度
  private int menuWidth;
  //菜单完全显示时,留给内容部分的宽度
  private int toRightPaddingWidth=50;
  //主体内容布局参数
  private LayoutParams contentParams;
  //主体内容滑动到左边缘,由菜单宽度来决定
  private int leftEdge;
  //菜单显示时,主体内容到右边界,值恒为0
  private int rightEdge=0;
  private VelocityTracker velocityTracker;//声明速度跟踪器
  private float xDown;//记录手指按下的横坐标
  private float xUp;//记录手指抬起时的横坐标
  private float xMove;//记录手指移动时的横坐标
  private boolean once=false;//只执行一次
  private boolean isShowMenu=true;//是否显示菜单
  public CustomSideslipMenu(Context context) {
    super(context);
    init(context);
  }
  public CustomSideslipMenu(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
  }
  /**
   * 初始化
   */
  private void init(Context context){
    //获取窗口管理类
    WindowManager windowManager= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    //创建DisplayMetrics
    DisplayMetrics displayMetrics=new DisplayMetrics();
    windowManager.getDefaultDisplay().getMetrics(displayMetrics);
    //获取屏幕宽度
    mScreenWidth=displayMetrics.widthPixels;
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if(!once){
      //获取菜单布局
      mMenu=this.getChildAt(0);
      //获取主体内容布局
      mContent=this.getChildAt(1);
      contentParams= (LayoutParams) mContent.getLayoutParams();//获取主体菜单参数
      //菜单宽度
      menuWidth=mMenu.getLayoutParams().width=mScreenWidth-toRightPaddingWidth;
      //设置主体内容的宽度
      mContent.getLayoutParams().width=mScreenWidth;
      leftEdge=menuWidth;//设置左边界
      once=true;
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    //调用创建速度跟踪器
    createVelocityTracker(event);
    switch (event.getAction()){
      //手指按下
      case MotionEvent.ACTION_DOWN:
        xDown=event.getRawX();
        break;
      //手指移动
      case MotionEvent.ACTION_MOVE:
        xMove=event.getRawX();
        //计算移动距离
        int distanceX= (int) (xMove-xDown);
        if(isShowMenu){
          contentParams.leftMargin=distanceX;
          contentParams.rightMargin=-distanceX;
        }else{
          contentParams.leftMargin=leftEdge+distanceX;
        }
        if(contentParams.leftMargin>leftEdge){
          contentParams.leftMargin=leftEdge;
          contentParams.rightMargin=-leftEdge;
        }
        if(contentParams.leftMargin<rightEdge){
          contentParams.leftMargin=rightEdge;
          contentParams.rightMargin=rightEdge;
        }
        mContent.setLayoutParams(contentParams);//测试参数
        break;
      //手指抬起
      case MotionEvent.ACTION_UP:
        xUp=event.getRawX();//手指抬起时横坐标
        //计算抬起时与按下时的距离
        int upDistanceX=(int) (xDown-xUp);
        if(upDistanceX>0&&!isShowMenu){
            if(upDistanceX>menuWidth/2||getScrollVelocity()>SNAP_VELOCITY){
              scrollToMenu();
            }else{
              scrollToContent();
            }
        }else if(upDistanceX<0&&isShowMenu){
          if(Math.abs(upDistanceX)>menuWidth/2||getScrollVelocity()>SNAP_VELOCITY){
            scrollToContent();
          }else{
            scrollToMenu();
          }
          //手指抬起时销毁
          recycleVelocityTracker();
        }
        break;
    }
    return true;
  }
  /**
   * 滚动菜单
   */
  private void scrollToMenu(){
    new ScrollTask().execute(-30);
  }
  /**
   * 滚动内容
   */
  private void scrollToContent(){
    new ScrollTask().execute(30);
  }
  /**
   * 获取速度
   * @return
   */
  private int getScrollVelocity(){
    velocityTracker.computeCurrentVelocity(1000);
    int velocity= (int) velocityTracker.getXVelocity();
    return Math.abs(velocity);
  }
  /**
   * 销毁速度跟踪器
   */
  private void recycleVelocityTracker(){
    if (null != velocityTracker) {
      velocityTracker.recycle();
      velocityTracker = null;
    }
  }

  /**
   * 创建速度跟踪器
   */
  private void createVelocityTracker(MotionEvent event){
    if(null==velocityTracker){
      velocityTracker=velocityTracker.obtain();
    }
    velocityTracker.addMovement(event);
  }
  /**
   * 创建异步滚动任务类
   */
  class ScrollTask extends AsyncTask<Integer,Integer,Integer>{
    @Override
    protected Integer doInBackground(Integer... params) {
      int leftMargin=contentParams.leftMargin;
      while(true){
        leftMargin=leftMargin+params[0];
        if(leftMargin>leftEdge){
          leftMargin=leftEdge;
          break;
        }
        if(leftMargin<rightEdge){
          leftMargin=rightEdge;
          break;
        }
        if(params[0]<0){
          isShowMenu=true;
           // leftMargin=rightEdge;
        }else{
          isShowMenu=false;
        }
        publishProgress(leftMargin);
        sleep(20);
      }
      return leftMargin;
    }
    @Override
    protected void onProgressUpdate(Integer... values) {
      contentParams.leftMargin=values[0];
       contentParams.rightMargin=-values[0];
      mContent.setLayoutParams(contentParams);
    }
    @Override
    protected void onPostExecute(Integer integer) {
      contentParams.leftMargin=integer;
       contentParams.rightMargin=-integer;
      mContent.setLayoutParams(contentParams);
    }
  }
  /**
   * 当前线程睡眠多少毫秒
   * @param millis
   */
  private void sleep(long millis){
    try {
      Thread.sleep(millis);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

布局文件:

<com.tenghu.customsideslip.menu.view.CustomSideslipMenu 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"

  android:background="@drawable/bg_01"

  tools:context=".MainActivity">
  <include layout="@layout/left_menu" />
  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="@drawable/bg_02"
      android:orientation="vertical">
      <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="testScrollMenu"
        android:text="测试第二种侧滑菜单"/>
    </LinearLayout>
  </LinearLayout>
</com.tenghu.customsideslip.menu.view.CustomSideslipMenu>
菜单布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center_vertical">
  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="30dp"
    android:orientation="vertical">
    <RelativeLayout
      android:layout_width="wrap_content"
      android:layout_height="50dp"
      android:layout_marginBottom="10dp">
      <ImageView
        android:id="@+id/iv_img_01"
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:src="@drawable/app_01" />
      <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/iv_img_01"
        android:gravity="center_vertical"
        android:text="第一个Item" />
    </RelativeLayout>
    <RelativeLayout
      android:layout_width="wrap_content"
      android:layout_height="50dp"
      android:layout_marginBottom="10dp">
      <ImageView
        android:id="@+id/iv_img_02"
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:src="@drawable/app_02" />
      <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/iv_img_02"
        android:gravity="center_vertical"
        android:text="第二个Item" />
    </RelativeLayout>
    <RelativeLayout
      android:layout_width="wrap_content"
      android:layout_height="50dp"
      android:layout_marginBottom="10dp">
      <ImageView
        android:id="@+id/iv_img_03"
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:src="@drawable/app_03" />
      <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/iv_img_03"
        android:gravity="center_vertical"
        android:text="第三个Item" />
    </RelativeLayout>
    <RelativeLayout
      android:layout_width="wrap_content"
      android:layout_height="50dp"
      android:layout_marginBottom="10dp">
      <ImageView
        android:id="@+id/iv_img_04"
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:src="@drawable/app_04" />
      <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/iv_img_04"
        android:gravity="center_vertical"
        android:text="第四个Item" />
    </RelativeLayout>
    <RelativeLayout
      android:layout_width="wrap_content"
      android:layout_height="50dp"
      android:layout_marginBottom="10dp">
      <ImageView
        android:id="@+id/iv_img_05"
        android:layout_width="50dp"
        android:layout_height="match_parent"
        android:src="@drawable/app_05" />
      <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/iv_img_05"
        android:gravity="center_vertical"
        android:text="第五个Item" />
    </RelativeLayout>
  </LinearLayout>
</RelativeLayout>

这里的菜单,可以是用ListView来布局,做测试就没有那样做了,所有代码全部在这里了,可以看出,两种效果只是继承了不通的布局,感觉第二种效果在设置背景时有点问题,就是在这里

<LinearLayout

    android:layout_width="match_parent"

    android:layout_height="match_parent">
    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="@drawable/bg_02"
      android:orientation="vertical">
      <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="testScrollMenu"
        android:text="测试第二种侧滑菜单"/>
    </LinearLayout>
  </LinearLayout>

如果将背景设置到第一层的LinearLayout上,那么自定义侧滑菜单那里设置背景就显示不出来,设置到第二层就可以,不知道是肿么回事,如果各位大师找到了,麻烦告诉小弟。

0 0