Android(安卓)Scroller的简单使用
1 前言
Scroller是一个专门用于处理滚动效果的工具类,一般在我们自定义View时,要实现滚动或者滑动效果,除了使用动画之外,我们往往会使用Scroller来帮我们完成。在android中如ViewPager、ListView等,内部也是使用了Scroller来实现的。
2 View的Scroll
在介绍Scroller之前,我们知道,在View中自带scroll属性,那就是View中有两个方法,可以完成view的滚动。他们是:
void scrollBy(int x, int y)//滚动x,yMove the scrolled position of your view. void scrollTo(int x, int y) //滚动到x ySet the scrolled position of your view.
需要说明的的两点
1 根据android的坐标体系,当x为负时,向右滚动,为正时向左滚动,y为负向下滚动,y为正,向上滚动
2 以上两个滚动只是滚动的View的内容,并没有滚动View本身,需要滚动view本身时,可以考虑布局属性LayoutParams及属性动画。
因此,针对这一点:我们队某个View做scroll往往并无太大的意义,但是对于ViewGroup来说,其内容往往就是某个子View,对ViewGroup做scroll就有意义!
3 以上滚动是瞬间完成,并没有过渡过程。
另外,以下两个API也是需要注意的,分别返回滚动时的x,y
final int getScrollX() //返回滚动时的left位置 xReturn the scrolled left position of this view. final int getScrollY() //返回滚动时的top 位置 yReturn the scrolled top position of this view.
下面来看一个View使用scroll的例子
<?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>
外层我们使用了一个LinearLayout,然后在里面包含了两个按钮,一个用于触发scrollTo逻辑,一个用于触发scrollBy逻辑。
接着修改MainActivity中的代码,如下所示:
public class MainActivity extends AppCompatActivity { private LinearLayout layout; private Button scrollToBtn; private Button scrollByBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); layout = (LinearLayout) findViewById(R.id.layout); scrollToBtn = (Button) findViewById(R.id.scroll_to_btn); scrollByBtn = (Button) findViewById(R.id.scroll_by_btn); scrollToBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { layout.scrollTo(-60, -100); } }); scrollByBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { layout.scrollBy(-60, -100); } }); }}
效果如下:
3 Scroller API
一般来说,我们使用Scroller主要涉及到以下几个api
final int getCurrX() //获取当前的x偏移Returns the current X offset in the scroll.final int getCurrY() //获取当前的y偏移Returns the current Y offset in the scroll.final int getDuration() //获取持续时间Returns how long the scroll event will take, in milliseconds. //开始滚动void startScroll(int startX, int startY, int dx, int dy, int duration)Start scrolling by providing a starting point, the distance to travel, and the duration of the scroll.//开始滚动void startScroll(int startX, int startY, int dx, int dy)Start scrolling by providing a starting point and the distance to travel. //判断滚动是否完成,true表示没有完成boolean computeScrollOffset()Call this when you want to know the new location.
4 Scroller使用
Scroller的基本用法分为以下几个步骤
1 创建Scroller的实例
2 调用Scroller#startScroll()方法来初始化滚动数据并调用invalidate()刷新界面
3 重写View#computeScroll()方法,并在其内部完成平滑滚动的逻辑,在滚动时,会不断的调用这个方法。由于默认的View的computeScroll()方法为空,我们需要自己实现,不过一般代码也固定
例子如下:
我们自定义一个ViewGroup,仿照ViewPager的类似效果,水平布局
/** * Created by qiyei2015 on 17/6/19. */public class ScrollerLayout extends ViewGroup { /** * 用于完成滚动操作的实例 */ 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); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, 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(); } } @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) { 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() { // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } }}
注意:在重写computeScroll()时,当中的代码往往是固定的,就是判断滚动有没有完成,没有完成就调用View的scrollTo()滚动到指定位置,然后invalidate()刷新界面。
<?xml version="1.0" encoding="utf-8"?><com.example.scrollertest.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 first child view"/> <Button android:layout_width="match_parent" android:layout_height="100dp" android:text="This is second child view"/> <Button android:layout_width="match_parent" android:layout_height="100dp" android:text="This is third child view"/>com.example.scrollertest.ScrollerLayout>
可以看到,这里我们在ScrollerLayout中放置了三个按钮用来进行测试,其实这里不仅可以放置按钮,放置任何控件都是没问题的。
效果如下:
更多相关文章
- Android(安卓)UI框架 Android(安卓)UI控件类简介 android5大布局
- Android(安卓)利用addView 动态给Activity添加View组件
- Android之自制的分页表格控件
- Android(安卓)Studio的TextView实现滚动显示文字
- Android控件之利用selector自定义的带文字的图片按钮
- android 自定义控件学习之三 控件布局常用知识总结
- Android之打造ListView的万能适配器
- Android中UI设计的一些技巧!!!
- 将服务器端字符读取至android的文本控件,换行符变成了黑方块的原