Android 自定义侧滑菜单

效果图

这里写图片描述

思考

  1. 可以看出滑动的是两个layout,所以自定义的侧滑控件应该继承ViewGroup,实现onMessure()和onLayout()方法,为了简化操作,可以继承android系统已经实现好的ViewGroup的子类 —— FrameLayout,这样就不用自己去测量了。
  2. onLayout()方法中,初始化的时候,要将侧边菜单的布局放到屏幕左边看不到的地方。menuView.layout(-menuWidth,0,0,menuView.getMeasuredHeight()); 将主页面的布局完全显示。mainView.layout(0, 0, r, b);
  3. 为了实现偏移控制,重写一下computeScroll()方法,在初始化自定义的侧滑菜单时创建一个Scroller()对象。关于Scroller类和computeScroll()方法可参考这个链接:http://www.cnblogs.com/wanqieddy/archive/2012/05/05/2484534.html
  4. 接下来就是在onTouchEvent()方法中进行判断了。
    • 手指按下时,记录按下的x坐标。
    • 手指移动时,计算出在x方向滑动的距离deltaX,获取当前view的左边在屏幕上的x的距离getScrollX(),减去deltaX,就得到应该移动的距离了。
    • 手指抬起时,判断一下,如果偏移的距离大于菜单布局的宽的一半,就关闭菜单,否则,打开菜单。

步骤

1. 主页面的布局

<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">

    <com.example.slidingmenu.view.SlidingMenu
        android:id="@+id/slidingMenu"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

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

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


    </com.example.slidingmenu.view.SlidingMenu>


</RelativeLayout>

2. 主内容的布局

<?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="#55666666"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@mipmap/top_bar_bg"
        android:gravity="center_vertical">

        <ImageView
            android:id="@+id/btn_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@mipmap/main_back" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"
            android:background="@mipmap/top_bar_divider" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:text="网易新闻"
            android:textColor="#ffffff"
            android:textSize="22sp" />

    </LinearLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="头疼的番茄..."
        android:textColor="#000000"
        android:textSize="30sp" />

</LinearLayout>

3. 侧边菜单栏的布局

<?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"
    android:background="@mipmap/menu_bg"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="300dp">

        <TextView
            style="@style/MenuTabText"
            android:background="#33aa9900"
            android:drawableLeft="@mipmap/tab_news"
            android:text="新闻" />

        <TextView
            style="@style/MenuTabText"
            android:drawableLeft="@mipmap/tab_read"
            android:text="订阅" />

        <TextView
            style="@style/MenuTabText"
            android:drawableLeft="@mipmap/tab_ties"
            android:text="跟帖" />

        <TextView
            style="@style/MenuTabText"
            android:drawableLeft="@mipmap/tab_pics"
            android:text="图片" />

        <TextView
            style="@style/MenuTabText"
            android:drawableLeft="@mipmap/tab_ugc"
            android:text="话题" />

        <TextView
            style="@style/MenuTabText"
            android:drawableLeft="@mipmap/tab_vote"
            android:text="投票" />

        <TextView
            style="@style/MenuTabText"
            android:drawableLeft="@mipmap/tab_focus"
            android:text="聚合阅读" />

    </LinearLayout>


</ScrollView>

4. 自定义菜单的代码

public class SlidingMenu extends FrameLayout{
    private View menuView,mainView;
    private int menuWidth = 0;
    private Scroller scroller;
    public SlidingMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SlidingMenu(Context context) {
        super(context);
        init();
    }

    private void init(){
        scroller = new Scroller(getContext());
    }

    /**
     * 当1级的子view全部加载完调用,可以用初始化子view的引用
     * 注意,这里无法获取子view的宽高
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        menuView = getChildAt(0);
        mainView = getChildAt(1);
        menuWidth = menuView.getLayoutParams().width;
    }

    /**
     * widthMeasureSpec和heightMeasureSpec是系统测量SlideMenu时传入的参数,
     * 这2个参数测量出的宽高能让SlideMenu充满窗体,其实是正好等于屏幕宽高
     */
//  @Override
//  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//
//      int measureSpec = MeasureSpec.makeMeasureSpec(menuWidth, MeasureSpec.EXACTLY);
//
//      //测量所有子view的宽高
//      //通过getLayoutParams方法可以获取到布局文件中指定宽高
//      menuView.measure(measureSpec, heightMeasureSpec);
//      //直接使用SlideMenu的测量参数,因为它的宽高都是充满父窗体
//      mainView.measure(widthMeasureSpec, heightMeasureSpec);
//
//  }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) ev.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = (int) ( ev.getX()- downX);

                if(Math.abs(deltaX)>8){
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
//      return super.onInterceptTouchEvent(ev);
    }

    /**
     * l: 当前子view的左边在父view的坐标系中的x坐标
     * t: 当前子view的顶边在父view的坐标系中的y坐标
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
//      Log.e("MAIN", "L: "+l+"   t: "+t  +"  r: "+r  + "   b: "+b);
        menuView.layout(-menuWidth, 0, 0, menuView.getMeasuredHeight());
        mainView.layout(0, 0, r, b);
    }

    private int downX;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getX();
                int deltaX = (int) ( moveX- downX);

                int newScrollX = getScrollX() - deltaX;

                if(newScrollX<-menuWidth)newScrollX = -menuWidth;
                if(newScrollX>0)newScrollX = 0;

                Log.e("Main", "scrollX: " + getScrollX());
                scrollTo(newScrollX, 0);
                downX = moveX;
                break;
            case MotionEvent.ACTION_UP:
                //1.使用自定义动画
//          ScrollAnimation scrollAnimation;
//          if(getScrollX()>-menuWidth/2){
//              //关闭菜单
                scrollTo(0, 0);
//              scrollAnimation = new ScrollAnimation(this, 0);
//          }else {
//              //打开菜单
                scrollTo(-menuWidth, 0);
//              scrollAnimation = new ScrollAnimation(this, -menuWidth);
//          }
//          startAnimation(scrollAnimation);
                //2.使用Scroller
                if(getScrollX()>-menuWidth/2){
//              //关闭菜单
                    closeMenu();
                }else {
                    //打开菜单
                    openMenu();
                }
                break;
        }
        return true;
    }

    private void closeMenu(){
        scroller.startScroll(getScrollX(), 0, 0-getScrollX(), 0, 400);
        invalidate();
    }

    private void openMenu(){
        scroller.startScroll(getScrollX(), 0, -menuWidth-getScrollX(), 0, 400);
        invalidate();
    }

    /**
     * Scroller不主动去调用这个方法
     * 而invalidate()可以掉这个方法
     * invalidate->draw->computeScroll
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if(scroller.computeScrollOffset()){//返回true,表示动画没结束
            scrollTo(scroller.getCurrX(), 0);
            invalidate();
        }
    }

    /**
     * 切换菜单的开和关
     */
    public void switchMenu() {
        if(getScrollX()==0){
            //需要打开
            openMenu();
        }else {
            //需要关闭
            closeMenu();
        }
    }

}

5. 主函数的代码

public class MainActivity extends Activity {

    private ImageView btn_back;
    private SlidingMenu slideMenu;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        btn_back = (ImageView) findViewById(R.id.btn_back);
        slideMenu = (SlidingMenu) findViewById(R.id.slidingMenu);

        btn_back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                slideMenu.switchMenu();
            }
        });
    }


}

6. 补充 :第一种滑动方式 自定义动画的代码

public class ScrollAnimation extends Animation{

    private View view;
    private int targetScrollX;
    private int startScrollX;
    private int totalValue;

    public ScrollAnimation(View view, int targetScrollX) {
        super();
        this.view = view;
        this.targetScrollX = targetScrollX;

        startScrollX = view.getScrollX();
        totalValue = this.targetScrollX - startScrollX;

        int time = Math.abs(totalValue);
        setDuration(time);
    }



    /**
     * 在指定的时间内一直执行该方法,直到动画结束
     * interpolatedTime:0-1  标识动画执行的进度或者百分比
     * time :  0   - 0.5  - 0.7  -   1
     * value:  10  -  60  -  80  -  110
     * 当前的值 = 起始值 + 总的差值*interpolatedTime
     */
    @Override
    protected void applyTransformation(float interpolatedTime,
                                       Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        int currentScrollX = (int) (startScrollX + totalValue*interpolatedTime);
        view.scrollTo(currentScrollX, 0);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值