欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-29 20:03:22
在android中,视图控件大致被分为两类,即viewgroup和view,viewgroup控件作为父控件,包含并管理着子view,通过viewgroup和view便形成...

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

android控件树:

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

androidui界面架构图:

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

一.测量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():设置触摸交互事件。

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