View的滑動與彈性滑動(ScrollBy+Scroller)

這次做一個SwitchView1,與上文的效果是一樣的,但是
1.跟隨手滑動使用scrollBy,
2. 鬆手後的彈性滑動使用scroller
需要使用下面的幾個知識點:

知識點

  1. 系統很多控件都是使用了scrollBy+scorller來實現滑動效果的,例如ViewPager就是如此

    如果你想控制ViewPager翻頁的速度,可以通過反射給ViewPager設置一個自定義的Scroller,在這個自定義的Scroller裏設置你想要的翻頁時長(做輪播圖你會用到的,Banner這是我做的一個輪播圖的demo,封裝的不太好)

  2. scrollBy(dx,dy)增量滑動,

  3. scrollTo(x,y)直接滑動到指定座標處

  4. 在本例中,一個LinearLayout裏有一個ImageView滑塊,當你想讓ImageView滑塊向左側移動時,必須想兩件事,

    1. 誰來調用scrollBy方法
    2. dx,dy的正負

    結論是:每次調用scrollBy,讓該子控件的父容器去調用scrollBy方法,然後方向取反即可

  5. scorller的使用也是固定的

第一步:初始化Scroller
mScroller = new Scroller(getContext());

第二步:調用scrollBy去讓一個控件滑動
((View) mImageView.getParent()).scrollBy(-deltaX, 0);// ★這是一個普適的公式,誰要滑動,就找誰的父親去調用scrollBy,然後方向取反

第三步:重寫computeScroll方法,下面的寫法也是固定的

   @Override
    public void computeScroll() {
        super.computeScroll();

        if (mScroller.computeScrollOffset()) {
            ((View) mImageView.getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }

第四步:調用startScroll(int startX, int startY, int dx, int dy, int duration) 實現彈性滑動

startX、startY是當前的scrollX,scrollY,dx、dy是你要基於當前位置要移動多少(增量)

所以(dx=你目的地的scrollX-當前的scrollX),下面的代碼裏,目的地的scrollX是0,所以dx= 0 - ((View) mImageView.getParent()).getScrollX(),dy=0,因爲這個例子不需要豎向滑動

 mScroller.startScroll(
      ((View) mImageView.getParent()).getScrollX(),
      ((View) mImageView.getParent()).getScrollY(),
      0 - ((View) mImageView.getParent()).getScrollX(),
      0,
      300);

應用:

直接上代碼:

package com.view.custom.dosometest.view;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Scroller;

/**
 * 描述當前版本功能
 * scroller實現
 *
 * @Project: DoSomeTest
 * @author: cjx
 * @date: 2019-12-01 10:06  星期日
 */
public class SwitchView1 extends LinearLayout {


    private ImageView mImageView;
    private LayoutParams mLayoutParams;
    private int mSliderHeight;
    private int mSliderWidth;
    private int mWidth;
    private int mHeight;
    private Scroller mScroller;

    public SwitchView1(Context context) {
        super(context);
        init();
    }

    public SwitchView1(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SwitchView1(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private void init() {
        setBackgroundColor(Color.GRAY);
        mScroller = new Scroller(getContext());

        post(new Runnable() {
            @Override
            public void run() {
                addSlider();
            }
        });

    }

    private void addSlider() {
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();

        mSliderWidth = mWidth / 2;
        mSliderHeight = mHeight;

        mLayoutParams = new LayoutParams(mSliderWidth, mSliderHeight);
        mImageView = new ImageView(getContext());
        mImageView.setBackgroundColor(Color.CYAN);
        mImageView.setLayoutParams(mLayoutParams);
        addView(mImageView);
    }


    float mLastX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                mLastX = x;
                break;

            case MotionEvent.ACTION_MOVE:
                int deltaX = (int) (x - mLastX);

                // 防止滑塊滑出邊界,矯正deltaX,這給地方打印出getScrollX()、deltaX的日誌仔細看,就寫對了,小邏輯有點繞
                if (getScrollX() - deltaX <= -mWidth / 2) {
                    deltaX = getScrollX() + mWidth / 2;
                } else if (getScrollX() - deltaX > 0) {
                    deltaX = getScrollX();
                }

                // SwitchView1是個LinearLayout,他的子控件是滑塊Slider,假設我們想讓滑塊往右側移動
                // 那麼我們應該先找到滑塊的父容器,讓他去調用scrollBy方法,正好父容器就是SwitchView1,所以this.scrollBy()即可,
                // ok,既然我們是讓父容器SwitchView1去scrollBy,那麼父容器往左側移動,滑塊才能看起來是往右側移動,所以要在deltaX前面加負號
                // 總結:每次調用scrollBy,讓該子控件的父容器去調用scrollBy方法,然後方向取反即可
                // (但是父容器scrollBy了,他的所有子View都會動,這是副作用,當你需要所有子View一起動,那就正好ok)

                ((View) mImageView.getParent()).scrollBy(-deltaX, 0);// ★這是一個普適的公式,誰要滑動,就找誰的父親去調用scrollBy,然後方向取反
                //scrollBy(-deltaX, 0);//這句話在本例子裏和上句話是等效的,因爲滑塊的父親就是this


                Log.e("qwe", getScrollX() + "scroll");
                mLastX = x;
                break;


            case MotionEvent.ACTION_UP:

                if (getScrollX() > -mSliderWidth / 2) {
                    mScroller.startScroll(
                            ((View) mImageView.getParent()).getScrollX(),
                            ((View) mImageView.getParent()).getScrollY(),
                            0 - ((View) mImageView.getParent()).getScrollX(),
                            0,
                            300);
                    // 本例子下面的語句與上面的等效,因爲((View) mImageView.getParent())就是this
//                    mScroller.startScroll(getScrollX(), getScrollY(), 0 - getScrollX(), 0, 300);

                } else {
                    mScroller.startScroll(
                            ((View) mImageView.getParent()).getScrollX(),
                            ((View) mImageView.getParent()).getScrollY(),
                            -mWidth / 2 - ((View) mImageView.getParent()).getScrollX(),
                            0,
                            300);
                    // 本例子下面的語句與上面的等效,因爲((View) mImageView.getParent())就是this
//                    mScroller.startScroll(getScrollX(), getScrollY(), getScrollX(), 0, 300);
                }
                invalidate();
                break;
        }
        return true;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();

        if (mScroller.computeScrollOffset()) {
            ((View) mImageView.getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            // 本例子下面的語句與上面的等效,因爲((View) mImageView.getParent())就是this
//            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章