Android中View的滑動衝突——Android開發藝術探索筆記

來源:互聯網
上載者:User

Android中View的滑動衝突——Android開發藝術探索筆記
介紹

相信開發Android的人都會有這種體會:從網上下載的demo啟動並執行好好的,但是只要出現了滑動衝突,Demo就無法正常工作了。但是不用擔心,解決滑動衝突有固定的模式,常見的有內部攔截和外部攔截兩種,只要按照這個模式來就可以順利解決。

常見的滑動衝突情境

樣本

處理規則

對於情境1,處理規則為:當使用者左右滑動時,讓外部的View攔截點擊事件,當使用者上下滑動時,讓內部的View攔截點擊事件。當產生滑動時,根據滑動的起始點與終點座標位置,如果垂直方向滑動距離大,就判斷為垂直滑動,否則判斷為水平滑動。其他兩種情況處理方法相似,都是從業務需求上得出相應的規則。

解決方案外部攔截髮

所有的點擊事件都先經過父容器攔截處理,如果父容器需要攔截就攔截,不需要就傳給內部的View。虛擬碼如下

  public boolean onInterceptTouchEvent(MotionEvent event) {        boolean intercepted = false;        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN: {                intercepted = false;                break;            }            case MotionEvent.ACTION_MOVE: {                if (滿足父容器的攔截要求) {                    intercepted = true;                } else {                    intercepted = false;                }                break;            }            case MotionEvent.ACTION_UP: {                intercepted = false;                break;            }            default:                break;        }        mLastXIntercept = x;        mLastYIntercept = y;        return intercepted;    }

註:
ACTION_DOWN這個事件是不能攔截的,因為一旦攔截後續的事件都會由父容器處理了。

內部攔截法

父容器不攔截任何事件,所有事件都傳給子項目。如果子項目需要此事件就直接消耗,否則就交給父容器進行處理。完成這個功能需要配合requestDisallowInterceptTouchEvent()方法才可。這個方法表示是否讓父容器攔截事件。虛擬碼如下:

 public boolean dispatchTouchEvent(MotionEvent event) {        int x = (int) event.getX();        int y = (int) event.getY();        switch (event.getAction()) {        case MotionEvent.ACTION_DOWN: {            parent.requestDisallowInterceptTouchEvent(true);            break;        }        case MotionEvent.ACTION_MOVE: {            if (滿足父容器的攔截要求) {                parent.requestDisallowInterceptTouchEvent(false);            }            break;        }        case MotionEvent.ACTION_UP: {            break;        }        default:            break;        }        mLastX = x;        mLastY = y;        return super.dispatchTouchEvent(event);    }


父容器預設攔截除了ACTION_DOWN以外的其他事件,這樣子當元素調用parent.requestDisallowInterceptTouchEvent(false)時,父元素才能攔截所需的事件。

總結解決滑動衝突有兩種方法,推薦外部攔截法,實現起來簡單。 本文以情境1為例做了講解,情境2,3的做法與1類似,都是根據業務需要制定處理規則。樣本效果的源碼Activity
public class DemoActivity_1 extends Activity {    private static final String TAG = "DemoActivity_1";    private HorizontalScrollViewEx mListContainer;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.demo_1);        Log.d(TAG, "onCreate");        initView();    }    private void initView() {        LayoutInflater inflater = getLayoutInflater();        mListContainer = (HorizontalScrollViewEx) findViewById(R.id.container);        final int screenWidth = MyUtils.getScreenMetrics(this).widthPixels;        final int screenHeight = MyUtils.getScreenMetrics(this).heightPixels;        for (int i = 0; i < 3; i++) {            ViewGroup layout = (ViewGroup) inflater.inflate(                    R.layout.content_layout, mListContainer, false);            layout.getLayoutParams().width = screenWidth;            TextView textView = (TextView) layout.findViewById(R.id.title);            textView.setText("page " + (i + 1));            layout.setBackgroundColor(Color.rgb(255 / (i + 1), 255 / (i + 1), 0));            createList(layout);            mListContainer.addView(layout);        }    }    private void createList(ViewGroup layout) {        ListView listView = (ListView) layout.findViewById(R.id.list);        ArrayList datas = new ArrayList();        for (int i = 0; i < 50; i++) {            datas.add("name " + i);        }        ArrayAdapter adapter = new ArrayAdapter(this,                R.layout.content_list_item, R.id.name, datas);        listView.setAdapter(adapter);        listView.setOnItemClickListener(new OnItemClickListener() {            @Override            public void onItemClick(AdapterView parent, View view,                    int position, long id) {                Toast.makeText(DemoActivity_1.this, "click item",                        Toast.LENGTH_SHORT).show();            }        });    }}
水平滑動的View
public class HorizontalScrollViewEx extends ViewGroup {    private static final String TAG = "HorizontalScrollViewEx";    private int mChildrenSize;    private int mChildWidth;    private int mChildIndex;    // 分別記錄上次滑動的座標    private int mLastX = 0;    private int mLastY = 0;    // 分別記錄上次滑動的座標(onInterceptTouchEvent)    private int mLastXIntercept = 0;    private int mLastYIntercept = 0;    private Scroller mScroller;    private VelocityTracker mVelocityTracker;    public HorizontalScrollViewEx(Context context) {        super(context);        init();    }    public HorizontalScrollViewEx(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public HorizontalScrollViewEx(Context context, AttributeSet attrs,                                  int defStyle) {        super(context, attrs, defStyle);        init();    }    private void init() {        mScroller = new Scroller(getContext());        mVelocityTracker = VelocityTracker.obtain();    }    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        boolean intercepted = false;        int x = (int) event.getX();        int y = (int) event.getY();        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN: {                Log.d(TAG, "onInterceptTouchEvent: ACTION_DOWN");                intercepted = false;                if (!mScroller.isFinished()) {                    mScroller.abortAnimation();                    intercepted = true;                }                break;            }            case MotionEvent.ACTION_MOVE: {                Log.d(TAG, "onInterceptTouchEvent: ACTION_MOVE");                int deltaX = x - mLastXIntercept;                int deltaY = y - mLastYIntercept;                if (Math.abs(deltaX) > Math.abs(deltaY)) {                    intercepted = true;                } else {                    intercepted = false;                }                break;            }            case MotionEvent.ACTION_UP: {                intercepted = false;                break;            }            default:                break;        }        Log.d(TAG, "intercepted=" + intercepted);        mLastX = x;        mLastY = y;        mLastXIntercept = x;        mLastYIntercept = y;        return intercepted;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        mVelocityTracker.addMovement(event);        int x = (int) event.getX();        int y = (int) event.getY();        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN: {                Log.d(TAG, "onTouchEvent: ACTION_DOWN");                if (!mScroller.isFinished()) {                    mScroller.abortAnimation();                }                break;            }            case MotionEvent.ACTION_MOVE: {                Log.d(TAG, "onTouchEvent: ACTION_MOVE");                int deltaX = x - mLastX;                int deltaY = y - mLastY;                Log.d(TAG, "onTouchEvent: deltaX" + deltaX);                scrollBy(-deltaX, 0);                break;            }            case MotionEvent.ACTION_UP: {                int scrollX = getScrollX();                int scrollToChildIndex = scrollX / mChildWidth;                mVelocityTracker.computeCurrentVelocity(1000);                float xVelocity = mVelocityTracker.getXVelocity();                //滑的速度到達閾值就認為需要進入下一頁                if (Math.abs(xVelocity) >= 100) {                    mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;                } else {                    //滑動的距離超過一半,就進入下一頁                    mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;                }                //保證在0頁和最後一頁滑動時不會越界                mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));                //沒有達到進入下一頁的要求,恢複原樣                int dx = mChildIndex * mChildWidth - scrollX;                smoothScrollBy(dx, 0);                Log.d(TAG, "onTouchEvent: dx = " + dx);                mVelocityTracker.clear();                break;            }            default:                break;        }        mLastX = x;        mLastY = y;        return true;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int measuredWidth = 0;        int measuredHeight = 0;        final int childCount = getChildCount();        measureChildren(widthMeasureSpec, heightMeasureSpec);        int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        if (childCount == 0) {            setMeasuredDimension(0, 0);        } else if (heightSpecMode == MeasureSpec.AT_MOST) {            final View childView = getChildAt(0);            measuredHeight = childView.getMeasuredHeight();            setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());        } else if (widthSpecMode == MeasureSpec.AT_MOST) {            final View childView = getChildAt(0);            measuredWidth = childView.getMeasuredWidth() * childCount;            setMeasuredDimension(measuredWidth, heightSpaceSize);        } else {            final View childView = getChildAt(0);            measuredWidth = childView.getMeasuredWidth() * childCount;            measuredHeight = childView.getMeasuredHeight();            setMeasuredDimension(measuredWidth, measuredHeight);        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childLeft = 0;        final int childCount = getChildCount();        mChildrenSize = childCount;        for (int i = 0; i < childCount; i++) {            final View childView = getChildAt(i);            if (childView.getVisibility() != View.GONE) {                final int childWidth = childView.getMeasuredWidth();                mChildWidth = childWidth;                childView.layout(childLeft, 0, childLeft + childWidth,                        childView.getMeasuredHeight());                childLeft += childWidth;            }        }    }    private void smoothScrollBy(int dx, int dy) {        mScroller.startScroll(getScrollX(), 0, dx, 0, 500);        invalidate();    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());            postInvalidate();        }    }    @Override    protected void onDetachedFromWindow() {        mVelocityTracker.recycle();        super.onDetachedFromWindow();    }}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.