Android Scroller的使用及自我理解
2019独角兽企业重金招聘Python工程师标准>>>
以前做一个看书的项目时,有个翻书的功能,当手指滑动书页移动一段然后抬起后,需要页面view自动完成剩余的操作:
1、当滑动距离大于某个设定值时,自动滚动到末尾处,翻一页。
2、当滑动距离小于该设定值时,自动回滚到起始处,还原。
实现这个功能,当时是用了Scroller来实现的,由于自己还是菜鸟,搞了老久没懂,最近看android时,猛然发现,原来是这样啊。
我一步步来说下吧:
1、Scroller的最简单用法解释
网上有很多关于Scroller的用法,很多讲的真的很不错,但我觉得,他们的讲解还是过于繁琐,这里我就最最简单的说下吧,这个Scroller到底怎么回事,且看下面代码:
下面的这个demo1,就一个布局,linearlayout上放置一个button,点击button,我们就调用Scroller的相关方法:
布局文件xml:
<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" ><Buttonandroid:id="@+id/button1"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignLeft="@+id/textView1"android:layout_margin="10dp"android:text="run Scroller " />
</LinearLayout>
控制器activity:
package cn.helloclq.nb.scroller;import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.Scroller;public class MainActivity extends Activity {private Scroller mScroller;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mScroller = new Scroller(this);this.setContentView(R.layout.activity_main); findViewById(R.id.button1).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mScroller.startScroll(-10, -100,- 200, -300, 1000);new Thread(){public void run() {while(mScroller.computeScrollOffset())// 如果mScroller没有调用startScroll,这里将会返回false。{Log.i("scroller", "getCurrX()= "+mScroller.getCurrX()+" getCurrY()="+mScroller.getCurrY());try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}};}.start();}});}
}
运行后,点击按钮,就可以看到效果,且看控制台日志输出:
05-02 11:30:55.455: I/scroller(30287): getCurrX()= -10 getCurrY()=-100
05-02 11:30:55.510: I/scroller(30287): getCurrX()= -26 getCurrY()=-124
05-02 11:30:55.557: I/scroller(30287): getCurrX()= -63 getCurrY()=-180
05-02 11:30:55.612: I/scroller(30287): getCurrX()= -111 getCurrY()=-251
05-02 11:30:55.658: I/scroller(30287): getCurrX()= -144 getCurrY()=-301
05-02 11:30:55.713: I/scroller(30287): getCurrX()= -166 getCurrY()=-334
05-02 11:30:55.760: I/scroller(30287): getCurrX()= -181 getCurrY()=-356
05-02 11:30:55.815: I/scroller(30287): getCurrX()= -191 getCurrY()=-371
05-02 11:30:55.862: I/scroller(30287): getCurrX()= -197 getCurrY()=-381
05-02 11:30:55.916: I/scroller(30287): getCurrX()= -201 getCurrY()=-387
05-02 11:30:55.963: I/scroller(30287): getCurrX()= -204 getCurrY()=-391
05-02 11:30:56.018: I/scroller(30287): getCurrX()= -206 getCurrY()=-394
05-02 11:30:56.065: I/scroller(30287): getCurrX()= -208 getCurrY()=-396
05-02 11:30:56.119: I/scroller(30287): getCurrX()= -208 getCurrY()=-398
05-02 11:30:56.166: I/scroller(30287): getCurrX()= -209 getCurrY()=-398
05-02 11:30:56.221: I/scroller(30287): getCurrX()= -209 getCurrY()=-399
05-02 11:30:56.268: I/scroller(30287): getCurrX()= -210 getCurrY()=-399
05-02 11:30:56.322: I/scroller(30287): getCurrX()= -210 getCurrY()=-400
05-02 11:30:56.369: I/scroller(30287): getCurrX()= -210 getCurrY()=-400
05-02 11:30:56.424: I/scroller(30287): getCurrX()= -210 getCurrY()=-400
05-02 11:30:56.471: I/scroller(30287): getCurrX()= -210 getCurrY()=-400
从日志输出的数据一看,你应该能大致知道这个scroller的作用了吧
mScroller.startScroll(-10, -100,- 200, -300, 1000);
我们在button的事件处理里做的是调用了 public void startScroll (int startX, int startY, int dx, int dy,int duration)
这个方法,然后我们在线程中一直查看scroller的几个属性数值,然后打印了出来,从日志可以看出,scroller中的这些数值,是按(int startX, int startY, int dx, int dy)来变化的,并且是在intduration这个时间段内完成的。我们设置线程的睡眠时间是50毫秒,而打印了总共20条日志,20* 50 = 1000,正好是我们设置的这个时间。
如果我们在调用了public void startScroll (int startX, int startY, int dx, int dy,in tduration) ,让我们的view重新绘制,并且利用scroller的几个属性数值来确定view的位置或其他的什么,然后不断的循环调用,你说会出现什么呢?答案就不用说了吧(view会动起来吧),这个就是我们平时在项目中利用scroller的主要思路了,而一般的时候,我们会多绕了几道弯儿而已。
2、Scroller结合view的用法
首先,我们简单的介绍下view,查看android的源码,你会发现如下的方法:
/**
* Called by a parent to request that a child update its values for mScrollX
* and mScrollY if necessary. This will typically be done if the child is
* animating a scroll using a {@link android.widget.Scroller Scroller}
* object.
*/
public void computeScroll()
{
}
该方法就是留给我们去覆写的,它一般都会被该view的父类viewGroup在绘制该view时调用,具体的,就不多说了,大家可以参见相关博客,如果我们在这个方法里,调用 scroller的相关属性来修改view的相关属性或调用其他方法,是不是可以做很多事呢?
下面看看改造后的demo2:
先介绍一个函数,view下面的:
public void scrollTo (int x, int y) Added in API level 1
Set the scrolled position of your view. This will cause a call to onScrollChanged(int, int, int, int) and the view will be invalidated.Parameters
x the x position to scroll to
y the y position to scroll to
该函数可以使view中的内容滚动到指定位置:
demo2:
该demo没有xml,直接就一个activity:
package cn.helloclq.nb.scroller;import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Scroller;public class MainActivity extends Activity {LinearLayout demoSubview1, demoSubview2, demoViewGroup;private Scroller mScrollerViewGroup;private Scroller mScrollerView;
// private Scroller mScroller;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mScrollerViewGroup = new Scroller(this);mScrollerView = new Scroller(this);demoSubview1 = new DemoView(this);demoSubview2 = new DemoView(this);demoSubview1.setBackgroundColor(this.getResources().getColor(android.R.color.darker_gray));demoSubview2.setBackgroundColor(this.getResources().getColor(android.R.color.white));demoViewGroup = new DemoViewGroup(this);demoViewGroup.setOrientation(LinearLayout.VERTICAL);LinearLayout.LayoutParams p0 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);this.setContentView(demoViewGroup, p0);LinearLayout.LayoutParams p1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);p1.weight = 1;demoViewGroup.addView(demoSubview1, p1);LinearLayout.LayoutParams p2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);p2.weight = 1;demoViewGroup.addView(demoSubview2, p2);DemoButton btn1 = new DemoButton(this);btn1.setText("run Scroller in viewGroup");btn1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mScrollerViewGroup.startScroll(-10, -100,- 200, -300, 1000);new Thread(){public void run() {while(mScrollerViewGroup.computeScrollOffset())// 如果mScroller没有调用startScroll,这里将会返回false。{Log.i("scroller", "getCurrX()= "+mScrollerViewGroup.getCurrX()+" getCurrY()="+mScrollerViewGroup.getCurrY());try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}};}.start();}});demoSubview1.addView(btn1);DemoButton btn2 = new DemoButton(this);btn2.setText("run Scroller in view");btn2.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mScrollerView.startScroll(-10, -100,- 200, -300, 1000);new Thread(){public void run() {while(mScrollerView.computeScrollOffset())// 如果mScroller没有调用startScroll,这里将会返回false。{Log.i("scroller", "getCurrX()= "+mScrollerView.getCurrX()+" getCurrY()="+mScrollerView.getCurrY());try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}};}.start();}});demoSubview2.addView(btn2);}class DemoButton extends Button {public DemoButton(Context ctx) {super(ctx);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.i("DemoButton", "------ onDraw------");}public void computeScroll() {Log.i("DemoButton", " --------------------computeScroll-----------");// Log.i(TAG, "getCurrX = " + mScroller.getCurrX());if (mScrollerView.computeScrollOffset())// 如果mScroller没有调用startScroll,这里将会返回false。{// 因为调用computeScroll函数的是MyLinearLayout实例,// 所以调用scrollTo移动的将是该实例的孩子,也就是MyButton实例scrollTo(mScrollerView.getCurrX(), 0);Log.i("DemoButton", "getCurrX = " + mScrollerView.getCurrX());// 继续让系统重绘invalidate();}}}class DemoView extends LinearLayout {public DemoView(Context ctx) {super(ctx);}@Overridepublic void computeScroll() {Log.i("DemoView", " DemoView --------------------computeScroll-----------");if (mScrollerViewGroup.computeScrollOffset())// 如果mScroller没有调用startScroll,这里将会返回false。{// 因为调用computeScroll函数的是MyLinearLayout实例,// 所以调用scrollTo移动的将是该实例的孩子,也就是MyButton实例scrollTo(mScrollerViewGroup.getCurrX(), 0);Log.i("DemoView", "getCurrX = " + mScrollerViewGroup.getCurrX());// 继续让系统重绘getChildAt(0).invalidate();}}}class DemoViewGroup extends LinearLayout {public DemoViewGroup(Context ctx) {super(ctx);}@Overrideprotected void dispatchDraw(Canvas canvas) {Log.i("DemoViewGroup", "contentview dispatchDraw");super.dispatchDraw(canvas);}}
}
运行程序效果:
点击第一个button后,日志如下:
05-02 13:38:02.291: I/DemoViewGroup(332): contentview dispatchDraw
05-02 13:38:02.291: I/DemoView(332): DemoView --------------------computeScroll------
05-02 13:38:02.301: I/DemoButton(332): --------------------computeScroll-----------
05-02 13:38:02.311: I/DemoButton(332): ------ onDraw------
05-02 13:38:02.311: I/DemoView(332): DemoView --------------------computeScroll------
05-02 13:38:02.311: I/DemoButton(332): --------------------computeScroll-----------
05-02 13:38:02.311: I/DemoButton(332): ------ onDraw------
05-02 13:39:50.432: I/scroller(332): getCurrX()= -11 getCurrY()=-101 05-02 13:39:50.432: I/DemoViewGroup(332): contentview dispatchDraw
05-02 13:39:50.432: I/DemoView(332): DemoView --------------------computeScroll------
05-02 13:39:50.432: I/DemoView(332): getCurrX = -12
05-02 13:39:50.442: I/DemoButton(332): --------------------computeScroll-----------
05-02 13:39:50.442: I/DemoButton(332): ------ onDraw------
......................此处省略部分日志...................................................
05-02 13:39:51.412: I/DemoViewGroup(332): contentview dispatchDraw
05-02 13:39:51.412: I/DemoView(332): DemoView --------------------computeScroll------
05-02 13:39:51.412: I/DemoView(332): getCurrX = -210
05-02 13:39:51.412: I/DemoButton(332): --------------------computeScroll-----------
05-02 13:39:51.422: I/DemoButton(332): ------ onDraw------
05-02 13:39:51.432: I/DemoViewGroup(332): contentview dispatchDraw
05-02 13:39:51.432: I/DemoView(332): DemoView --------------------computeScroll------
05-02 13:39:51.432: I/DemoButton(332): --------------------computeScroll-----------
05-02 13:39:51.432: I/DemoButton(332): ------ onDraw------
通过日志,看出什么了没?
另外,通过该日志,你了解了view的绘画机制没,应该能发现吧。
最终的效果图片:
点击buttom2,日志就不给出了,效果图片如下:
button内的文字移动到了不可见处,
所以调用scrollTo移动的将是该view的内容,如果是viewGroup的话,就移动了它的孩子view了
所以调用scrollTo移动的将是该实例的孩子,也就是MyButton实例
这会儿明白了没?
后面我会尝试的自己模拟一个scrolelr,其实他的作用就是存储一些基本的数值,其他的啥也没做,也和view没有真正关系的,它只是被动的被调用的
转载于:https://my.oschina.net/chengliqun/blog/126935
Android Scroller的使用及自我理解相关推荐
- [深入理解Android卷一全文-第九章]深入理解Vold和Rild
为什么80%的码农都做不了架构师?>>> 由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的 ...
- 关于OpenGL ES 3D 光晕如何产生的自我理解
2019独角兽企业重金招聘Python工程师标准>>> 星空图的自我理解:可以看作一个透明的半径很大的天球,然后在其表面上绘制大小不一的点,这样便可以够成星空图. 如何产生光晕的自我 ...
- 关于渗透的一些思路持续更新(自我理解)
关于渗透的一些思路持续更新(自我理解) 先回忆一下 准确的是17年才接触到渗透以及ddos 其他hei产东西就不介绍了 接触的自我感觉有点迟 从最开始我也是从阿D明小子那些很傻瓜化的软件走过来的 但是 ...
- [深入理解Android卷一全文-第十章]深入理解MediaScanner
由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断.所以我将在CSDN博客中全文转发这两本书的全部内容. ...
- android binder与handler的简要理解
android的进程之间的通信 采用binder机制,我们先弄明白binder到底是什么. 其实binder属于一个驱动,位于linux内核层,它的作用是完成进程间的通信. 而binder机制包括4个 ...
- Android中BindService方式使用的理解
Android中BindService方式使用的理解 - 唯一小神 - 博客园 最近学习了一下Android里面的Service的应用,在BindService部分小卡了一下,主要是开始没有彻底理解为 ...
- 【Android 10 源码】深入理解 Omx 初始化
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- 【Android 10 源码】深入理解 software Codec2 服务启动
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- [深入理解Android卷二 全文-第八章]深入理解ContentService和AccountManagerService
由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第 ...
最新文章
- Mysql函数group_concat、find_in_set 多值分隔字符字段进行数据库字段值翻译
- Facebook的一些基本操作(网页版)
- 各种AJAX方法的使用比较
- fastd java下载_配置 FastDFS Java 客户端
- K8S部署工具:KubeOperator集群部署
- java课外兴趣小组管理系统_课外兴趣小组活动方案
- 车站信号计算机联锁系统英语,车站信号计算机联锁-复习题
- 深入理解DOM事件机制系列第四篇——事件模拟
- Echart 地图实例
- mac maven安装位置_Maven的安装以及仓库的作用
- 玩转python字典与列表(下)
- 一文读懂如何用深度学习实现网络安全
- 代理app请求地址访问本机服务(同一局域网的本地起的服务器)方便测试app
- 三十款国外IES灯光经典素材整理i
- linux下visual studio code安装cpptools-linux.vsix插件
- Arduino学习总结
- clip gradient
- 81章 老子1章到_《老子》第一章新解
- Nginx安装/负载均衡/反向代理配置与调优
- 世界杯流量大作战,来看各路品牌如何玩转世界杯营销?
热门文章
- python连接数据库mysql失败_解决python连接mysql报错问题
- python爬取网页实时数据_使用 Python 爬取网页数据
- js中字符串全部替换函数(正则表达式)
- 开始VC6学习之旅3
- rpm deb命令集合
- 最大学术出版商妥协!与挪威46所机构签协议,90%出版物免费阅读
- 清华AI的华山论剑现已开启,不服就来战!王小川楼天城唐文斌都曾搅动风云...
- YY创始人体内植入芯片真相调查
- Ooooops! 这通骚扰电话是AI机器人打的(浙大出品,中文很6)
- 马化腾很忙:阐释腾讯新定位、与长安合资建车公司,还出了本新书