Android视图控件架构分析之View、ViewGroup

在Android中,视图控件大致被分为两类,即ViewGroup和View,ViewGroup控件作为父控件,包含并管理着子View,通过ViewGroup和View便形成了控件树,各个ViewGoup对象和View对象就是控件树中的节点。在控件树中,以树的深度来遍历查找对应的控件元素,同时,上层控件负责子控件的测量与绘制,并传递交互事件。

Android控件树:

  

AndroidUI界面架构图:

  

一.测量View的工具类:MeasureSpec

1.MeasureSpec包含了测量的模式和测量的大小,通过MeasureSpec.getMode()获取测量模式,通过MeasureSpec.getSize()获取测量大小;

2.MeasureSpec是一个32位的int值,高2位为测量的模式,低30位为测量的大小,使用位运算的目的在于提高优化效率。

二.测量的模式

1.EXACTLY,精确值模式:将layout_width或layout_height属性指定为具体数值或者match_parent。

2.AT_MOST,最大值模式:将layout_width或layout_height指定为wrap_content。

3.UNSPECIFIED: View想多大就多大

三.View类默认的onMeasure()方法只支持EXACTLY模式,如果要支持其它模式,就必须重写onMeasure(),重写onMeasure()的模板代码:

package com.example.demoapp.views;

import android.content.Context;
import android.view.View;

public class MeasuredView extends View {
  public MeasuredView(Context context) {
    super(context);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 调用父类的onMeasure()
    super.onMeasure(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    // 或者直接调用父类的setMeasuredDimension(),因为父类的onMeasure()最终调用了setMeasuredDimension()
    // setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
  }

  /**
   * 测量View的width
   * @param measureSpec MeasureSpec对象
   * @return View的width
   */
  private int measureWidth(int measureSpec) {
    int result = 0;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    if (specMode == MeasureSpec.EXACTLY) {
      result = specSize;
    } else {
      result = 200;
      if (specMode == MeasureSpec.AT_MOST) {
        result = Math.min(result, specSize);
      }
    }
    return result;
  }

  /**
   * 测量View的height
   * @param measureSpec MeasureSpec对象
   * @return View的height
   */
  private int measureHeight(int measureSpec) {
    int result = 0;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    if (specMode == MeasureSpec.EXACTLY) {
      result = specSize;
    } else {
      result = 200;
      if (specMode == MeasureSpec.AT_MOST) {
        result = Math.min(result, specSize);
      }
    }
    return result;
  }
}

四.View的绘制

1.2D绘图必备利器——Canvas

  1)获取Canvas对象的方式:

    a.由方法中的参数传入,例如,View的onDraw()中有一个参数就是Canvas对象

    b.通过构造方法构造,即:Canvas canvas = new Canvas(bitmap),在Canvas的构造方法传入一个Bitmap对象,即可获取一个Canvas对象。通过传入Bitmap对象构造Canvas对象的过程称为“画布的装载”,传入的Bitmap对象承载了多有绘制在Canvas上的像素信息,调用Canvas.drawXXX方法(如:Canvas.drawBitmap(bitmap, 0, 0, null))都将发生在该Bitmap对象上。

  2)利用Canvas绘图

    a.通过Canvas.drwaXXX进行绘制操作将直接作用于Bitmap对象,当再次刷新View的时候,我们将会被绘制的Bitmap对象发生了改变;

    b.利用Canvas和Paint进行绘图;

    c.不管多么复杂、精美的空间,都可以被拆分为一个个小的图形单元,我们只要找到这些图形单元,就可以将控件绘制出来。

五.ViewGroup的测量

  1.ViewGroup的作用:管理子View,如子View的大小、位置;

  2.ViewGroup通过遍历子View,调用子View的Measure()来获得每一个子View的测量结果;

  3.ViewGroup测量完子View,调用子View的Layout()将子View放到合适的位置;

  4.在自定义ViewGroup的时候,通常会重写onLayout()控制子View的显示;

  5.如果需要支持wrap_content属性,必须重写onMeasure()。

六、ViewGroup的绘制

  通常情况下,ViewGoup不需要绘制,但是ViewGroup会使用dispatchDraw()来绘制其子View。

七.自定义View

1.自定义View的时候,通常需要重写onDraw()来绘制View要显示的内容,如果还需要支持wrap_content属性,必须重写onMeasure();

2.通过自定义attrs属性,可以设置新的View属性;

3.View中一些重要的回调方法:

    1)onFinishInflate():从XML中加载组建后回调;

    2)onSizeChanged():组件大小改变时回调;

    3)onMeasure():进行测量;

    4)onLayout():设置显示的位置;

    5)onTouchEvent():触摸事件。

4.实现自定义View的三种常用方法:

    1)通过重写onDraw()对原生控件进行扩展;

    2)通过组合实现新的控件,通常集成一个合适的额ViewGoup,再通过addView()给它添加指定功能的控件,从而组合成新的复合控件。

    3)重写View实现全新的控件,通过重写onDraw(),onMeasure()实现绘制逻辑,重写onTouchEvent()实现交互逻辑。

5.自定义属性

    1)自定义属性的方法:在res资源目录的values目录下创建一个attrs.xml的属性定义文件,文件模板:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="customAttr">
    <attr name="title" format="string" />
    <attr name="fontSize" format="dimension" />
    <attr name="fontColor" format="color" />
    <attr name="background" format="reference|color" />
    <attr name="fontStyle" format="enum" />
    <attr name="shadeSupport" format="boolean" />
  </declare-styleable>
</resources>

    2)通过TypedArray获取自定义属性集,通过TypedArray.getString()、TypedArray.getColor()等方法获取属性值,模板代码:

package com.jy.myrecyclerview.test;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;

import com.jy.myrecyclerview.R;

/**
 * Created by 123 on 2016/5/6.
 */
public class TestCustomAttrs extends View {
  private Context mContext;
  private AttributeSet mAttrs;
  private String mTitle;
  private float mFontSize;
  private int mFontColor;
  private int mBackground;
  private int mFontStyle;
  private boolean mShadeSupport;

  public TestCustomAttrs(Context context) {
    super(context);
    this.mContext = context;
  }

  public TestCustomAttrs(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.mContext = context;
    this.mAttrs = attrs;
  }

  public TestCustomAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.mContext = context;
    this.mAttrs = attrs;
  }

  private void getCustomAttrs() {
    TypedArray ta = mContext.obtainStyledAttributes(mAttrs, R.styleable.customAttr);
    mTitle = ta.getString(R.styleable.customAttr_title);
    mFontSize = ta.getDimension(R.styleable.customAttr_fontSize, 10);
    mFontColor = ta.getColor(R.styleable.customAttr_fontColor, 0);
    mBackground = ta.getColor(R.styleable.customAttr_background, 0);
    mFontStyle = ta.getInt(R.styleable.customAttr_fontStyle, 0);
    mShadeSupport = ta.getBoolean(R.styleable.customAttr_shadeSupport, false);
    ta.recycle();
  }
}
  

6.定义回调接口,实现自定义控件的灵活控制;

7.引用UI模板

    1)自定义控件需要使用命名空间进行引入:xmlns:custom="http://schemas.android.com/apk/res-auto",即将自定义控件的命名空间取名为custom

    2)在XML文件中使用自定义属性的时候,就可以通过这个命名空间来引用,代码模板如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:custom="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

  <com.jy.myrecyclerview.test.TestCustomAttrs
    android:id="@+id/id_recyclerview"
    android:divider="#ffff0000"
    android:dividerHeight="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    custom:title="title"
    custom:fontSize="12sp"
    custom:fontColor="@color/colorPrimary"
    custom:background="@color/colorPrimary"
    custom:shadeSupport="false" />

</RelativeLayout>

九.自定义ViewGroup

  1.需要重写的方法:

    1)onMeasure():对子View进行测量;

    2)onLayout():设置子View的位置;

    3)onTouchEvent():设置触摸交互事件。

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • Android开发中获取View视图宽与高的常用方法小结

    本文实例讲述了Android开发中获取View视图宽与高的常用方法.分享给大家供大家参考,具体如下: 一.根据WindowManager管理器获得 1)这两种方法在屏幕未显示的时候,还是处于0的状态,即要在setContentView调用之后才有效. 2)Activity必须如此设置才能获得view的宽高 //设置为无标题 requestWindowFeature(Window.FEATURE_NO_TITLE); //设置为全屏模式getWindow().setFlags(WindowMana

  • Android ImageView 固定宽高比例的实现方法

    Android ImageView 固定宽高比例的实现方法 本文主要介绍 ImageView 固定宽高比例, 方法一:设置 adjustViewBounds="true", 方法二:使用 Universal-Image-Loader 图片缓存类,需要注意的是方法二和方法一同时使用导致设置无效. 方法一:设置 adjustViewBounds="true" <ImageView android:id="@+id/img_banner" and

  • Android获取屏幕或View宽度和高度的方法

    本文实例讲述了Android获取屏幕或View宽度和高度的方法.分享给大家供大家参考,具体如下: 在Activity中获取屏幕的高度和宽度 Display display=getWindowManager().getDefaultDisplay(); int width=display.getWidth(); int height=display.getHeight(); 在重写ViewGroup中获取屏幕的有效宽度和高度在OnMesure方法中 protected void onMeasure

  • Android视图的绘制流程(上) View的测量

    综述 View的绘制流程可以分为三大步,它们分别是measure,layout和draw过程.measure表示View的测量过程,用于测量View的宽度和高度:layout用于确定View在父容器的位置:draw则是负责将View绘制到屏幕中.下面主要来看一下View的Measure过程. 测量过程 View的绘制流程是从ViewRoot的performTraversals方法开始的,ViewRoot对应ViewRootImpl类.ViewRoot在performTraversals中会调用p

  • Android中RecyclerView的item宽高问题详解

    前言 本文主要给大家介绍了关于Android中RecyclerView的item宽高问题的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 在创建viewholder传入的View时,如果不指定其viewgroup,就会出现宽高只包裹显示内容的问题. View view = LayoutInflater.from(context).inflate(R.layout.test_test,null); 上面的做法就会出问题 改成这样就可以正常显示设置的宽高 View vie

  • Android 获得View宽高的几种方式总结

    <Android开发艺术探索>笔记: 在Activity的onCreate()或者onResume()中去获得View的高度的时候不能正确获得宽度和高度信息,这是因为 View的measure过程和Activity的生命周期不是同步执行的,因此无法保证Activity执行了onCreate onStart onResume时,某个View已经测量完毕了,如果还没有测量完,那么获得的宽高就是0.可以通过下面几种方式来获得: 1.onWindowFocusChanged onWindowFocus

  • Android App中自定义View视图的实例教程

    一.基础 很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 3.重写onMesure 4.重写onDraw 我把3用[]标出了,所以说3不一定是必须的,当然了大部分情况下还是需要重写的. 1.自定义View的属性,首先在res/values/  下建立一个attrs.xm

  • Android开发之自定义View(视图)用法详解

    本文实例讲述了Android开发之自定义View(视图)用法.分享给大家供大家参考,具体如下: View类是Android的一个超类,这个类几乎包含了所有的屏幕类型.每一个View都有一个用于绘图的画布,这个画布可以进行任意扩展.在游戏开发中往往需要自定义视图(View),这个画布的功能更能满足我们在游戏开发中的需要.在Android中,任何一个View类都只需重写onDraw 方法来实现界面显示,自定义的视图可以是复杂的3D实现,也可以是非常简单的文本形式等. 为了实现自定义View,需要创建

  • 4种Android获取View宽高的方式

    有时我们会有基于这样的需求,当Activity创建时,需要获取某个View的宽高,然后进行相应的操作,但是我们在onCreate,onStart中获取View的大小,获取到的值都是0,只是由于View的绘制工程还未完成,和在onCreate中弹出Dialog或者PopupWindow会报一个Activity not running原理类似. 接下来就为大家介绍几种获取View宽高的方法: 第一种方式:重写Activity中的onWindowFocusChanged,当Activity获取到焦点的

  • Android视图控件架构分析之View、ViewGroup

    在Android中,视图控件大致被分为两类,即ViewGroup和View,ViewGroup控件作为父控件,包含并管理着子View,通过ViewGroup和View便形成了控件树,各个ViewGoup对象和View对象就是控件树中的节点.在控件树中,以树的深度来遍历查找对应的控件元素,同时,上层控件负责子控件的测量与绘制,并传递交互事件. Android控件树: AndroidUI界面架构图: 一.测量View的工具类:MeasureSpec 1.MeasureSpec包含了测量的模式和测量的

  • Android Service控件用法实例分析

    本文实例讲述了Android Service控件用法.分享给大家供大家参考,具体如下: 1.Service是一个应用程序的组件 2.Service没有图形化界面 3.用来处理耗时比较长的功能(下载.播放MP3) 4.更新ContentProvider.Intent以及系统的启动 Servcie不是一个单独的进程,不是一个线程 定义一个Service比较简单,只要继承Service类,实现其生命周期的方法即可.一个定义好的Service必须在AndroidManifest.xml文件中通过<ser

  • Android开发 -- 控件的显示与隐藏 setVisibility View.VISIBLE View.INVISIBLE View.GONE

    在Android中setVisibility作为显示和隐藏的属性,一般我们呢都是在代码中进行操作,例如: [code]<span style="white-space:pre">  </span>this.mItem.setVisibility(View.VISIBLE);[code] 其能够被设置的值有三个,分别是: View.VISIBLE    可见 View.INVISIBLE    不可见,但是它原来占用的位子还在 View.GONE  不可见,并且不

  • Android布局控件View ViewRootImpl WindowManagerService关系

    目录 1. View,ViewRoot和WindowManager简单介绍 1.1 View和ViewGroup 1.2 ViewRootImpl 1.3 WindowManager 2. ViewRootImpl的起源 2.1 ViewRootImpl创建时机 2.2 ViewRootImpl通知注册Window 3.ViewRootImpl与WindowManagerService的通信 3.1 WindowSession 3.2 IWindow 4. ViewRootImpl与View 1

  • Android设置控件阴影的三种方法

    本文实例为大家分享了Android设置控件阴影的方法,供大家参考,具体内容如下 第一种方式:elevation View的大小位置都是通过x,y确定的,而现在有了z轴的概念,而这个z值就是View的高度(elevation),而高度决定了阴影(shadow)的大小. View Elevation(视图高度) View的z值由两部分组成,elevation和translationZ(它们都是Android L新引入的属性). eleavation是静态的成员,translationZ是用来做动画.

  • Android AutoCompleteTextView控件基本用法示例

    本文实例讲述了Android AutoCompleteTextView控件基本用法.分享给大家供大家参考,具体如下: 当输入部分内容之后会有相关的建议,类似于百度提示信息 1.在布局文件中声明一个AutoCompleteTextView <AutoCompleteTextView android:id="@+id/autocomplete_country" android:layout_width="fill_parent" android:layout_he

  • Android基础控件(EditView、SeekBar等)的使用方法

    android提供了大量的UI控件,本文将介绍TextView.ImageView.Button.EditView.ProgressBar.SeekBar.ScrollView.WebView的使用方法.在介绍各种控件之前,先简单介绍android UI控件最基本的几种属性: id: id是控件唯一标识符,可通过**findViewById(R.id.*)**操作控件. layout_width:控件宽度,可设置为match_parent(充满父布局,即让父布局决定当前控件的宽度).wrap_c

  • Android列表控件Spinner简单用法示例

    本文实例讲述了Android列表控件Spinner简单用法.分享给大家供大家参考,具体如下: Android的Spinner控件用来显示列表项,类似于一组单选框RadioButton.这里介绍一下其简单用法: xml布局: <?xml version="1.0" encoding="utf-8"?> <LinaerLayout xmlns:android="http://schemas.android.com/apk/res/androi

  • Android WebView控件基本使用示例

    Android WebView用于在 android 中显示网页.可以从相同的应用程序或 URL 加载网页.它用于在 android 活动中显示在线内容. Android WebView 使用 webkit 引擎来显示网页. android.webkit.WebView 是 AbsoluteLayout 类的子类. Android WebView 类的loadUrl()和loadData()方法用于加载和显示网页. Android WebView 示例 让我们看看使用 Web 视图显示 baid

  • iOS应用开发中视图控件UIWindow的基本使用教程

    一.简单介绍 iPhone应用程序通常只有一个窗口,表示为一个UIWindow类的实例.应用程序在启动时(或者从nib文件进行装载)创建这个窗口,并往窗口中加入一或多个视图并显示出来.之后我们很少需要再次引用它.UIWindow对象是所有UIView的根,管理和协调的应用程序的显示.一般应用程序只有一个UIWindow对象,即使有多个UIWindow对象,也只有一个UIWindow可以接受到用户的触屏事件. 在IOS中,UIWindow对象并没有像windows应用程序中常见的关闭框或标题栏这样

随机推荐