View的事件体系(二)view的弹性滑动

上节主要总结了view的滑动和view的基础概念,这里就补充view的弹性滑动实现方式

弹性滑动方式

  • 使用Scroller操作scrollTo/ScrollBy
  • 使用动画
  • 使用延时策略

一、Scroller的使用

1、使用步骤(固定):
1、 创建Scroller对象
Scroller mScroller = new Scroller(context);
//2、 调用方法
        mScroller.startScroll(getScrollX(), getScrollY(), -60, -60);
        invalidate();
//3、 重写view的computeScroll
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
        
2、栗子:

自定义个view:

package com.example.administrator.androidview;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.Scroller;

/**
 * Create by SunnyDay on 2019/04/09
 * <p>
 * 可以滑动的LinearLayout
 */
public class ScrollerLinearLayout extends LinearLayout {
    private final Scroller mScroller;

    public ScrollerLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        //1 创建Scroller对象
        mScroller = new Scroller(context);
    }

    /**
     *
     *滑动到指定位置
     * */
    public void startScroll() {
        //2 调用方法
        mScroller.startScroll(getScrollX(), getScrollY(), -60, -60);
        invalidate();
    }

    //3 重写view的computeScroll
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }

}

xml中使用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:id="@+id/layout">


    <com.example.administrator.androidview.ScrollerLinearLayout
        android:id="@+id/sll"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="设置ImageView背景"/>
    </com.example.administrator.androidview.ScrollerLinearLayout>



</LinearLayout>

mainActivity中调用:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ssl = (ScrollerLinearLayout) findViewById(R.id.sll);
        
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ssl.startScroll();
            }
        });
    }

在这里插入图片描述

点击按钮,按钮沿着路线滑动。

3、Scroller使用原理分析

分析:如上的主activity中的调用:我们知道Scroller的主要入口功能在其对象的startScroll()方法。所以我们便从这开始看源码(如下)

3.1 startScroll源码:

首先进入startScroll,看到这里只是进行了包装。

public void startScroll(int startX, int startY, int dx, int dy) {
        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
    }

继续进入startScroll

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartX = startX;
        mStartY = startY;
        mFinalX = startX + dx;
        mFinalY = startY + dy;
        mDeltaX = dx;
        mDeltaY = dy;
        mDurationReciprocal = 1.0f / (float) mDuration;
    }

看到这我们知道Scroller内部其实啥也没做,只是保存了我们传递来的参数(吧我们传递来的参数用新的值接收)
1、startX、startY表示滑动的起点。
2、dx、dy表示滑动的距离。
3、duration表示滑动事件,整个滑动过程所需时间。

我们看出仅仅调用startScroll是无法让view的内用滑动的,startScroll内部根本没有做滑动相关的事情,那么Scroller如何让view的内容弹性滑动呢?原因就是startScroll下面view的invalidate()方法。
invalidate方法会导致view重绘走view的draw方法,view的draw方法中会调用computeScroll而这个computeScroll是个空实现需要我们自己实现所以会调用我们重写的computeScroll,我们在重写的computeScroll中使用scrollTo,scrollTo的参数就是Scroller对象中保存的滚动的距离。接着postInvalidate();调用进行二次重绘。继续调用computeScroll继续滑动。(参考下图理解)

在这里插入图片描述

接着看:mScroller.computeScrollOffset()

  int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                mCurrX = mStartX + Math.round(x * mDeltaX);
                mCurrY = mStartY + Math.round(x * mDeltaY);
                break;
                ........

在computeScroll()方法中,我们调用了computeScrollOffset()方法进行判断,该方法内部会根据时间的流逝来计算出scrollX和scrollY改变的百分比并计算出当前的值。这个方法的返回值也很重要,返回true表示滑动还未结束,false表示滑动已经结束。所以在这里,我们进行了判断,当其返回true时,就调用scrollTo()方法使View滑动,并调用invalidate()重绘,只要滑动没有完成就继续递归下去。

二、使用动画

动画本身是一种渐进的过程,因此通过它进行滑动具有天然的弹性效果。

三、使用延时策略

核心思想:发送一些列延时消息,从而达到一些渐进式效果。

1、使用方式
  • Handler的postDelayed方法:延时发送消息,消息中进行view滑动。连续不断发送时就可实现弹性滑动。
  • View的postDelayed方法:延时发送消息,消息中进行view滑动。连续不断发送时就可实现弹性滑动。
  • 线程的Sleep()方法:通过在while循环中不断地进行滑动view和sleep就可以实现弹性滑动效果。

接下来就举个Handler的栗子:点击按钮 发送消息,让view移动。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:id="@+id/layout">

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="设置ImageView背景"/>

</LinearLayout>
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "aaa";
    private LinearLayout linearLayout;
    private int mCount = 0;
    public Handler handler = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        linearLayout = (LinearLayout) findViewById(R.id.layout);

        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 0:
                        if (mCount > -1000) {
                            linearLayout.scrollBy(mCount, mCount);
                            mCount--;
                        }
                        break;
                }
            }

        };

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handler.sendEmptyMessageDelayed(0, 5);
            }
        });
    }
}

四、小结

这里就是对上节滑动的补充,到此view的滑动结束。
View的事件体系(三)view事件的分发机制

The end

本文来自<安卓开发艺术探索>笔记总结

猜你喜欢

转载自blog.csdn.net/qq_38350635/article/details/89141033