什麼是Scroller
我們知道View中有兩個方法可以實現滾動/位置變化,scrollTo/scrollBy
/**
* 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) `
/**
* Move 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 amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically
*/
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
那爲什麼view已經有了滾動的能力還要一個Scroller去單獨實現一套:
- 自帶的效果比較生硬(瞬間完成),沒有中間的過度效果,所以需要scroller
Sroller 使用
郭霖老師文章裏的使用:
public class ScrollerLayout extends ViewGroup {
private static final String TAG = "ScrollerLayout";
/**
* 用於完成滾動操作的實例
*/
private Scroller mScroller;
/**
* 判定爲拖動的最小移動像素數
*/
private int mTouchSlop;
/**
* 手機按下時的屏幕座標
*/
private float mXDown;
/**
* 手機當時所處的屏幕座標
*/
private float mXMove;
/**
* 上次觸發ACTION_MOVE事件時的屏幕座標
*/
private float mXLastMove;
/**
* 界面可滾動的左邊界
*/
private int leftBorder;
/**
* 界面可滾動的右邊界
*/
private int rightBorder;
public ScrollerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// 第一步,創建Scroller的實例
mScroller = new Scroller(context);
ViewConfiguration configuration = ViewConfiguration.get(context);
// 獲取TouchSlop值
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
Log.d(TAG, "ScrollerLayout: default=" + ViewConfigurationCompat.getScaledPagingTouchSlop(configuration)
+ " mTouchSlop=" + mTouchSlop);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d(TAG, "onMeasure: widthMeasureSpec=" + widthMeasureSpec + " heightMeasureSpec=" + heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
// 爲ScrollerLayout中的每一個子控件測量大小
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
// 爲ScrollerLayout中的每一個子控件在水平方向上進行佈局
childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
}
// 初始化左右邊界值
leftBorder = getChildAt(0).getLeft();
rightBorder = getChildAt(getChildCount() - 1).getRight();
Log.d(TAG, "onLayout: changed=" + changed + " l=" + l + " t=" + t + " r=" + r + " b=" + b
+ " leftBorder=" + leftBorder + " rightBorder=" + rightBorder);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mXDown = ev.getRawX();
mXLastMove = mXDown;
break;
case MotionEvent.ACTION_MOVE:
mXMove = ev.getRawX();
float diff = Math.abs(mXMove - mXDown);
mXLastMove = mXMove;
// 當手指拖動值大於TouchSlop值時,認爲應該進行滾動,攔截子控件的事件
if (diff > mTouchSlop) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: event=" + event.getAction());
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
mXMove = event.getRawX();
int scrolledX = (int) (mXLastMove - mXMove);
if (getScrollX() + scrolledX < leftBorder) {
scrollTo(leftBorder, 0);
return true;
} else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
scrollTo(rightBorder - getWidth(), 0);
return true;
}
scrollBy(scrolledX, 0);
mXLastMove = mXMove;
break;
case MotionEvent.ACTION_UP:
// 當手指擡起時,根據當前的滾動值來判定應該滾動到哪個子控件的界面
int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
int dx = targetIndex * getWidth() - getScrollX();
// 第二步,調用startScroll()方法來初始化滾動數據並刷新界面
mScroller.startScroll(getScrollX(), 0, dx, 0);
invalidate();
break;
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
Log.d(TAG, "computeScroll: ");
// 第三步,重寫computeScroll()方法,並在其內部完成平滑滾動的邏輯
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
}
佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.guolin.scrollertest.MainActivity">
<Button
android:id="@+id/scroll_to_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="scrollTo"/>
<Button
android:id="@+id/scroll_by_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="scrollBy"/>
</LinearLayout>
-
TouchSlop是做什麼用的
view跟隨手指移動的最小距離,小於這個距離,view不會跟隨手指移動