文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2022-06-06 08:31

关注

在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));
  }
  
  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;
  }
  
  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;

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

您可能感兴趣的文章:4种Android获取View宽高的方式Android获取屏幕或View宽度和高度的方法Android ImageView 固定宽高比例的实现方法Android 获得View宽高的几种方式总结Android中RecyclerView的item宽高问题详解Android开发之自定义View(视图)用法详解Android App中自定义View视图的实例教程Android视图的绘制流程(上) View的测量Android开发中获取View视图宽与高的常用方法小结


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯