文章出处:http://blog.youkuaiyun.com/scarthr/article/details/42063965
一 自定义控件的方式
自定义控件大体上分为3种方式:
1. 继承其它控件类(如继承EditView、Button等)
2. 组合方式。当前控件类从容器类继承,并将若干个控件添加到当前的容器中。
3. 绘制控件。也就是控件类从View继承,并在onDraw方法中从零绘制控件。例如:TextView。
二 继承方式的自定义控件(带命名空间验证)
今天我们主要来介绍一下第三种方式:继承实现自定义控件。
首先在values文件夹中创建attrs.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="iconPosition">
<enum name="left" value="0" />
<enum name="right" value="1" />
</attr>
<declare-styleable name="IconTextView">
<attr name="iconSrc" format="reference" />
<attr name="iconPosition" />
</declare-styleable>
</resources>
其中iconPosition是我们自定的属性,它有两个可选值,一个是left,一个是right。IconTextV是我们自定义控件的名字,它的iconSrc属性是引用类型,可以引用Android的资源文件,iconPosition就是我们上面所定义的属性。
关于format有一下几种可以选择:string, color, demension, integer, enum, reference, float, boolean, fraction, flag.具体什么含义,看名称大家应该都能猜出来吧。
接下来是我们的自定义控件类IconTextView:
package com.thr.imagetextview;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.TextView;
@SuppressLint({ "DrawAllocation", "Recycle" })
public class IconTextView extends TextView {
private int resourceId;
private int iconPosition;
private Bitmap bitmap;
public IconTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.IconTextView);
int count = typedArray.getIndexCount();
for (int i = 0; i < count; i++) {
// 遍历类型数组,取得我们设置的值
int attr = typedArray.getIndex(i);
switch (attr) {
// 图片资源属性
case R.styleable.IconTextView_iconSrc:
resourceId = typedArray.getResourceId(
R.styleable.IconTextView_iconSrc, 0);
if (resourceId > 0) {
bitmap = BitmapFactory.decodeResource(getResources(),
resourceId);
}
break;
// 自定义的位置属性
case R.styleable.IconTextView_iconPosition:
iconPosition = typedArray.getInt(
R.styleable.IconTextView_iconPosition, 0);
break;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
if (bitmap != null) {
Rect src = new Rect();
Rect target = new Rect();
int textHeight = (int) getTextSize();
// 获得bitmap图像的位置
src.left = 0;
src.top = 0;
src.right = bitmap.getWidth();
src.bottom = bitmap.getHeight();
// 计算实际图像在绘制图像中的位置
int left = 0;
if (iconPosition == 1) {
left = (int) (getPaint().measureText(getText().toString()) + 1);
}
target.left = left;
// 计算图像复制到目标区域的纵坐标,由于TextView中文本不是从最顶端开始,所以要计算顶部起始位置
target.top = (getMeasuredHeight() - textHeight) / 2;
target.bottom = target.top + textHeight;
// 计算图像的宽高比和文字宽高比一致
target.right = bitmap.getWidth() / bitmap.getHeight() * textHeight
+ left;
// 开始绘制图像
canvas.drawBitmap(bitmap, src, target, getPaint());
// 如果图标在左边,将TextView中文本向右移动图像宽度+1的距离
if (iconPosition == 0) {
canvas.translate(target.right + 1, 0);
}
}
super.onDraw(canvas);
}
}
注释已经写的很清楚,关键是
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.IconTextView);
是读取我们在attrs.xml中读取的属性值,并在onDraw中判断设置。
接下来是布局文件activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:thr="http://schemas.android.com/apk/res/com.thr.imagetextview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.thr.imagetextview.IconTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/darker_gray"
android:text="右面的图标"
thr:iconPosition="right"
thr:iconSrc="@drawable/ic_launcher" />
<com.thr.imagetextview.IconTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:background="@android:color/darker_gray"
android:text="左面的图标"
thr:iconPosition="left"
thr:iconSrc="@drawable/ic_launcher" />
</LinearLayout>
xmlns:thr="http://schemas.android.com/apk/res/com.thr.imagetextview"
是我们引入我们自定控件属性的命名空间,其中xmlns后面的thr可以随便定义方便下面自定义控件使用,最后的com.thr.imagetextview是我们的包名,设置正确才可以使我们后面可以提示调出所需要设置的属性值:
thr:iconPosition="right"
thr:iconSrc="@drawable/ic_launcher"
是我们设置自定义的属性,其中iconPosition只能设置为我们枚举的那两个类型,不可设置其他值。
最后是activity文件:
package com.thr.imagetextview;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
最后附上效果图: