android自定义控件(理论知识学习 +自定义属性的讲解)
View树和UI界面架构图
UI界面架构图:
view的测量:
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);将测量后的数据设置进去。
int specMode =MeasureSpec.getMode(measureSpec);
int specsize =MeasureSpec.getSize(measureSpec);view的绘制:
@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);
}
}
ViewGroup的测量:
android:layout_width="wrap_content"
android:layout_height="wrap_content"- 的时候,ViewGroup通过遍历其下面所有组 件来确定view的大小,子view的大小通过子view的onMesure来确定。
ViewGroup的绘制:
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为引用名称
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文件中设置自定义属性:
<?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>xmlns:app="http://schemas.android.com/apk/res-auto"为自定义属性的前缀,前缀为app,也就说后面自定义的属性设置必须以app开头,例如后面的:app:border_width="2dp"
app:border_color="@color/light"
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;
}- 通过context.obtainStyledAttributes来获取TypedArray对象,通过不同的方法来获取你设置在布局文件里面的属性,后面如果如下内容将获取到的资源设置到组件上面去:
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
android自定义控件(理论知识学习 +自定义属性的讲解)的更多相关文章
- Android权限管理知识学习记录
一.Android权限背景知识 在Android 6.0之前,所申请的权限只需要在AndroidManifest.xml列举就可以了,从而容易导致一些安全隐患,因此,在Android 6.0时,Goo ...
- android自定义控件(三) 增加内容 自定义属性 format详解
转自 http://www.gisall.com/html/35/160435-5369.html 1. reference:参考某一资源ID. (1)属性定义: <declare-stylea ...
- 一起来学习Android自定义控件1
概述 Android已经为我们提供了大量的View供我们使用,但是可能有时候这些组件不能满足我们的需求,这时候就需要自定义控件了.自定义控件对于初学者总是感觉是一种复杂的技术.因为里面涉及到的知识点会 ...
- Android自定义控件之自定义属性
前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...
- Android自定义控件之自定义属性(二)
前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...
- 掌握AI学习路上核心理论知识,你绝对不能错过这份最全资料包
人工智能成为当下科技发展的代表之一,持续受到了不少追捧,不管你是否是这一专业的学生或者职场人,学习并掌握一项新潮技能总是不会被同龄人淘汰的.我曾经问过别人.也被别人问过关于学习人工智能(AI)最好的方 ...
- TestNG学习-001-基础理论知识
此 文主要讲述用 TestNG 的基础理论知识,TestNG 的特定,编写测试过程三步骤,与 JUnit4+ 的差异,以此使亲对 TestNG 测试框架能够有一个简单的认知. 希望能对初学 TestN ...
- Android自定义控件及自定义属性
Android自定义控件及自定义属性 自定义控件 创建自定义控件 自定义一个类,继承View 继承View还是哪个类,取决于你要实现一个什么样的控件 如果你要实现的是一个线性布局的组合控件,就可以继承 ...
- Android初级教程理论知识(第四章内容提供器)
之前第三章理论知识写到过数据库.数据库是在程序内部自己访问自己.而内容提供器是访问别的程序数据的,即跨程序共享数据.对访问的数据也无非就是CRUD. 内容提供者 应用的数据库是不允许其他应用访问的 内 ...
随机推荐
- SharePoint 2016 自定义城市和区域字段
前言 最近有这么一个需求,就是用到中国的各种行政区,然后还是三级联动,就琢磨写这么一个字段.然后,觉得挺有意义的,写字段的过程也有点心得,就想到拿到博客里分享给大家,一起看看. 1. 创建字段的解决方 ...
- LaTeX入门教程(二)
LaTeX(LATEX,音译"拉泰赫")是一种基于ΤΕΧ的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在20世纪80年代初期开发,利用这种格式,即使使用 ...
- C# 关于委托的小例子
本例子是一个关于委托的小例子[猫叫,狗跳,人喊]. 委托是C#开发中一个非常重要的概念,理解起来也和常规的方法不同,但一旦理解清楚,就可以信手拈来,随处可用. 委托是对方法的抽象.它存储的就是一系列具 ...
- SQL Server存储过程同时返回分页结果集和总数
前言 好长时间没摸数据库了,周末在家写了个报表的存储过程,一时间对使用存储过程实现分页的同时并计算出记录总数不知道怎么更好的去实现.按照我们正常的业务逻辑,存储过程数据首先是分页,其次接受若干查询条件 ...
- docker网络解析
Docker概念和默认网络 什么是Docker网络呢?总的来说,网络中的容器们可以相互通信,网络外的又访问不了这些容器.具体来说,在一个网络中,它是一个容器的集合,在这个概念里面的一个容器,它会通过容 ...
- java基础练习 9
import java.util.Scanner; public class Ninth { /*取一个整数a从右端开始的4-7位.*/ public static void main(String[ ...
- 【.NET】Linq几样特点
LinQ 是 .net framework 3.5的技术,对集合的查询和操作十分方便,结合LinQ Provider ,可以更方便操作XML和SQL(或Entity). LinQ有6样常用的特性: 自 ...
- 带分页、过滤条件的DSL
"query": { "bool": { "must_not": { "match_phrase": { "p ...
- linuxlab下虚拟板与主机通信
- web service client端调用服务器接口
打开项目的web service client 其中wsdl URL http://www.51testing.com/html/55/67755-848510.html 去这里面查找一些公开的 ...