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

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

你可能感兴趣的:(android,自定义,侧滑菜单)