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 ...
随机推荐
- 复习C语言
今天突然有感觉复习下C语言了,发现已经好久没有用过C编程了,话说最近都没有编过程序了都,趁现在还有点时间,好好学习下C了.话不多说上题目 请定义一个宏,比较两个数a.b的大小,不能使用大于.小于.if ...
- ESXI安装
1.设置从USB启动 按DEL进入BIOS设置 参考http://www.upanok.com/jiaocheng/68.html 先插入USB,在选择ADVANCAED BIOS FEATURES ...
- 设置结点的ID为固定ID
https://www.java.net//forum/topic/jxta/jxta-community-forum/how-initialize-pse-jxse-27 ————————————— ...
- JavaScript,通过分析Array.prototype.push重新认识Array
在阅读ECMAScript的文档的时候,有注意到它说,数组的push方法其实不仅限于在数组中使用,专门留作通用方法.难道是说,在一些类数组的地方也可以使用?而哪些是和数组非常相像的呢,大家或许一下子就 ...
- CSS选择器的特殊性
在我们为元素添加样式的时候,或多或少会出现一个元素会有几个不同规则的样式.有#id的,有.class,直接标签元素的,还有各种组合起来的选择器.那CSS到底如何解决这些冲突呢,我们这次专门来探讨一下. ...
- Struts2零碎点整理
1. 关于 Struts2 请求的扩展名问题 1). org.apache.struts2 包下的 default.properties 中配置了 Struts2 应用的一些常量 2). struts ...
- AngularJS 学习笔记一
ng-app 指令 ng-app 指令定义了 AngularJS 应用程序的 根元素. ng-app 指令在网页加载完毕时会自动引导(自动初始化)应用程序. 稍后您将学习到 ng-app 如何通过一个 ...
- UIImageView旋转任意角度---实现方法
转自:http://blog.csdn.net/trandy/article/details/6626281 -(UIImageView *) makeRotation:(UIImageView *) ...
- hdu4614Vases and Flowers(线段树,段设置,更新时范围的右边值为变量)
Problem Description Alice is so popular that she can receive many flowers everyday. She has N vases ...
- c语言 while (~scanf("%d%d",&n,&m)) 在这里这个符号“~”是什么意思
按位取反,简单地说就是二进制1变0,0变1 由于scanf是有返回值的,且返回值为int型 特别的此处用法导致只有scanf返回-1,循环才会结束,也就是要返回EOF while (~scanf(&q ...