View树和UI界面架构图

 

UI界面架构图:

android视图最外层是一个window对象
phoneWindow来实现。
phoneWindow将一个decorView作为整个布局的根view.
屏幕分为TitleView和ContentView.
ContentView的根布局为framelayout.
 

view的测量:

view的测量通过onMesure()来进行的:
onMesure用来确定视图大小和位置。
MesureSpec用来帮助我们测量view.
  1. Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // TODO Auto-generated method stub
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);调用setMeasureredDimension(int width,int height);将测量后的数据设置进去。

 
    测量模式:
    EXACTLY
    当视图控件精确确定大小的时候,系统使用该模式,精确模式。默认支持这种模式。
    
    AT_MOST
    控件的layout_width和layout_height设置为wrap_layout的时候,控件尺寸不超过父控件大小。
    
    UNSPECIFIED
    自定义控件的使用使用,不指定view大小。
 
如何获取测量模式和测量大小:
  1. int specMode =MeasureSpec.getMode(measureSpec);
    int specsize =MeasureSpec.getSize(measureSpec);
  2. view的绘制:

View的绘制主要是在onDraw中通过canvas(画布)和paint(画笔)来进行绘制操作。
如下是绘制圆形ImageView里面onDraw方法里面的设置:
  1. @Override
    protected void onDraw(Canvas canvas) {
    if (getDrawable() == null) {
    return;
    }
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius,
    mBitmapPaint);
    if (mBorderWidth != 0) {
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius,
    mBorderPaint);
    }
    }
canvas当你重写onDraw方法的时候由系统提供,通过这个对象来进行绘制操作。
 

ViewGroup的测量:

ViewGroup的宽高设置为:
  1. android:layout_width="wrap_content"
    android:layout_height="wrap_content"
  2. 的时候,ViewGroup通过遍历其下面所有组  件来确定view的大小,子view的大小通过子view的onMesure来确定。

ViewGroup的绘制:

几乎不调用其onDraw方法进行绘制,大部分都是调用dispatchDraw()来绘制子view,遍历所有子view并进行绘制。
 
自定义组件几个比较重要的方法:
onFinishInflate()从xml加载组建后进行回调。
onSizeChanged()组件大小发生变化的时候回调。
onMesure()回调进行组件大小的测量。
onLayout()确定组件的显示位置。
onTouchEvent()确实视图组件事件的分发处理。
 
自定义组件属性以及在代码中如何获取已经给组件设置相关属性内容改变组件显示效果?
例如前面说到的自定义圆形Imageview,我们在attrs.xml文件定义如下:
首先贴出CircleImageView的源码,后面再讲解:
  1. package com.soyoungboy.base.view;
    import com.soyoungboy.base.R;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.RectF;
    import android.graphics.Shader;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.ColorDrawable;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.widget.ImageView;
    /**
    *
    * 圆形Imageview
    *
    */
    public class CircleImageView extends ImageView {
    private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    private static final int COLORDRAWABLE_DIMENSION = 1;
    private static final int DEFAULT_BORDER_WIDTH = 0;
    private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
    private final RectF mDrawableRect = new RectF();
    private final RectF mBorderRect = new RectF();
    private final Matrix mShaderMatrix = new Matrix();
    private final Paint mBitmapPaint = new Paint();
    private final Paint mBorderPaint = new Paint();
    private int mBorderColor = DEFAULT_BORDER_COLOR;
    private int mBorderWidth = DEFAULT_BORDER_WIDTH;
    private Bitmap mBitmap;
    private BitmapShader mBitmapShader;
    private int mBitmapWidth;
    private int mBitmapHeight;
    private float mDrawableRadius;
    private float mBorderRadius;
    private boolean mReady;
    private boolean mSetupPending;
    public CircleImageView(Context context) {
    super(context);
    }
    public CircleImageView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }
    public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    super.setScaleType(SCALE_TYPE);
    TypedArray a = context.obtainStyledAttributes(attrs,
    R.styleable.CircleImageView, defStyle, 0);
    mBorderWidth = a.getDimensionPixelSize(
    R.styleable.CircleImageView_CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
    mBorderColor = a.getColor(R.styleable.CircleImageView_CircleImageView_border_color,
    DEFAULT_BORDER_COLOR);
    a.recycle();
    mReady = true;
    if (mSetupPending) {
    setup();
    mSetupPending = false;
    }
    }
    @Override
    public ScaleType getScaleType() {
    return SCALE_TYPE;
    }
    @Override
    public void setScaleType(ScaleType scaleType) {
    if (scaleType != SCALE_TYPE) {
    throw new IllegalArgumentException(String.format(
    "ScaleType %s not supported.", scaleType));
    }
    }
    @Override
    protected void onDraw(Canvas canvas) {
    if (getDrawable() == null) {
    return;
    }
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius,
    mBitmapPaint);
    if (mBorderWidth != 0) {
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius,
    mBorderPaint);
    }
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    setup();
    }
    public int getBorderColor() {
    return mBorderColor;
    }
    public void setBorderColor(int borderColor) {
    if (borderColor == mBorderColor) {
    return;
    }
    mBorderColor = borderColor;
    mBorderPaint.setColor(mBorderColor);
    invalidate();
    }
    public int getBorderWidth() {
    return mBorderWidth;
    }
    public void setBorderWidth(int borderWidth) {
    if (borderWidth == mBorderWidth) {
    return;
    }
    mBorderWidth = borderWidth;
    setup();
    }
    @Override
    public void setImageBitmap(Bitmap bm) {
    super.setImageBitmap(bm);
    mBitmap = bm;
    setup();
    }
    @Override
    public void setImageDrawable(Drawable drawable) {
    super.setImageDrawable(drawable);
    mBitmap = getBitmapFromDrawable(drawable);
    setup();
    }
    @Override
    public void setImageResource(int resId) {
    super.setImageResource(resId);
    mBitmap = getBitmapFromDrawable(getDrawable());
    setup();
    }
    private Bitmap getBitmapFromDrawable(Drawable drawable) {
    if (drawable == null) {
    return null;
    }
    if (drawable instanceof BitmapDrawable) {
    return ((BitmapDrawable) drawable).getBitmap();
    }
    try {
    Bitmap bitmap;
    if (drawable instanceof ColorDrawable) {
    bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION,
    COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
    } else {
    bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
    drawable.getIntrinsicHeight(), BITMAP_CONFIG);
    }
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    return bitmap;
    } catch (OutOfMemoryError e) {
    return null;
    }
    }
    private void setup() {
    if (!mReady) {
    mSetupPending = true;
    return;
    }
    if (mBitmap == null) {
    return;
    }
    mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,
    Shader.TileMode.CLAMP);
    mBitmapPaint.setAntiAlias(true);
    mBitmapPaint.setShader(mBitmapShader);
    mBorderPaint.setStyle(Paint.Style.STROKE);
    mBorderPaint.setAntiAlias(true);
    mBorderPaint.setColor(mBorderColor);
    mBorderPaint.setStrokeWidth(mBorderWidth);
    mBitmapHeight = mBitmap.getHeight();
    mBitmapWidth = mBitmap.getWidth();
    mBorderRect.set(0, 0, getWidth(), getHeight());
    mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2,
    (mBorderRect.width() - mBorderWidth) / 2);
    mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width()
    - mBorderWidth, mBorderRect.height() - mBorderWidth);
    mDrawableRadius = Math.min(mDrawableRect.height() / 2,
    mDrawableRect.width() / 2);
    updateShaderMatrix();
    invalidate();
    }
    private void updateShaderMatrix() {
    float scale;
    float dx = 0;
    float dy = 0;
    mShaderMatrix.set(null);
    if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width()
    * mBitmapHeight) {
    scale = mDrawableRect.height() / (float) mBitmapHeight;
    dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
    } else {
    scale = mDrawableRect.width() / (float) mBitmapWidth;
    dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
    }
    mShaderMatrix.setScale(scale, scale);
    mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth,
    (int) (dy + 0.5f) + mBorderWidth);
    mBitmapShader.setLocalMatrix(mShaderMatrix);
    }
    }

    <declare-styleable name="CircleImageView">
    <attr name="border_width" format="dimension" />
    <attr name="border_color" format="color" />
    </declare-styleable>

    CircleImageView为引用名称

format指定属性的类型
 
 1.reference:参考指定Theme中资源ID,这个类型意思就是你传的值可以是引用资源
2.string:字符串,如果你想别人既能直接写值也可以用类似"@string/test"引用资源的方式,可以写成format="string|reference"
3.Color:颜色
4.boolean:布尔值
5.dimension:尺寸值
6.float:浮点型
7.integer:整型
8.fraction:百分数
9.enum:枚举 ,如果你提供的属性只能让别人选择,不能随便传入,就可以写成这样
如何在xml文件中设置自定义属性:
  1. <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:padding="@dimen/base_padding"
    android:background="@color/light">
    <de.hdodenhof.circleimageview.CircleImageView
    android:layout_width="160dp"
    android:layout_height="160dp"
    android:layout_centerInParent="true"
    android:src="@drawable/demo"
    app:border_width="2dp"
    app:border_color="@color/dark" />
    </RelativeLayout>
    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:padding="@dimen/base_padding"
    android:background="@color/dark">
    <de.hdodenhof.circleimageview.CircleImageView
    android:layout_width="160dp"
    android:layout_height="160dp"
    android:layout_centerInParent="true"
    android:src="@drawable/lena"
    app:border_width="2dp"
    app:border_color="@color/light" />
    </RelativeLayout>
    </LinearLayout>
  2. xmlns:app="http://schemas.android.com/apk/res-auto"自定义属性的前缀,前缀为app,也就说后面自定义的属性设置必须以app开头,例如后面的:
  3. app:border_width="2dp"
    app:border_color="@color/light"
那么如何在代码中获取属性并设置组件属性呢?我们看下CircleImageView组件的构造方法:
  1. public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    super.setScaleType(SCALE_TYPE);
    TypedArray a = context.obtainStyledAttributes(attrs,
    R.styleable.CircleImageView, defStyle, 0);
    mBorderWidth = a.getDimensionPixelSize(
    R.styleable.CircleImageView_CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
    mBorderColor = a.getColor(R.styleable.CircleImageView_CircleImageView_border_color,
    DEFAULT_BORDER_COLOR);
    a.recycle();
    mReady = true;
    if (mSetupPending) {
    setup();
    mSetupPending = false;
    }
  2. 通过context.obtainStyledAttributes来获取TypedArray对象,通过不同的方法来获取你设置在布局文件里面的属性,后面如果如下内容将获取到的资源设置到组件上面去:
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
注意获取完属性数据后,通过recycle来回收资源。其实设置自定义属性主要在设置属性参数类型和在代码中获取属性以及在组件上根据获取的属性内容设置到组件上面,并影响在组件上的显示效果。
 
自定义控件之组合控件:
 
组合控件有两种实现方式:
1,通过在xml文件中定义布局的大概样子,通过继承viewGroup相关控件,然后写上一些回调方法,通过java代码来控制组件内容显示,隐藏,内容变化,颜色,状态的变化等....,这种方式比较简便,容易实现,所以经常使用在普通的组合控件使用上,比如对一些项目中常用布局的组装。前边也有讲到比较简单的实现:http://www.cnblogs.com/androidsuperman/p/4580523.html
 
2,就是在java代码里面实现,其实就是xml里面对组件的形式通过set的形式添加到组件上那种。很少这种去实现。
 
对于组件的点击等事件处理,一般都是些接口,接口里面方法指向对应组件的系统点击等事件来处理。

android自定义控件(理论知识学习 +自定义属性的讲解)的更多相关文章

  1. Android权限管理知识学习记录

    一.Android权限背景知识 在Android 6.0之前,所申请的权限只需要在AndroidManifest.xml列举就可以了,从而容易导致一些安全隐患,因此,在Android 6.0时,Goo ...

  2. android自定义控件(三) 增加内容 自定义属性 format详解

    转自 http://www.gisall.com/html/35/160435-5369.html 1. reference:参考某一资源ID. (1)属性定义: <declare-stylea ...

  3. 一起来学习Android自定义控件1

    概述 Android已经为我们提供了大量的View供我们使用,但是可能有时候这些组件不能满足我们的需求,这时候就需要自定义控件了.自定义控件对于初学者总是感觉是一种复杂的技术.因为里面涉及到的知识点会 ...

  4. Android自定义控件之自定义属性

    前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...

  5. Android自定义控件之自定义属性(二)

    前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...

  6. 掌握AI学习路上核心理论知识,你绝对不能错过这份最全资料包

    人工智能成为当下科技发展的代表之一,持续受到了不少追捧,不管你是否是这一专业的学生或者职场人,学习并掌握一项新潮技能总是不会被同龄人淘汰的.我曾经问过别人.也被别人问过关于学习人工智能(AI)最好的方 ...

  7. TestNG学习-001-基础理论知识

    此 文主要讲述用 TestNG 的基础理论知识,TestNG 的特定,编写测试过程三步骤,与 JUnit4+ 的差异,以此使亲对 TestNG 测试框架能够有一个简单的认知. 希望能对初学 TestN ...

  8. Android自定义控件及自定义属性

    Android自定义控件及自定义属性 自定义控件 创建自定义控件 自定义一个类,继承View 继承View还是哪个类,取决于你要实现一个什么样的控件 如果你要实现的是一个线性布局的组合控件,就可以继承 ...

  9. Android初级教程理论知识(第四章内容提供器)

    之前第三章理论知识写到过数据库.数据库是在程序内部自己访问自己.而内容提供器是访问别的程序数据的,即跨程序共享数据.对访问的数据也无非就是CRUD. 内容提供者 应用的数据库是不允许其他应用访问的 内 ...

随机推荐

  1. 结构-行为-样式-JqueryUI拖放使用实例(全)

    最近工作中有个需要是动态配置页面,想到之前公司有做过类似的,用的是JqueryUi,所以就看了下它的Api.下面就是我做的小Demo,想用的同学可以参考: Html: <div class=&q ...

  2. Linux环境下安装Redis步骤即问题解决

    第一步:将安装包在window平台上解压后拷贝到Linux机器的/usr/soft目录下,并且为文件夹和文件赋予最高权限,chmod+x *: 第二步:进入到redis-3.2.6目录下,执行make ...

  3. JMS(java消息服务)整合Spring项目案例

    转载自云栖社区 摘要: Sprng-jms消息服务小项目 所需的包: spring的基础包 spring-jms-xx包 spring-message–xx包 commons-collection-x ...

  4. Leetcode-34-Search for a Range-(Medium)

    这道题借助二分查找算法来查找目标值的index 然后向前和向后分别搜索起始边界 注意开始排除异常值优化速度 #!/usr/local/bin/python3 # -*- coding: utf-8 - ...

  5. JVM性能调优监控工具jps、jstack、jmap、jhat、jstat使用详解

    Jmap是一个可以输出所有内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本.打印出某个java进程(使用pid)内存内的,所有'对象'的情况(如:产生那些对象,及其数量). 使用方法 ...

  6. linux操作系统简单使用

    文章将包含几个内容: linux简介 linux操作系统的安装简述 linux操作系统的磁盘文件结构 linux操作系统中的文件操作 linux中的用户管理 网络配置管理 常用系统管理指令 linux ...

  7. FileOutputStream flush()

    FileOutputStream 继承 OutputStream ,flush方法查看源码方法体为空,所以flush没起到清除缓存的作用 改用BufferedOutputStream再调用flush( ...

  8. MyEclipse 代码提示设置

    打开 Eclipse -> Window -> Perferences -> Java -> Editor -> Content Assist,在右边最下面一栏找到 au ...

  9. USACO 3.3 Riding the Fences

    Riding the Fences Farmer John owns a large number of fences that must be repaired annually. He trave ...

  10. [Q]pdfFactory打印机纸张方向设置为横向

    不推荐更改pdfFactory打印机默认纸张方向(默认为横向),更改后可能导致不必要的麻烦(pdfFactory要求所定义的纸张方向与实际的纸张方向需一致,因此若更改为横向,则纸张宽度的定义需大于纸张 ...