在写这篇文章之前 ,我仔细读了一篇博客 Android 带你从源码的角度解析Scroller的滚动实现原理
这里把大致的思路都分析清楚了,但是有几点可能让人看有点迷惑 ,我这里做下总结
1、mScrollX 、mScrollY 如何定义的
view X方向上移动x,Y方向上移动 y,假设移动完毕后view ->view1
那么以view1的原点(左上角)建立坐标系,view的原点的坐标就是(-x , -y),那么mScrollX就是-x, mScrollY就是 -y
也就是说向右和向下移动,mScrollX和mScrollY都为负值,向左和向上移动为正值
2、View的scrollTo和scrollBy相比较 ,scrollTo是非常简单粗暴的 , 因为我具体的告诉view,你要给我到哪个具体的位置,完全不顾View刚开始的位置
scrollBy呢,告诉View,相对你原来的位置慢慢移动
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
invalidate();
}
}
}
有了上面的了解 ,我们再来理解下这个 scrollTo的参数 , 这个x 和 y的值始终为 最初(可以简单说为应用启动时候)View原点 相对于 最终View位置的坐标,假如我们从(0,0) 到 (-10,0), 可以简单用scrollTo(-10,0) ,那么如果再用scrollTo(-10,0)呢,当然不变,因为我相对于最初的位置根本没有改变,而如果我现在要再向右移动10呢,用scrollTo(-20,0) ,或者用scrollBy(-10,0),简单用图看看
3、Scroller为了让View的滑动能平滑,而且为View的滑动定制各种滑动效果,有匀速,加速,先加速后减速等等
而且最重要的一点是,Scroller是操作的对象是ViewGroup,ViewGroup再操作View, 因此并不能直接操作View,这个大家可以自己验证
4、Scroller 如何操作ViewGroup的
scroller.startScroll(int startx, int startY, int dx, int dy,int duration) 初始化一些参数 ->
手动调动invalidate()或者postInvalidate()方法让ViewGroup重绘 ->
ViewGroup调用computeScroll() ->
在computeScroll中,手动调用scroller.computeScrollOffset()方法可以判断ViewGroup是否在绘制的过程上,并且在计算坐标,只要在绘制的duration时间内就会返回true ->
手动调动scrollTo达到平滑的绘制效果 ,同时再
手动调动invalidate()或者postInvalidate()方法让ViewGroup重绘
这样循环调动,就可以在duration时间内以细小的变化不断的绘制 ,从而达到一种平滑的绘制效果
今天我们来感受下第一和第二总结
布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/bt_scroll_to"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="ScrollTo"
android:layout_weight="1"/>
<Button
android:id="@+id/bt_reset"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="Reset"
android:layout_weight="1"/>
<Button
android:id="@+id/bt_scroll_by"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="ScrollBy"
android:layout_weight="1"/>
</LinearLayout>
<com.example.admin.scroller_01.MyViewGroup
android:id="@+id/my_vg"
android:padding="20dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffcaee39"
android:orientation="vertical">
<TextView
android:background="#ffee4546"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="30sp"
android:text="TextView1"/>
<TextView
android:gravity="center"
android:background="#ff567fee"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="30sp"
android:text="TextView2"/>
</com.example.admin.scroller_01.MyViewGroup>
</LinearLayout>
看下效果图
再看下自定义的ViewGroup
package com.example.admin.scroller_01;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
* Created by admin on 2015/9/24.
*/
public class MyViewGroup extends LinearLayout {
private int mLastx, mLastY;
private int mDx,mDy;
private Scroller mScroller;
public MyViewGroup(Context context) {
this(context, null);
}
public MyViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void computeScroll() {
super.computeScroll();
}
//利用手指移动,让它移动任意位置,便于观察
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action){
case MotionEvent.ACTION_DOWN:
mLastx = x;
mLastY = y;
break;
case MotionEvent.ACTION_MOVE:
mDx = x - mLastx;
mDy = y - mLastY;
scrollBy(-mDx, -mDy);
mLastx = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
最后就是主的Activity了
package com.example.admin.scroller_01;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button mBtScTo, mBtScBy,mBtReset;
private MyViewGroup mViewGroup;
private int mScreenHeight, mScreentWidth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewGroup = (MyViewGroup) findViewById(R.id.my_vg);
mBtReset = (Button) findViewById(R.id.bt_reset);
mBtScTo = (Button) findViewById(R.id.bt_scroll_to);
mBtScBy = (Button) findViewById(R.id.bt_scroll_by);
mBtScBy.setOnClickListener(this);
mBtScTo.setOnClickListener(this);
mBtReset.setOnClickListener(this);
//获取屏幕的宽高
WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMet = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMet);
mScreenHeight = outMet.heightPixels;
mScreentWidth = outMet.widthPixels;
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.bt_scroll_by:
//x,y为正值代表向左和向上移动,为负值代表向右和向下
mViewGroup.scrollBy(mScreentWidth/10, mScreenHeight/10);
break;
case R.id.bt_scroll_to:
//x,y为正值代表向左和向上移动,为负值代表向右和向下
mViewGroup.scrollTo(mScreentWidth/10, mScreenHeight/10);
break;
case R.id.bt_reset:
mViewGroup.scrollTo(0,0);
break;
}
}
}
最后看下动态效果图(好像GIF图像这里崩了)