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

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