Android 侧滑菜单

效果图

Android 侧滑菜单_第1张图片

思考

  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. 主页面的布局

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

        "@layout/layout_menu" />

        "@layout/layout_main" />


    com.example.slidingmenu.view.SlidingMenu>



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2. 主内容的布局


<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

3. 侧边菜单栏的布局


<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

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();
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162

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();
            }
        });
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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);
    }
}

你可能感兴趣的:(android,android应用,java)