阅读《Android 从入门到精通》(?)——View 和 ViewGroup

当前阶段未提炼要点

版权申明:本文转载自红黑联盟,作者 gemmem,链接为:http://www.2cto.com/kf/201207/143408.html

http://blog.sina.com.cn/s/blog_67abf5570100xi6d.html

http://weizhulin.blog.51cto.com/1556324/311457/

http://www.2cto.com/kf/201506/404121.html

http://blog.csdn.net/lmj623565791/article/details/38960443

http://www.2cto.com/kf/201401/274740.html

1.概念

Android 中的 View 与我们以前理解的“视图”不同。在 Android 中,View 比视图具有更广的含义,它包含了用户交互和显示,更像 Windows 操作系统中的 window;
ViewGroup 是 View 的子类,所以它也具有 View 的特性,但它主要用来充当 View 的容器,将其中的 View 视作自己的孩子,对它的子 View 进行管理,当然它的孩子也可以是ViewGroup 类型;
ViewGroup(树根)和它的孩子们(View 和 ViewGroup)形成了一个树形的层次结构,View 类有接受和处理消息的功能,Android 系统所产生的消息会在这些 ViewGroup 和 View 之间传递。

2.Android 的窗口系统

Android 的窗口系统是 Client/Server 模式的,我在这里只讲窗口系统的客户端;我们所提到的概念:View,ViewGroup,DecorView,ViewRoot都是存在于窗口系统的Client端。
Android 中的 Window 是表示 Top Level 等顶级窗口的概念。DecorView 是 Window 的 Top-Level View,这个 View 可以称之为主 View,DecorView 会缺省的 attach 到 Activity 的主窗口中;ViewRoot 建立了主 View(DecorView) 与窗口系统 Server 端的通讯桥梁, ViewRoot 是 Handler 的子类,即它其实是个 Handler,它接受窗口系统服务器端的消息并将消息投递到窗口系统的客户端,然后消息就从客户端的主 View 往其下面的子 View 传递,直到消息被完全处理掉为止。

DecorView 实际上是一个 ViewGroup。在依存关系上来讲,对单个主窗口来讲,DecorView 是 Top-Level View。View 并不是关注的重点,重要的是我们需要知道消息分发路径是建立在什么关系上的。View 的成员变量 mParent 用来管理 View 上级关系的。而 ViewGroup 顾名思义就是一组 View 的管理,于是在 ViewGroup 构建了焦点管理和子 View 节点数组。这样通过 View 的 mParent 和 ViewGroup 的 mChildren 构建了 Android 中 View 直接的关系网:

3.View 介绍

 

3.1.事件和绘制

绘制流程(可以类比 Cocos2dx 游戏里面的渲染树):
绘制按照视图树的顺序执行。视图绘制时会先绘制子控件。如果视图的背景可见,视图会在调用 onDraw() 之前绘制背景。强制重绘,可以使用 invalidate()。

事件的基本流程如下:
1.事件分配给相应视图,视图处理它,并通知相关监听器;
2.操作过程中如果发生视图的尺寸变化,则该视图用调用 requestLayout() 方法,向父控件请求再次布局;
3.操作过程中如果发生视图的外观变化,则该视图用调用 invalidate() 方法,请求重绘;
4.如果 requestLayout() 或 invalidate() 有一个被调用,框架会对视图树进行相关的测量、布局和绘制;
注意:视图树是单线程操作,直接调用其它视图的方法必须要在UI线程里。跨线程的操作必须使用句柄Handler。

焦点处理:
框架处理焦点的转移,来响应用户输入。isFocusable() 表示视图是否能接受焦点;setFocusable(boolean) 可以改变视图能否接受焦点;触摸屏模式(Touch Mode)的相关函数是 isFocusableInTouchMode() 和 setFocusableInTouchMode(boolean)。

焦点转移:
焦点转移按照就近算法。按哪个方向就近可以在 XML 布局文件中配置:nextFocusDown、nextFocusLeft、nextFocusRight、nextFocusUp,视图请求焦点可以用requestFocus()。

3.2.成员介绍

(2) 成员介绍

 

protected ViewParent mParent;

 

mParent用于记录它的父亲,就是我们前面提到的ViewGroup。

 

 

 

protected OnClickListener mOnClickListener;

 

mOnClickListener是click事件的回调接口.

 

大家经常使用的setOnClickListener(OnClickListener listener):

 

public void setOnClickListener(OnClickListener I) {

 

        if (!isClickable()) {

 

            setClickable(true);

 

        }

 

        mOnClickListener =I;

 

}

 

可以看出,mOnClickListener其实就是保存我们在应用程序中定义的OnClickListener接口的。

 

 

 

public void draw(Canvas canvas)

 

这个函数用于渲染View和它的孩子,我们不应该在子类对它进行override。

 

 

 

protected void onDraw(Canvas canvas)

 

我们一般override此函数来实现自己的绘制操作。

 

 

 

IWindowSession getWindowSession() {

 

        return mAttachInfo != null ? mAttachInfo.mSession : null;

 

}

 

函数getWindowSession()用户得到窗口系统Client端和服务器端通讯的接口IWindowSession。这是一个AIDL接口,android系统中的跨进程通讯就是用AIDL接口实现的。

 

 

 

public final void layout(int l, int t, int r, int b)

 

此函数用于确定View和其子View的尺寸和位置,它的调用发生在onMeasure之后。

 

 

 

protected void onLayout(boolean changed, int left, int top, int right, int bottom)

 

此函数在layout调用完成后执行,View的子类一般override此函数,并在函数中对其每个孩子调用layout方法。

 

 

 

 

 

public View getRootView()

 

此函数用于得到View层次结构的top-level View,即上文中提到的DecorView。

 

 

 

public final void measure(int widthMeasureSpec, int heightMeasureSpec)

 

此函数用户找出View的大小,它的参数widthMeasureSpec、heightMeasureSpec是其父亲传递给它的,这2个参数是View找出其大小时的限制条件,其实真正的精确大小确定是由onMeasure()完成的,onMeasure由measure函数调用。

 

 

 

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

 

此函数测量View并根据其内容来决定View的高和宽,它应该被子类override以实现大小的精确测量。在onMeasure中我们必须调用View.setMeasuredDimension(int, int)来保存测量得到的大小,高和宽分别被保存在View.mMeasuredHeight和View.mMeasureWidth中。

 

 

 

 

 

public boolean onKeyUp(int keyCode, KeyEvent event)

 

此函数会在键盘按键释放后被调用,但前提是View必须获得焦点。

 

 

 

public boolean onTouchEvent(MotionEvent event)

 

此函数用于响应触摸屏事件。

 

 

 

public void invalidate()

 

此函数将调用onDraw,强制重绘。

 

 

 

public void requestLayout()

 

当某些东西发生改变后,当前View层次结构无效了,调用此函数对View的层次结构进行重新布局。

 

4.ViewGroup 介绍

ViewGroup继承于View,它可以包含其他的View,就像一个View的容器,我们可以调用其成员函数addView()将View当作孩子放到ViewGroup中。

 

 

 

我们经常使用的LinearLayout、relativeLayout等都是ViewGroup的子类,ViewGroup类中有一个内部类ViewGroup.LayoutParams,我们经常使用LayoutParams的子类来构造布局参数。

 

 

 

我们也可以自定义自己的布局,以方便日后使用和维护,这时我们就需要继承ViewGroup类并在派生类中重写ViewGroup的一些方法,下面是一个简单的例子:

 

 

 

public class MyViewGroup extends ViewGroup { 

 

    public MyViewGroup(Context context) { 

 

        super(context); 

 

        initChilren(context);   //向容器中添加孩子

 

    } 

 

    private void initChilren (Context context) { 

 

        Button aBtn = new Button(context);

 

        this.addView(aBtn); 

 

 

 

        Button bBtn = new Button(context);

 

        this.addView(bBtn); 

 

    }

 

 

 

    @Override

 

    protected void onLayout(boolean changed, int l, int t, int r, int b)

 

 

//对容器的孩子进行布局。

 

………………

 

………………

 

    child.measure(r - l, b - t); 

 

    child.layout(0, 50, child.getMeasuredWidth(), child .getMeasuredHeight() + 50); 

 

………………

 

………………

 

     }

 

}

 作者:gemmem

 

 


5.Android View

  1. 这个类是描绘块状视图的基类。View会绘制一个包含Drawing是event事件的方形块,View是所有与用户交互的组件的Widgets的基类(Buttons,textField等),View的子类ViewGroup是layouts类的基类,layouts类可以包含其他的View/ViewGroup组件并且定义展示的属性。
    Using Views
    Views在界面中被组织成一个单一的树形。可以通过硬编码或则XML文件来增加一个View,
    View的每个特定的子类可以展示如HTML中Text、images等组件的功能。当你创建一个View时,下面的方法可能是你最想要实现的:
    -Set properties:比如设置TextView的text值,每个组件的属性是不一样的。可以在xml文件中来设置这些属性
    -Set focus:框架可以对焦点的移动做出反应。如果想将焦点设置到某个特定的View,可以使用requestFocus()
    -Set up listeners:View允许客户端设置监听机制来监听自己,当View上有监听器上感兴趣的事件发生时可以通知客户端来处理。 例如:所有的组件都允许设置在得到和失去焦点的时候通知监听器,你可以通过setonFocusChangeListener(View.OnFocusChangeListener)来注册监听器。有一些组件有自己特有的监听事件,入Button的Click事件。
    -Set visibility:可以通过setVisibility(int)来展示或则隐藏View
    特别说明:Android framework负责测绘,定线和绘画Views。一般情况下你不需要重写这些方法除非你想实现一个ViewGroup
    Implementing a Custom View
    实现一个View,首先需要实现框架中一些所有Views公用的方法。不必重写所有所有的方法,可以仅仅重写onDraw(android.graphics.Canvas)
    -------------------------------------------------------------------------------------
    Creation  |Constructors |可以在代码中创建View时候调用这个构造函数
    | |或则在XML文件中配置了这个View的时候创建,
    | |XML形式是包含了所有XML中定义的属性
    ----------------------------------------------------------------------------|onFinishInflate()|当XML中View节点所有子节点都创建好的时候调用的
    ---------------------------------------------------------------------------------------
    Layout |onMeasure(int,int) |用来确定View和它所有子类的大小
    ------------------------------------------------------------------------
    |onLayout(boolean,int,int,int,int)|用来设定所有字节点的大小和位置
    ------------------------------------------------------------------------
    |onSizeChanged(int,int,int,int)|当节点的大小变化时候调用
    ---------------------------------------------------------------------------------------Drawing  |onDraw(Canvas)   |当节点需要给它的内容着色的时候调用
    ---------------------------------------------------------------------------------------
    Event |onKeyDown(int,KeyEvent)|当有按键按下发生时候调用
    ------------------------------------------------------------------------
    Processing |onKeyUp(int,KeyEvent) |当有按键松开始调用
    ------------------------------------------------------------------------
    |onTrackballEvent(MotionEvent)|当有轨迹事件发生时候调用
    ------------------------------------------------------------------------
    |onTouchEvent(MotionEvent)|当触摸屏幕的事件发生时候调用
    ---------------------------------------------------------------------------------------
    Focus |onFocusChanged(boolean,int,Rect)|当View得到或则失去焦点时候调用
    ---------------------------------------------------------------------------------------Attaching|onAttachedToWindow() |当View被添加到window中时调用
    ------------------------------------------------------------------------
    |onDetachedFromWindwo() |当View从window中删除时候调用
    -----------------------------------------------------------------------|onWindowVisibilityChanged(int) |当包含该View的window可见性发生变化的时候调用
    ----------------------------------------------------------------------------------------
    IDs
    Views都需要有个ID来确定这个View。
    XML文件配置示例:
    <Button
    android:id="@+id/my_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/my_button_text"/>
    代码定位View示例:
    Button myButton = (Button)findViewById("my_button");
    View的ID不是必须的,建议在一个查找树中保持它的唯一性

Position
View在系统中展示为一个方形状。View可以通过width,height来定位left和top两个坐标(单位为pixel)
可以通过getLeft()来返回方形的左上角的X值、getTop()来返回方形的左上角的Y值(这里返回的值是相对于父节点左上角的位置)
为了方便,android还提供了getRight(),getBottom()方法来获取方形框的右下脚的坐标。

Size,padding and margins
View视图有两个width和height的值:一个指的是在父View里所占的大小(通过getMesuredWidth()和getMeasuredHeight()获得)
另一个指View实际的大小的值(通过getWidth()和getHeight()来获得)。两者不限定是否需要相等。
padding值和HTML的一样指内边距,可以通过setPadding(int,int,int,int)来设定以及通过getPaddingLeft(),getPaddingTop()
getPaddingRight()和getPaddingBottom()来获取
View并不提供外边距属性,当时ViewGroup提供这个属性(View.MarginLayoutParams).

Layout:
Layout是一个两步的过程:测绘和布局。测绘是通过measure(int,int)实现的,是一个对View树从上到下的
遍历过程。在递归过程中,向树节点插入自己的尺寸,最后,每个View保存了它的大小。布局是通过
layout(int,int,int,int,int,int)实现的,在遍历过程中,每个父节点通过View的大小来布置子View的位置。
当视图measure()方法返回时,View和它的子节点的getMeasureWidth()和getMeasureHeight()的值必须设置好了,
这样确保了当测绘结束时,所有的父节点都可以接收其子节点的尺寸数据。一个父级View可能多次调用子View的
measuer()方法。比如,父级View在不指定大小条件下遍历子Views来确定需要多大的尺寸,当所有子Views的尺寸大小
太大或太小时,父级View会再次调用measure()方法来设置实际的值。
测绘步骤通过两个类来传递尺寸:子类通过View.MeasureSpec类告诉父级类自己的的尺寸和位置,LayoutParams子类
则通过width和height描述了View的的大小。每个维度可以这样定义:
-一个确切的数字
-MATCH_PARENT,子类将覆盖怎个父级(不包括填充值)
-WRAP_CONTENT,子要求大小能覆盖其内容(包含填充值)
不同的ViewGroup子类有不同的LayoutParams子类。
比如,AbsoluteLayout包含了一个含有X、Y值的LayoutParams子类
MeasureSpecs类用于父级向子类传递需求,一个MeasureSpec有三种模式:
-未指定:父级View的大小由子Views决定。比如LinearLayout()通脱调用高度指定为UNSPECIFIED宽度指定为240的
measure()方法时,父级的Height大小由子视图的高度。
-确切的值:父级向子View设置一个确定的值并且确保子类符合这个设定
-最大值:父级向子View设定一个最大值,并且确保子Views符合这个设定
通过requestLayout()初始化一个布局,这个方法一般在视图认为当前约束不能满足其要求时候调用

Drawing
画图的过程是通过遍历树来展示View有效部分来实现的。因为遍历树是有序的,所以父节点优先于
子节点绘制出来,兄弟节点则通过在树中的顺序来展示框架不会在有效区域之外画图,
并且会画好Views的背景。可以通过invalidate()方法来执行绘图。

Event Handing and Threading
视图的生命周期如下:
-当一个事件发生时会被派发到适当的View中,视图处理事件并且通知所有的监听器
-在处理事件的过程中,如果视图的边界发生了变化,那么View会调用requestLayout()方法
-相似的情况下,当视图的可视性发生了变化,那么需要调用invalidate()方法
-不管requestLayout()或则invalidate()方法被调用,框架都会重新测绘和布局,并且在树中合适的地方画上view
注意:整个视图树是在一个线程内处理的,因此调用任何方法时,必须在UI现场内处理,如果其他线程内需要更新
view的视图状态,则需要通过Handler类

Focus Handing
框架会处理常规的焦点移动以应对用户输入作出反应。包括试图移动或隐藏或新视图可见时改变焦点。视图通过
isFocusable()方法提示其是否需要焦点。调用setFocusable(boolean)方法来设置视图是否需要焦点
焦点移动基于一种算法:找到指定方向上最近的邻居。在极少情况下,算法的结果与开发人员期望的不一样。
可以通过在布局文件中设置如下四个属性来明确定义属性指向:nextFocusDown,nextFocusLeft,nextFocusRight
nextFocusUp.要强制一个视图获取焦点,调用requestFocus()方法。

Touch Mode
当用户操作方向键时最好是活的焦点的组件一些动作来提示用户那个组件获得了焦点。当设备拥有触摸功能时,用户
可以通过触摸来选定组件。这个模式叫做"Touch mode".
在一个有触摸能力的设备中,一旦用户触摸了屏幕,设备就会进入到"touch mode"。只有isFocusableInTouchMode()
为true的组件才可以活的焦点,比如editing组件,其他的比如buttons不会获得焦点,它只关心点击操作。
任何时候用户点击了方向键,设备就会退出touch模式而进入到按键模式
可以在Activitys中通过isInTouchMode()方法来确定设备是否在触摸模式中。

Scrolling
框架对期望在内部滚动其内容的视图提供了基本的支持,包括跟踪X和Y的滚动偏移来画滚动条。可通过srolllBy(int,int)
,scrollTo(int,int)和awakenScrollBars()来了解更多细节

Tags
与ID不同,标枪不用来识别视图。标签是可以关联到视图上的额外信息。通常用来方便在视图中存储与视图相关的数据,
而不是存放在独立的结构中。

Animation
通过setCurrentAnimation(Animation)或startAnimation(Animation)把一个动画对象加到View中。动画可以改变View的
比例,旋转,平移以及ALPHA。如果有子视图,则该动画会影响整个视图树。当动画开始,框架负责重画视图,知道动画结束。

Security
大部分时候应用应该可以确认动作所需要的所有内容比如获得访问的权限,购买东西以及点击广告链接。
不幸的是,一些恶意的应用会视图欺骗用户去操作这些动作,用户不经意的就会操作隐藏在view后面的动作。
作为一种补救,框架提供了一种轻的过滤框架来提高比较敏感模块的安全性。
可以通过setFilterTouchesWhenObscured(boolean)或则android:filterTouchesWhenObscured来启用过滤机制。
启用后,框架会在用户启动另外一个view的时候锁定原先的view。比如当有警告,对话或其他视窗在view上面展示时
就不在允许点击view了。
在更小的控制颗粒度下。可以重写onFilterTouchEventForSecurity(MotionEvent)来实现你自己的安全策略。
也可以查看FLAG_WINDOW_IS_OBSCURED.

Constants
int |GONE |视图不展示,并且不占用任何布局空间

int|VISIBLE|视图时可见的

interfaces
public static interface OnClickListener:用于响应View的点击事件

public Methods
abstract void onClick(View v):处理点击事件的方法。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值