View绘制详解(五),draw方法细节详解之View的滚动/滑动问题
关于View绘制系列的文章已经完成了四篇了,前面四篇文章主要带小伙伴们熟悉一下View的体系的整体框架、View的测量以及布局等过程,从本篇博客开始,我们就来看看View的绘制过程。View的绘制涉及到许多细小的知识点,一篇博客很难全部介绍清楚,所以我打算采用农村包围城市的方式,先把这里边会涉及到的各种琐碎的知识点给小伙伴们介绍一遍,然后我们再来看View绘制的整体流程,及draw方法和onDraw方法到底是怎么回事。OK,那么本篇博客我们就先来看看View绘制过程中涉及到的第一个问题,View的滚动。对了,如果小伙伴们还没看过之前的四篇文章,可以先移步这里,那四篇文章有助于你理解本文:
2.View绘制详解(二),从setContentView谈起
好了,废话不多说,来看看今天的话题。
OK,说到View的绘制,我们首先得明白一点,就是我们Android手机中View是没有边界的,View是无限大的,View中的画布Canvas也是无限大的。说到这里有许多小伙伴就有疑问了,我们前面不是刚说了View的测量吗?每一个View都是有大小的,怎么现在又没有大小了呢?其实,我们说的View的大小是指View的父容器分配给它的大小,View的内容只能在父容器分配的大小中显示,如果View的内容显示在父容器分配的区域之外,则用户就看不到这一部分内容,但是并不是说超过的部分就不存在。我们以下图的TextView为例:
TextView显示在任意一个容器中,该容器分配给TextView的大小就是中间黑色框的部分,TextView在绘制它自身的内容的时候只能在中间黑色框中绘制,绘制在黑色框以外的部分将会显示不出来,但并不能意味着这不能绘制。我们在onDraw方法中获取到的canvas,它的大小经过剪裁之后已经是当前控件的大小了,同时,参考的坐标点也变成了当前View的左上角。
OK,为什么要说这个问题呢?因为这里涉及到View绘制时的两个方法ScrollTo和ScrollBy,我们来看看View中draw方法的一小段源码:
@CallSuper
public void draw(Canvas canvas) { ...
... if (!dirtyOpaque) {
drawBackground(canvas);
} ...
...
} private void drawBackground(Canvas canvas) { ...
... final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
这个是绘制View的背景的一段源码,在View绘制之前,要先将canvas平移,平移完了之后再绘制,绘制完了之后再平移成原来的状态。为什么要这么做呢?我们来看下面一张图:
父容器分配给TextView可显示的区域就是中间的黑色框,但是TextView的内容区域由于滚动已经不处于这个黑色框中了(辛弃疾的词即为TextView的内容区域),那怎么样完成这种滚动效果呢?我们需要通过translate方法来让canvas平移。小伙伴们想象一下,中间的黑色框不动,画布向右下平移才能进入到黑色框中,移到黑色框中之后进行绘制,绘制完成之后再移动回去,这个时候TextView的内容区域不就跑到TextView的左上角去了么。那么在这次的移动操作中,我们的scrollX和scrollY两个参数为正数,但是我们的View的内容却往左上角移动,这就是原因。因为我们移动的不是TextView,我们移动的是canvas。我们来看下面一个简单的例子,效果图如下:
当我点击Button时,TextView的内容自动移动到TextView的左下角,我们来看看Button的点击事件:
tv.scrollTo(-100, -100);
负数是向右下角移动,因为负数表示canvas先向左上角x、y轴各移动100px,然后再向右下角x、y各100px,如下:
说到这里,实名反对泡网这篇文章,View 的scrollTo 和scrollBy,误人子弟。
OK,对于一个View而言,scrollTo移动的是View的内容,对于ViewGroup而言,scrollTo移动的则是ViewGroup中的子控件。OK,我们来看看scrollTo的源码:
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
这里就是给mScrollX和mScrollY重新赋值,然会回调onScrollChangeListener重新绘制View。OK,有一个和scrollTo功能相似的方法叫做ScrollBy,我们来看看:
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
scrollBy中也是调用了scrollTo发那个发,不同的是它没有给mScrollX和mScrollY重新赋值,而是在当前的基础上再偏移多少。这也就是我们常说的scrollTo表示移动到哪里,而scrollBy表示移动多少。OK,基于此,我们来做下面一个小案例:
屏幕中有一个TextView,当我的手指在TextView上拖动的时候,我们的TextView可以自由的移动,OK,我们来看看代码:
tv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
((ViewGroup) v.getParent()).scrollBy(-(int) event.getX()+downX, -(int) event.getY()+downY);
break;
}
return true;
}
});
TextView想在它自己所在的容器中滚动,但是TextView自己不能滚动,如果调用了TextView的滚动方法实际上滚动的就是TextView中的文本了。所以我们要调用的是TextView的父容器的滚动方法,而且还要将值改为相反数,原因不用我多说了吧。至于要加上downX和downY两个值,是因为我们的手指不一定就按在TextView的左上角,所以要减去手指到TextView左上角这一段距离。
OK,这就是View绘制方法中涉及到的第一个小细节(View无限大,canvas无限大),以及由这个细节牵扯出来的两个方法scrollTo和scrollBy。OK,draw方法的第一次分析就到这里,有问题欢迎留言讨论。
以上。
View绘制详解(五),draw方法细节详解之View的滚动/滑动问题的更多相关文章
- Android View 绘制流程(Draw) 完全解析
前言 前几篇文章,笔者分别讲述了DecorView,measure,layout流程等,接下来将详细分析三大工作流程的最后一个流程——绘制流程.测量流程决定了View的大小,布局流程决定了View的位 ...
- Android应用层View绘制流程之measure,layout,draw三步曲
概述 上一篇博文对DecorView和ViewRootImpl的关系进行了剖析,这篇文章主要是来剖析View绘制的三个基本流程:measure,layout,draw.仅仅有把这三个基本流程搞清楚了, ...
- Android应用层View绘制流程与源码分析
1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...
- 【view绘制流程】理解
一.概述 View的绘制是从上往下一层层迭代下来的.DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下,依次measure ...
- 【朝花夕拾】Android自定义View篇之(一)View绘制流程
前言 转载请申明转自[https://www.cnblogs.com/andy-songwei/p/10955062.html]谢谢! 自定义View.多线程.网络,被认为是Android开发者必须牢 ...
- 从Android源码分析View绘制
在开发过程中,我们常常会来自定义View.它是用户交互组件的基本组成部分,负责展示图像和处理事件,通常被当做自定义组件的基类继承.那么今天就通过源码来仔细分析一下View是如何被创建以及在绘制过程中发 ...
- Android 使用View绘制文字(DrawText)技术总结
转载请注明出处: http://www.cnblogs.com/renhui/p/7453534.html 这里的绘制文字不是直接调用TextView.setText(String content)去 ...
- Android笔记--View绘制流程源码分析(二)
Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...
- android自定义View绘制天气温度曲线
原文:android自定义View绘制天气温度曲线 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u012942410/article/detail ...
随机推荐
- nodejs 5.2.0文档自翻译——HTTP模块
HTTP Class: http.Agent new Agent([options]) agent.destroy() agent.freeSockets agent.getName(options) ...
- [Hive - LanguageManual] GroupBy
Group By Syntax Simple Examples Select statement and group by clause Advanced Features Multi-Group-B ...
- Google App Engine Deployment 相关问题
1.GAE instance上传成功之后可以正常运行的情况下.在Google账户中“关联的应用和网站”,撤销Google App Engine appcfg对Google账户的访问权限. 再次上传时, ...
- 解开发者之痛:中国移动MySQL数据库优化最佳实践(转)
开源数据库MySQL比较容易碰到性能瓶颈,为此经常需要对MySQL数据库进行优化,而MySQL数据库优化需要运维DBA与相关开发共同参与,其中MySQL参数及服务器配置优化主要由运维DBA完成,开发则 ...
- Tengine – Nginx衍生版
Tengine是淘宝在Nginx基础上开发的一个衍生版.官方的简介说针对大访问量网站的需求,添加了很多高级功能和特性.Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验. ...
- urllib2中自定义opener
正常用Python抓取网页信息,需要用到urllib2,调用urllib2.urlopen(url),可以获得response 反馈信息,再用response.read()即可获得页面的源码. 最简单 ...
- iOS WebServiceFramework网络服务框架浅解
网络服务几乎是每一款成功APP的必备条件,打开你手机你会发现里面不用联网的应用数量十只手指可以数出来,就算是一些以独特技术切入市场的APP如美颜相机,都至少加入了分享功能.下面我先做下简单的回顾兼扫盲 ...
- DATASNAP多表提交之事务控制之通用方法
ERP系统的单据,总是些主从表结构,有一个主表,N个子表,子表又有子表,形成N层,单据数据提交时,主从表数据都要提交,为了保证数据的完整性,必须提供事务控制,要么都提交成功,有一个提交失败所有的提交都 ...
- HDU2079选课时间(母函数)
母函数的简单应用http://acm.hdu.edu.cn/showproblem.php?pid=2079 介绍见另一篇随笔HDU1028Ignatius and the Princess III( ...
- 剑指OFFER之变态跳台阶(九度OJ1389)
题目描述: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳法. 输入: 输入可能包含多个测试样例,对于每个测试案例, 输入包括一个整数n(1 ...