通过android.widget.Scroller制作齿轮滚动的
2017-10-22 本文已影响248人
生活简单些
想要做成类似如下效果:

实现自定义view的代码如下:
public class ScrollerLayout extends ViewGroup {
private Scroller mScroller;
/**
* 触摸移动距离超过此值才认为是有效滑动
*/
private int mTouchSlop;
/**
* 移动前第一次按下会记录下
*/
private float mXDown;
/**
* 每一次移动都给它赋值
*/
private float mXMove;
/**
* 按下以及移动过程中都会给它赋值
*/
private float mXLastMove;
/**
* 界面可滚动的左边界, 小于它则重置到此边界
*/
private int mLeftBorder;
/**
* 界面可滚动的右边界,大于它则重置到此边界
*/
private int mRightBorder;
public ScrollerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(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());
}
// 初始化左右边界值
mLeftBorder = getChildAt(0).getLeft();
mRightBorder = getChildAt(getChildCount() - 1).getRight();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mXLastMove = mXDown = ev.getRawX();
break;
case MotionEvent.ACTION_MOVE:
mXLastMove = mXMove = ev.getRawX();
float diff = Math.abs(mXMove - mXDown);
// 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
if (diff > mTouchSlop) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
mXMove = event.getRawX();
// 因为view向右滑scrollX越小,所以计算滑动了多少距离反着减
int scrolledX = (int) (mXLastMove - mXMove);
// 滑动左边过界进行重置
if (getScrollX() + scrolledX < mLeftBorder) {
scrollTo(mLeftBorder, 0);
return true;
}
// 滑动右边过界进行重置
// 因为即便滑到最右边也会有最后一个屏幕宽度不能滑动,所以下面要加上getWidth()
if (getScrollX() + getWidth() + scrolledX > mRightBorder) {
scrollTo(mRightBorder - getWidth(), 0);
return true;
}
scrollBy(scrolledX, 0);
mXLastMove = mXMove;
break;
case MotionEvent.ACTION_UP:
int targetIndex;
// 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面
boolean slideLeft = event.getRawX() < mXDown;
if (slideLeft) {
// 原则是左滑一整屏才会进入下一个getWidth()的倍数,加上0.9意味着右滑到达getWidth()的0.1倍就进入下一个倍数了
targetIndex = (getScrollX() + getWidth() * 9 / 10) / getWidth();
} else {
// 原则上只要右滑一点getScrollX()的长度就不够getWidth()的倍数了,如果未超过0.1的宽度则保持当前倍数
targetIndex = (getScrollX() + getWidth() / 10) / getWidth();
}
int dx = targetIndex * getWidth() - getScrollX();
// 调用startScroll()方法来初始化滚动数据并刷新界面, 最后参数还可以设定动画时间
mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
invalidate();
break;
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
// 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
}
用法也简单:
<?xml version="1.0" encoding="utf-8"?>
<com.apptest.ScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="This is 1 child view"
android:background="@android:color/background_light"/>
<Button
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="This is 2 child view"
android:background="@android:color/holo_red_light"/>
<Button
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="This is 3 child view"
android:background="@android:color/holo_blue_light"/>
<Button
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="This is 4 child view"
android:background="@android:color/holo_blue_dark"/>
<Button
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="This is 5 child view"
android:background="@android:color/holo_orange_light"/>
<Button
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="This is 6 child view"
android:background="@android:color/holo_red_dark"/>
</com.apptest.ScrollerLayout>