您的位置:首页 > 移动开发 > Android开发

Android绘图机制(一) View类

2015-11-01 11:25 363 查看
对android绘图机制的理解,在Android学习中可谓至关重要,包括自定义控件也是使用非常频繁的内容。最近在项目中遇到一个比较棘手的问题,项目中好几个模块都用到ListView或者GridView的”下拉刷新,上拉加载更多“功能 。一开始在网上找了大牛写的作品,用在项目中后发现时不时会出现卡壳的现象,改进以后会有所改善,不过还是感觉有所欠缺。无奈我是个处女座菜鸟,尝试着找出这些问题的根本原因却发现无从下手,所以先补补基础。( 纯文字看着确实很费劲,所以顺便引用下其他的文章)

概述

View类代表UI组件的基本创建块。一个View视图占用屏幕的一个矩形区域,并负责绘制和事件处理。View类是基本控件的基类,用于创建UI组件(如按钮button,文本域textview等)。视图组ViewGroup类是布局类的基类,布局类是不可见的容器,用于装载其他的视图(或者其他的ViewGroup)并可以申明他们的属性。


(这个矩形是没有边界的,可参考http://www.open-open.com/lib/view/open1328834050046.html

开发指南

若要获取关于创建UI组件的信息参考文档,可以阅读文档中User Interface的开发指南。

使用视图

窗体中所有的视图都被排列在一个独立的是视图树中。你可以通过硬编码的形式添加视图,或者在一个或者多个xml文件中指定一个视图树。android已经默认提供了许多的独特的视图子类,用于展示文本,图片,或者其他内容。一旦你创建一颗视图树,你将可以进行下列一系列经典的动作:

* 设置属性: 例如,设置TextView的文本。可用的属性和方法在控件视图中可能各不相同,需要注意的是在创建时可见的属性一般可以再xml布局文件中设置。
* 设置焦点: android框架会处理移动焦点以响应用户输入,如果要强制设置焦点,可以调用requesFocus()。
* 设置监听器:视图允许客户端设置监听器,当相关事件在视图上发生时,监听器会收到通知。例如,允许给视图设置一个监听器以获取焦点得失通知。你可以通过setOnFocusChangeListener(android.view.View.OnFocusChangeListener)注册焦点监听器。其他的视图子类可能提供更多的特定的监听器。例如,一个Button会暴露出一个监听器,用于通知按钮的点击事件


注:android框架负责测量,布局和绘制视图(这三个过程是不一样的)。你不应该在视图中主动调用完成这些动作的方法,除非你实现了ViewGroup。

自定义控件

要创建一个自定义视图,你通常需要提供一些重写的标准方法,框架会在所有的视图上调用这些方法。不过,并不需要重写所有的方法,甚至可以仅仅重写onDraw(android.graphics.Canvas).


自定义View的方法

onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法

onMeasure() 检测View组件及其子组件的大小

onLayout() 当该组件需要分配其子组件的位置、大小时

onSizeChange() 当该组件的大小被改变时

onDraw() 当组件将要绘制它的内容时

onKeyDown 当按下某个键盘时

onKeyUp 当松开某个键盘时

onTrackballEvent 当发生轨迹球事件时

onTouchEvent 当发生触屏事件时

onWindowFocusChanged(boolean) 当该组件得到、失去焦点时

onAtrrachedToWindow() 当把该组件放入到某个窗口时

onDetachedFromWindow() 当把该组件从某个窗口上分离时触发的方法

onWindowVisibilityChanged(int): 当包含该组件的窗口的可见性发生改变时触发的方法

IDs
视图可以与一个整型的id相关联,这些id通常定义在布局文件中,用于在一棵视图树中寻找指定的视图。视图的id要求在整棵视图树中是唯一的,不过,最好确保他们至少在你正在搜索的视图树是唯一的。

位置

视图的几何图形是一个矩形。视图具有一个位置,通过left和top坐标对和以及宽高度尺寸表示。位置的单位是像素。通过调用getLeft(),getTop()方法可以检索视图的位置。 前者返回表示这个视图 矩形的左边界或者x坐标。后者返回顶部或者y轴坐标。这两个方法都返回相对于其父类的位置。例如当调用getLeft()返回20时,表示这个视图位于父类的左边界偏右20像素的位置。
此外,有一些快捷的方法被提供,以避免不必要的计算,既getRight()和getBottom()。这个方法返回视图矩形的右边和底边的坐标。例如,调用getRight()方法相当于计算:getLeft()+getWidth()。(可以查看文档中Size部分的内容获取更多关于width的信息)。

Size,Padding,Margins

视图的尺寸通过宽度和高度表达。一个视图实际上具有两对宽高值。第一对是测量高度和测量宽度。这组尺寸定义申明其希望在父容器中获得多大的空间(可以查看布局类Layout获取更多细节),测量尺寸可以通过调用getMeasureHeight()和geMeasureWidth()获得。第二对是通常讲的宽和高,或者称为绘制宽度和绘制高度。这些尺寸表明了视图在屏幕上绘制时和布局后的实际大小。测量尺寸的大小和绘制尺寸的大小可能不一样。宽度和高度可以通过调用getWidth()和getHeight()获得。测量尺寸时,视图会将padding计算在内。padding值通过left,top,right,bottom表达,设置padding可以用于指定视图内容的偏移量。例如,设置左内边(leftPadding)为2像素可以将视图内容从左向右偏移2像素。可以通过调用setPadding(int, int, int, int) 或者setPaddingRelative(int, int, int, int)进行设置,并通过getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom(), getPaddingStart(), getPaddingEnd()查询。视图支持通过硬编码设置一个内边padding,但不支持以硬编码方式设置外边margin。不过, 视图组ViewGroup提供了相应的方式。可以查看ViewGroup的ViewGroup.MarginLayoutParam获取更多内容。

布局

布局过程包含两个阶段。测量阶段和布局阶段。测量阶段在measure(int, int)中完成,是一个从视图树由上到下的深度遍历过程。每个视图在递归阶段中将其尺寸规则向下传递。测量阶段结束时,每个视图保存了各自的测量规则。第二个阶段发生在layout(int, int)中,也是由上到下的。在这个传递过程中,每个父类都会根据测量过程中计算出来的尺寸放置其子视图。
当一个视图的measure(int,int)方法返回时,它的getMeasureWidth()和getMeasureHeight()必须被设置,然后才是该视图的所有后代视图。一个视图的测量尺寸必须遵从其父视图暴露出来的约束。这就确保了在测量过程的最后,所有的父视图都能接受其全部子视图的尺寸规则。一个视图可能会在其子视图中调用不止调用一次measure()方法,例如父视图可能会根据不明确的尺寸规则测量找出它们的大小,如果所有子视图的不受限尺寸过大或者过小,则视图会根据实际数值再次调用measure()方法。

测量传递过程使用两个类表达尺寸,View.MeasureSpec类用于告知父视图,他们希望怎样被绘制和放置,LayoutParam基类用于描述他们希望自己的宽度和高度是多少。

尺寸规则的可选值为下列三者之一:
* 一个精确的数值
* MATCH_PARENT, 表示视图想和其视图一样大小(减去父视图的padding)
* WRAP_CONTENT, 表示视图的尺寸足够装载其内容(加上padding)
针对不同的ViewGroup子类,有不同的LayoutParam子类,例如, AbsoluteLayout 具有包含x值和y值的LayoutParams 子类

MeasureSpect 用于将布局要求从父类向下传递到子类,可选值为:

UNSPECIFIED: 父视图用来决定子树想要得到的尺寸,例如,LinearLayout可能会调用子视图的measure(),并设置其高为UNSPECIFIED宽为EXACTLY 240,以找出子视图的高度和给定的高度。
EXACTLY: 父视图用来给子视图设置一个精确的尺寸。子视图必须使用这个尺寸,并保证其所有的后代适用于这个尺寸。
AT_MOST: 父视图用来给子视图设置一个最大尺寸,所有子类必须确保其子视图适合于这个尺寸。
通过调用requestLayout()方法创建一个布局, 这个方法通常由一个View自身调用,调用的情况是其自认为不再适用于当前边界。

绘制

绘制过程是通过遍历视图树并呈现有效区域内的每个视图而完成处理。因为视图树被有序遍历,这就意味着,父视图会在子视图之前被绘制,然后才是依次出现的兄弟视图(视图树深度优先遍历),如果为视图设置了背景图片,那么视图会在回调onDraw()之前为你绘制背景图片。


注意:框架不会绘制无效区域内的视图,强制视图重绘可以调用invalidate()方法。

事件处理和线程
视图的基本周期如下:
1.  时间进入并被传递到适当的视图,视图处理处理事件并通知相关的监听器。
2.  如果处于事件的处理过程中,那么视图的边界可能需要改变,视图会调用requestLayout()。
3.  类似的,如果处理事件的过程中,对应视图的外观改变了,视图会调用invalidate()。
4.  如果既没有调用requestLayout()也没有调用invalidate(),那么框架会负责测量,布局,并绘制视图的适当值。


注意:整个视图树是单线程的,你必须在UI线程中调用视图的任何方法,如果你要在其它视图中工作,并试图从该线程中更新视图的的状态,那你应该使用Handler。

触屏模式

当用户通过方向键导航一个控件时,有必要将焦点给到可以响应动作的控件,以使用户可以看到输入的内容。 如果设备可以接受触屏事件,并且用户通过点触控件或给予焦点开始与之进行交互,那么控件不一定一直是高亮的,而会触发触屏模式。
对于可触屏设备而言,一旦用户点触屏幕,设备将进入触屏模式。从这一点讲,只有视图的 isFocusableInTouchMode()值为真时,才可以获得焦点,比如说EditText控件,其他的可触屏视图在被点触时并不会获得焦点,如按钮。他们自会激发点击监听器。
任何时候,当用户点击方向键时,视图设备会退出点触模式,并找到一个视图接管焦点,所以用户可以在不触屏的情况下恢复与控件的交互动作。
触屏状态被Activity保持着,可以通过调用 isInTouchMode() 查看设备当前是否处于触屏模式。

动画

从Android3.0开始,让视图动画的首选方式是使用android.animation包下的Api,这些基于Animator的类会改变视图对象的实际属性,如透明度和X轴平移量, 这些动作相对于3.0之前的基于Animation而言,之前的动画类只是决定了视图在屏幕上如何绘制,而且,ViewPropertyAnimator 类使视图的动画更加快捷和高效。


不过,你也可以使用android3.0之前的动画类,决定视图应该如何被渲染。你可以通过setAnimation(Animation) or startAnimation(Animation). 将一个Animation对象依附到视图上。动画可以随着时间的流逝改变视图的尺寸,旋转,位移和透明度。如果动画被用在一个具有子视图的视图,那么这个动画会影响该视图节点的整个子视图树。动画开始后,绘图框架会负责重绘视图的外观,直到动画完成。

Scrolling

The framework provides basic support for views that wish to internally scroll their content. This includes keeping track of the X and Y scroll offset as well as mechanisms for drawing scrollbars. See scrollBy(int, int), scrollTo(int, int), and awakenScrollBars() for more details.

视图框架提供了基本的支持使视图能够移动他们的内容,包括包吃x和y轴滑动偏移的轨迹,以及绘制滑动条的机制。请查看scrollBy(int, int),

scrollTo(int, int), 以及awakenScrollBars()查看更多信息。

Tags

Unlike IDs, tags are not used to identify views. Tags are essentially an extra piece of information that can be associated with a view. They are most often used as a convenience to store data related to views in the views themselves rather than by putting them in a separate structure.

Properties

The View class exposes an ALPHA property, as well as several transform-related properties, such as TRANSLATION_X andTRANSLATION_Y. These properties are available both in the Property form as well as in similarly-named setter/getter methods (such as setAlpha(float) for ALPHA). These properties can be used to set persistent state associated with these rendering-related properties on the view. The properties and methods can also be used in conjunction with Animator-based animations, described more in the Animation section.

Security

Sometimes it is essential that an application be able to verify that an action is being performed with the full knowledge and consent of the user, such as granting a permission request, making a purchase or clicking on an advertisement. Unfortunately, a malicious(恶意的) application could try to spoof(欺骗) the user into performing these actions, unaware, by concealing(掩饰) the intended purpose of the view. As a remedy(补救), the framework offers a touch filtering mechanism that can be used to improve the security of views that provide access to sensitive functionality.

To enable touch filtering, call setFilterTouchesWhenObscured(boolean) or set the android:filterTouchesWhenObscured layout attribute to true. When enabled, the framework will discard touches that are received whenever the view’s window is obscured by another visible window. As a result, the view will not receive touches whenever a toast, dialog or other window appears above the view’s window.

For more fine-grained control over security, consider overriding the onFilterTouchEventForSecurity(MotionEvent) method to implement your own security policy. See also FLAG_WINDOW_IS_OBSCURED.

(触摸过滤机制,在toast,dialog,popupwindow等窗口相关的组件出现时屏蔽触摸事件)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: