Android之仿美拍主要菜单滑动反弹效果

来源:互联网 发布:java web角色权限设计 编辑:程序博客网 时间:2024/05/02 01:52

本文主要记录一些零碎的东西

先说说要实现的效果:

菜单滑动最左边,还可以拖动一定距离,松开手后,view自动反弹会原位置

主要的坑:控制滑动的view响应touch事件,里面的子view无法响应click事件

左右滑动很多可以实现,最简单是 HorizontalScrollView,下面这个布局就可以实现上下左右滑动

<!-- 上下左右滑动 -->        <ScrollView            android:layout_width="match_parent"            android:layout_height="match_parent">                        <HorizontalScrollView                android:layout_width="match_parent"                android:layout_height="match_parent">                                <View                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"/>                            </HorizontalScrollView>                    </ScrollView>

我一开始是直接在activity里监听view的touch事件,然后,遇到了上面的坑


自定义一个view,处理touch事件,View的移动使用

import android.content.Context;import android.os.Build;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.widget.HorizontalScrollView;import android.widget.Scroller;/** * <p>Description: 滑动到边界,反弹效果的 ScrollView </p> * Created by slack on 2016/10/18 18:37 . */public class BoundScrollView extends HorizontalScrollView {    private static final String TAG = "BoundScrollView";    private static final int DEFAULT_MOVE = 30; // 每次移动距离    private static final long DELAY_DURATION = 10L; // 默认延时时间    private View innerView;// 子View    private int downX, tempX, moveX;    private boolean isFirstTouch;    public BoundScrollView(Context context) {        this(context, null);    }    public BoundScrollView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public BoundScrollView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        isFirstTouch = true;        // 取消滑动到顶部或底部时边缘的黄色或蓝色底纹        if (Build.VERSION.SDK_INT >= 9) {            this.setOverScrollMode(View.OVER_SCROLL_NEVER);        }    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() > 0) {            innerView = getChildAt(0);        }    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (innerView != null) {            handleTouchEvent(ev);        }        return super.onTouchEvent(ev);    }    /*    *  这是因为ACTION_DOWN和子View的OnClick有冲突,如果touch点在有click事件的view上ACTION_DOWN进入不了    * */    private void handleTouchEvent(MotionEvent event) {        int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN://                Log.i(TAG, "ACTION_DOWN...");                break;            case MotionEvent.ACTION_MOVE:                if (isFirstTouch) {                    downX = (int) event.getX();                    moveX = downX;                    isFirstTouch = false;                }                if ((tempX = (int) event.getX() - moveX) != 0) {                    innerView.scrollBy(-tempX / 2, 0);//                  innerView.offsetLeftAndRight(tempX); // 都可以实现随手指滑动效果                }                moveX = (int) event.getX();//                Log.i(TAG, "ACTION_MOVE..." + tempX+ " , " + innerView.getScrollX());                break;            case MotionEvent.ACTION_UP://                Log.i(TAG, "ACTION_UP..." + innerView.getScrollX());                isFirstTouch = true;                // 这里需要处理反弹回去                if(innerView.getScrollX() != 0) {                    innerView.postDelayed(new BoundTask(-innerView.getScrollX()), DELAY_DURATION);                }//                innerView.scrollBy( -innerView.getScrollX() , 0);//                innerView.offsetLeftAndRight(downX - (int)event.getX() );                break;            default:                break;        }    }    // 按滑动长度 反弹    private class BoundTask implements Runnable{        private int distanceX;        public BoundTask(int distance) {            this.distanceX = distance;        }        @Override        public void run() {//            Log.i(TAG, "BoundTask..." + innerView.getScrollX() + " " + distanceX);            if(distanceX > 0){                if(distanceX > DEFAULT_MOVE) {                    innerView.scrollBy(DEFAULT_MOVE, 0);                    distanceX -= DEFAULT_MOVE;                }else {                    innerView.scrollBy(distanceX, 0);                    distanceX = 0;                }                innerView.postDelayed(this,DELAY_DURATION);            }else if(distanceX < 0){                if(distanceX < -DEFAULT_MOVE) {                    innerView.scrollBy(-DEFAULT_MOVE, 0);                    distanceX += DEFAULT_MOVE;                }else {                    innerView.scrollBy(distanceX, 0);                    distanceX = 0;                }                innerView.postDelayed(this,DELAY_DURATION);            }        }    }}

上面的代码有些问题,反弹动画是自己写的,感觉有些死板,调整移动距离和时间也感觉怪怪的

,觉得还是使用Google提供好的吧 Scroller,再自定义一个布局

FrameLayoutView

import android.content.Context;import android.util.AttributeSet;import android.widget.FrameLayout;import android.widget.Scroller;/** * <p>Description: innerView 纯粹为实现反弹效果 </p> * Created by slack on 2016/10/19 14:45 . * * Scroller的基本用法其实还是比较简单的,主要可以分为以下几个步骤: * 1. 创建Scroller的实例 * 2. 调用startScroll()方法来初始化滚动数据并刷新界面 * 3. 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 */public class FrameLayoutView extends FrameLayout {    private Scroller mScroller;    public FrameLayoutView(Context context) {        this(context,null);    }    public FrameLayoutView(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    public FrameLayoutView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mScroller = new Scroller(context);    }    @Override    public void computeScroll() {        // 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑        if ( mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            invalidate();        }    }    public void startScroll(int startX, int startY, int dx, int dy, int duration){        mScroller.startScroll(startX,  startY,  dx,  dy,  duration);        invalidate();    }}

BoundScrollView
handleTouchEvent 里 case MotionEvent.ACTION_UP:修改
case MotionEvent.ACTION_UP://                Log.i(TAG, "ACTION_UP..." + innerView.getScrollX());                isFirstTouch = true;                // 这里需要处理反弹回去                if(innerView.getScrollX() != 0) {                    //这里使用了 viewGroup.getScrollX() 和 viewGroup.getScrollY() 作为起始坐标,                    // ScrollY 和 ScrollX 记录了使用 scrollBy 进行偏移的量                    //所以使用他们就等于是使用了现在的坐标作为起始坐标,                    // 目的坐标为他们的负数,就是偏移量为0的位置,也是view在没有移动之前的位置                    innerView.startScroll(innerView.getScrollX(),                            innerView.getScrollY(),                            -innerView.getScrollX(),                            -innerView.getScrollY(),                            800);//                    innerView.postDelayed(new BoundTask(-innerView.getScrollX()), DELAY_DURATION);                }//                innerView.scrollBy( -innerView.getScrollX() , 0);//                innerView.offsetLeftAndRight(downX - (int)event.getX() );                break;
完整的
import android.content.Context;import android.os.Build;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.widget.HorizontalScrollView;/** * <p>Description: 滑动到边界,反弹效果的 ScrollView </p> * Created by slack on 2016/10/18 18:37 . */public class BoundScrollView extends HorizontalScrollView {    private static final String TAG = "BoundScrollView";    private static final int DEFAULT_MOVE = 30; // 每次移动距离    private static final long DELAY_DURATION = 15L; // 默认延时时间    private FrameLayoutView innerView;// 子View    private int downX, tempX, moveX;    private boolean isFirstTouch;    public BoundScrollView(Context context) {        this(context, null);    }    public BoundScrollView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public BoundScrollView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        isFirstTouch = true;        // 取消滑动到顶部或底部时边缘的黄色或蓝色底纹        if (Build.VERSION.SDK_INT >= 9) {            this.setOverScrollMode(View.OVER_SCROLL_NEVER);        }    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() > 0) {            innerView = (FrameLayoutView)getChildAt(0);        }    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (innerView != null) {            handleTouchEvent(ev);        }        return super.onTouchEvent(ev);    }    /*    *  这是因为ACTION_DOWN和子View的OnClick有冲突,如果touch点在有click事件的view上ACTION_DOWN进入不了    * */    private void handleTouchEvent(MotionEvent event) {        int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN://                Log.i(TAG, "ACTION_DOWN...");                break;            case MotionEvent.ACTION_MOVE:                if (isFirstTouch) {                    downX = (int) event.getRawX();                    moveX = downX;                    isFirstTouch = false;                }                if ((tempX = (int) event.getRawX() - moveX) != 0) {                    innerView.scrollBy(-tempX / 2, 0);//                  innerView.offsetLeftAndRight(tempX); // 都可以实现随手指滑动效果                }                moveX = (int) event.getX();//                Log.i(TAG, "ACTION_MOVE..." + tempX+ " , " + innerView.getScrollX());                break;            case MotionEvent.ACTION_UP://                Log.i(TAG, "ACTION_UP..." + innerView.getScrollX());                isFirstTouch = true;                // 这里需要处理反弹回去                if(innerView.getScrollX() != 0) {                    //这里使用了 viewGroup.getScrollX() 和 viewGroup.getScrollY() 作为起始坐标,                    // ScrollY 和 ScrollX 记录了使用 scrollBy 进行偏移的量                    //所以使用他们就等于是使用了现在的坐标作为起始坐标,                    // 目的坐标为他们的负数,就是偏移量为0的位置,也是view在没有移动之前的位置                    innerView.startScroll(innerView.getScrollX(),                            innerView.getScrollY(),                            -innerView.getScrollX(),                            -innerView.getScrollY(),                            800);//                    innerView.postDelayed(new BoundTask(-innerView.getScrollX()), DELAY_DURATION);                }//                innerView.scrollBy( -innerView.getScrollX() , 0);//                innerView.offsetLeftAndRight(downX - (int)event.getX() );                break;            default:                break;        }    }    // 按滑动长度 反弹  , 自己处理的感觉有些死板    private class BoundTask implements Runnable{        private int distanceX;        public BoundTask(int distance) {            this.distanceX = distance;        }        @Override        public void run() {//            Log.i(TAG, "BoundTask..." + innerView.getScrollX() + " " + distanceX);            if(distanceX > 0){                if(distanceX > DEFAULT_MOVE) {                    innerView.scrollBy(DEFAULT_MOVE, 0);                    distanceX -= DEFAULT_MOVE;                }else {                    innerView.scrollBy(distanceX, 0);                    distanceX = 0;                }                innerView.postDelayed(this,DELAY_DURATION);            }else if(distanceX < 0){                if(distanceX < -DEFAULT_MOVE) {                    innerView.scrollBy(-DEFAULT_MOVE, 0);                    distanceX += DEFAULT_MOVE;                }else {                    innerView.scrollBy(distanceX, 0);                    distanceX = 0;                }                innerView.postDelayed(this,DELAY_DURATION);            }        }    }}


然后界面的布局就变成
<com.benqu.wuta.views.BoundScrollView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_gravity="center"            >            <com.benqu.wuta.views.FrameLayoutView                android:layout_width="match_parent"                android:layout_height="match_parent">                // your view....            </com.benqu.wuta.views.FrameLayoutView>                   </com.benqu.wuta.views.BoundScrollView>
这样修改之后,反弹动画果然舒服多了。







0 0
原创粉丝点击