上节主要总结了view的滑动和view的基础概念,这里就补充view的弹性滑动实现方式
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();
}
}
自定义个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
*
* 可以滑动的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();// 通知重新绘制-走view的draw方法 draw内部走computeScroll
}
//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();
}
});
}
点击按钮,按钮沿着路线滑动。
分析:如上的主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()重绘,只要滑动没有完成就继续递归下去。
动画本身是一种渐进的过程,因此通过它进行滑动具有天然的弹性效果。
核心思想:发送一些列延时消息,从而达到一些渐进式效果。
接下来就举个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事件的分发机制
本文来自<安卓开发艺术探索>笔记总结