android 自定义布局之侧拉布局

来源:互联网 发布:建筑软件有哪些 编辑:程序博客网 时间:2024/05/17 22:09

                                     android 自定义布局之侧拉布局


侧拉布局是一个自定义的布局,类似QQ的侧拉


代码如下,

package com.example.android_project_two;/** * Created by Administrator on 2017/6/21 0021. */import android.content.Context;import android.os.AsyncTask;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.WindowManager;import android.widget.LinearLayout;/** * @ 对外仅需设置的接口 * * -判断左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效 * boolean SlidingLayout.isLeftLayoutVisible() * * -将屏幕滚动到右侧布局界面 * void SlidingLayout.scrollToRightLayout() * * -将屏幕滚动到左侧布局界面 * void SlidingLayout.scrollToLeftLayout() * */public class SlidingLayout extends LinearLayout {    private static final int SNAP_VELOCITY = 200;  //滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。    private VelocityTracker mVelocityTracker;      //用于计算手指滑动的速度。    private int touchSlop;                         //在被判定为滚动之前用户手指可以移动的最大值。    private int screenWidth;                       //屏幕宽度值    private int leftEdge ;                         //左边最多可以滑动到的左边缘,值由左边布局的宽度来定,marginLeft到达此值之后,不能再减少。    private int rightEdge = 0;                     //左边最多可以滑动到的右边缘,值恒为0,即marginLeft到达0之后,不能增加。    private float xDown;                           //记录手指按下时的横坐标。    private float yDown;                           //记录手指按下时的纵坐标。    private float xMove;                           //记录手指移动时的横坐标。    private float yMove;                           //记录手指移动时的纵坐标。    private float xUp;                             //记录手机抬起时显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。    private boolean isSliding;                     //是否正在滑动。的横坐标。    private boolean isLeftLayoutVisible;           //左侧布局当前是    private MarginLayoutParams leftLayoutParams;   //左侧布局的参数,通过此参数来重新确定左侧布局的宽度,以及更改leftMargin的值。    private MarginLayoutParams rightLayoutParams;  //右侧布局的参数,通过此参数来重新确定右侧布局的宽度。    private View leftLayout;                       //左侧布局对象。    private View rightLayout;                      //右侧布局对象。    //构造函数    public SlidingLayout(Context context, AttributeSet attrs)    {        super(context, attrs);        //获取屏幕宽度        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);        screenWidth = wm.getDefaultDisplay().getWidth();        //获取滑动的最短距离        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();    }    //创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。    private void createVelocityTracker(MotionEvent event)    {        if (mVelocityTracker == null)        {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(event);    }    //拦截触摸事件    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        switch(event.getAction())        {            case MotionEvent.ACTION_DOWN:                xDown = event.getRawX();                yDown = event.getRawY();                //当左边菜单完全显示时,点击右边的View,我们希望是拦截,而左边不拦截                if(xDown > leftLayout.getLeft()+leftLayout.getWidth() && isLeftLayoutVisible)                    return true;                break;            case MotionEvent.ACTION_MOVE:                int distanceX=(int) (event.getRawX()-xDown);                //水平滑动距离超过一定距离,拦截所有子view的touch事件,来处理自身onTouch事件来达到滑动的目的                if(Math.abs(distanceX)>=touchSlop)                {                    return true;                }                break;        }        return false;    }    //触摸事件    @Override    public boolean onTouchEvent(MotionEvent event)    {        // TODO 自动生成的方法存根        createVelocityTracker(event);        switch (event.getAction())        {            case MotionEvent.ACTION_DOWN:                // 手指按下时,记录按下时的横坐标                xDown = event.getRawX();                yDown = event.getRawY();                break;            case MotionEvent.ACTION_MOVE:                // 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整右侧布局的leftMargin值,从而显示和隐藏左侧布局                xMove = event.getRawX();                yMove = event.getRawY();                int distanceX = (int) (xMove - xDown);                int distanceY = (int) (yMove - yDown);                //只有触摸右边的View,才发生滑动                if(xMove > leftLayout.getLeft()+leftLayout.getWidth())                {                    //向右滑                    if (!isLeftLayoutVisible && distanceX >= touchSlop && (isSliding || Math.abs(distanceY) <= touchSlop))                    {                        isSliding = true;                        leftLayoutParams.leftMargin = leftEdge + distanceX;                        if (leftLayoutParams.leftMargin > rightEdge)                        {                            leftLayoutParams.leftMargin = rightEdge;                        }                    }                    //向左滑                    else if (isLeftLayoutVisible && -distanceX >= touchSlop)                    {                        isSliding = true;                        leftLayoutParams.leftMargin = distanceX;                        if (leftLayoutParams.leftMargin < leftEdge)                        {                            leftLayoutParams.leftMargin = leftEdge;                        }                    }                    leftLayout.setLayoutParams(leftLayoutParams);                }                break;            case MotionEvent.ACTION_UP:                xUp = event.getRawX();                int upDistanceX = (int) (xUp - xDown);                if (isSliding)                {                    // 手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局                    if (wantToShowLeftLayout())                    {                        if (shouldScrollToLeftLayout())                        {                            scrollToLeftLayout();                        }                        else                        {                            scrollToRightLayout();                        }                    }                    else if (wantToShowRightLayout())                    {                        if (shouldScrollToRightLayout())                        {                            scrollToRightLayout();                        }                        else                        {                            scrollToLeftLayout();                        }                    }                }                //在左侧菜单完全显示时,我们希望的是点击右边的View可以发生恢复                else if (upDistanceX < touchSlop && isLeftLayoutVisible && xUp > leftLayout.getLeft()+leftLayout.getWidth())                {                    scrollToRightLayout();                }                recycleVelocityTracker();                break;        }        if (this.isEnabled())        {            if (isSliding)            {                unFocusBindView();                return true;            }            if (isLeftLayoutVisible)            {                return true;            }            return false;        }        return true;    }    //将屏幕滚动到左侧布局界面,滚动速度设定为50.    public void scrollToLeftLayout()    {        //传入第一个参数        new ScrollTask().execute(50);    }    //将屏幕滚动到右侧布局界面,滚动速度设定为-50.    public void scrollToRightLayout()    {        //传入第一个参数        new ScrollTask().execute(-50);    }    //左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效。    public boolean isLeftLayoutVisible()    {        return isLeftLayoutVisible;    }    //在onLayout中重新设定左侧布局和右侧布局的参数。    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b)    {        super.onLayout(changed, l, t, r, b);        if (changed)        {            // 获取左侧布局对象            leftLayout = getChildAt(0);            leftLayoutParams = (MarginLayoutParams) leftLayout.getLayoutParams();            // 设置最左边距为负的左侧布局的宽度            leftEdge = -leftLayoutParams.width;            leftLayoutParams.leftMargin = leftEdge;            leftLayout.setLayoutParams(leftLayoutParams);            // 获取右侧布局对象            rightLayout = getChildAt(1);            rightLayoutParams = (MarginLayoutParams) rightLayout.getLayoutParams();            rightLayoutParams.width = screenWidth;            rightLayout.setLayoutParams(rightLayoutParams);            rightLayout.setClickable(true);        }    }    //判断当前手势的意图是不是想显示右侧布局。如果手指移动的距离是负数,且当前左侧布局是可见的,则认为当前手势是想要显示右侧布局。    private boolean wantToShowRightLayout()    {        return xUp - xDown < 0 && isLeftLayoutVisible;    }    //判断当前手势的意图是不是想显示左侧布局。如果手指移动的距离是正数,且当前左侧布局是不可见的,则认为当前手势是想要显示左侧布局。    private boolean wantToShowLeftLayout()    {        return xUp - xDown > 0 && !isLeftLayoutVisible;    }    //判断是否应该滚动将左侧布局展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,就认为应该滚动将左侧布局展示出来。    private boolean shouldScrollToLeftLayout()    {        return xUp - xDown > leftLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY;    }    //判断是否应该滚动将右侧布局展示出来。如果手指移动距离加上leftLayoutPadding大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将右侧布局展示出来。    private boolean shouldScrollToRightLayout()    {        return xDown - xUp > leftLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY;    }    //获取手指在右侧布局的监听View上的滑动速度。    private int getScrollVelocity()    {        mVelocityTracker.computeCurrentVelocity(1000);        int velocity = (int) mVelocityTracker.getXVelocity();        return Math.abs(velocity);    }    //回收VelocityTracker对象。    private void recycleVelocityTracker()    {        mVelocityTracker.recycle();        mVelocityTracker = null;    }    //使用可以获得焦点的控件在滑动的时候失去焦点。    private void unFocusBindView()    {        if (rightLayout != null)        {            rightLayout.setPressed(false);            rightLayout.setFocusable(false);            rightLayout.setFocusableInTouchMode(false);        }    }//*********************************************************************************************************************    /**     * @ 利用轻量级线程实现滑动 ,应用在手指松开的滑动动画     */    class ScrollTask extends AsyncTask<Integer, Integer, Integer>    {        //后台处理,入口参数对应第一个参数类型        @Override        protected Integer doInBackground(Integer... speed) {            int leftMargin = leftLayoutParams.leftMargin;            // 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。            while (true)            {                leftMargin = leftMargin + speed[0];                if (leftMargin > rightEdge)                {                    leftMargin = rightEdge;                    break;                }                if (leftMargin < leftEdge)                {                    leftMargin = leftEdge;                    break;                }                //主动回调onProgressUpdate来更新界面(滑动)                publishProgress(leftMargin);                //使当前线程睡眠指定的毫秒数,为了要有滚动效果产生,每次循环使线程睡眠10毫秒,这样肉眼才能够看到滚动动画。                try                {                    Thread.sleep(10);                }                catch (InterruptedException e)                {                    e.printStackTrace();                }            }            if (speed[0] > 0)            {                isLeftLayoutVisible = true;            }            else            {                isLeftLayoutVisible = false;            }            isSliding = false;            //返回对应第三个参数类型,并且把值传入onPostExecute            return leftMargin;        }        //调用publishProgress时,回调这个方法,用来更新界面(滑动),入口参数对应第二个参数类型        @Override        protected void onProgressUpdate(Integer... leftMargin)        {            leftLayoutParams.leftMargin = leftMargin[0];            leftLayout.setLayoutParams(leftLayoutParams);            //使用可以获得焦点的控件在滑动的时候失去焦点。            unFocusBindView();        }        //在doInBackground执行完成后执行界面更新,入口参数对应第三个参数类型        @Override        protected void onPostExecute(Integer leftMargin)        {            leftLayoutParams.leftMargin = leftMargin;            leftLayout.setLayoutParams(leftLayoutParams);        }    }}


在xml方面的布局就直接引用它就行,这个代码注释也是很多,很容易懂






原创粉丝点击