关于View绘制系列的文章已经完成了四篇了,前面四篇文章主要带小伙伴们熟悉一下View的体系的整体框架、View的测量以及布局等过程,从本篇博客开始,我们就来看看View的绘制过程。View的绘制涉及到许多细小的知识点,一篇博客很难全部介绍清楚,所以我打算采用农村包围城市的方式,先把这里边会涉及到的各种琐碎的知识点给小伙伴们介绍一遍,然后我们再来看View绘制的整体流程,及draw方法和onDraw方法到底是怎么回事。OK,那么本篇博客我们就先来看看View绘制过程中涉及到的第一个问题,View的滚动。对了,如果小伙伴们还没看过之前的四篇文章,可以先移步这里,那四篇文章有助于你理解本文:

1.View绘制详解,从LayoutInflater谈起

2.View绘制详解(二),从setContentView谈起

3.View绘制详解(三),扒一扒View的测量过程

4.View绘制详解(四),谝一谝layout过程

好了,废话不多说,来看看今天的话题。

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的滚动/滑动问题的更多相关文章

  1. Android View 绘制流程(Draw) 完全解析

    前言 前几篇文章,笔者分别讲述了DecorView,measure,layout流程等,接下来将详细分析三大工作流程的最后一个流程——绘制流程.测量流程决定了View的大小,布局流程决定了View的位 ...

  2. Android应用层View绘制流程之measure,layout,draw三步曲

    概述 上一篇博文对DecorView和ViewRootImpl的关系进行了剖析,这篇文章主要是来剖析View绘制的三个基本流程:measure,layout,draw.仅仅有把这三个基本流程搞清楚了, ...

  3. Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

  4. 【view绘制流程】理解

    一.概述 View的绘制是从上往下一层层迭代下来的.DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下,依次measure ...

  5. 【朝花夕拾】Android自定义View篇之(一)View绘制流程

    前言 转载请申明转自[https://www.cnblogs.com/andy-songwei/p/10955062.html]谢谢! 自定义View.多线程.网络,被认为是Android开发者必须牢 ...

  6. 从Android源码分析View绘制

    在开发过程中,我们常常会来自定义View.它是用户交互组件的基本组成部分,负责展示图像和处理事件,通常被当做自定义组件的基类继承.那么今天就通过源码来仔细分析一下View是如何被创建以及在绘制过程中发 ...

  7. Android 使用View绘制文字(DrawText)技术总结

    转载请注明出处: http://www.cnblogs.com/renhui/p/7453534.html 这里的绘制文字不是直接调用TextView.setText(String content)去 ...

  8. Android笔记--View绘制流程源码分析(二)

    Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...

  9. android自定义View绘制天气温度曲线

    原文:android自定义View绘制天气温度曲线 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u012942410/article/detail ...

随机推荐

  1. bzoj 3207 花神的嘲讽计划Ⅰ(哈希法+主席树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3207 [题意] 给定一个文本串,多次询问K长的模式串是否在文本[l,r]区间内出现. ...

  2. web服务器分析与设计(一)

    自己写一个简单的服务器. 面向对象分析与设计第一步:获取需求(基于用例) 功能:1,支持html静态网页,2,支持常用HTTP请求,且容易扩展支持不现请求 3,可以发布站点 补充:至于对动态网页等高级 ...

  3. HDFS分布式文件系统设计思想

    HDFS设计目标 1)硬件错误是常态,数据保存需要冗余. 2)数据批量读取,Hadoop擅长数据分析而不是事务处理. 3)大规模数据集. 4)简单一致醒模型,降低系统复杂度,文件一次写入多次读取, 5 ...

  4. JAVA NIO 类库的异步通信框架netty和mina

    Netty 和 Mina 我究竟该选择哪个? 根据我的经验,无论选择哪个,都是个正确的选择.两者各有千秋,Netty 在内存管理方面更胜一筹,综合性能也更优.但是,API 变更的管理和兼容性做的不是太 ...

  5. HDU 5794 A Simple Chess (容斥+DP+Lucas)

    A Simple Chess 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5794 Description There is a n×m board ...

  6. Spring EL ternary operator (if-then-else) example

    Spring EL supports ternary operator , perform "if then else" conditional checking. For exa ...

  7. jdk的wsimport方法实现webservice客户端调用服务

    1.配置好jdk环境,打开命令行,输入wsimport回车能看到很多该命令的参数, -s:要生成客户端代码的存储路径 -p:对生成的代码从新打包 这两个最常用. 在打开的命令行中输入:wsimport ...

  8. Linux命令之env:显示当前用户的环境变量

    Linux系统里的env命令可以显示当前用户的环境变量,还可以用来在指定环境变量下执行其他命令.下面来比较一下set,env和export命令的异同:set命令显示当前shell的变量,包括当前用户的 ...

  9. UIImage 相关操作

    修改UIImage大小 修改UISlider的最大值和最小值图片的时候,发现需要修改图片的大小,否则会导致UISlider变形.目前苹果还不支持直接修改UIImage类的大小,只能修改UIImageV ...

  10. rxjava各种使用场景

    1. 数据的三级缓存 final Observable memory = Observable.create(new Observable.OnSubscribe() { @Override publ ...