【Android知识点精讲】(1)用继承和组合方式自定义控件

本文介绍如何通过继承TextView来自定义一个带有图标显示的TextView控件。该控件支持设置图标位置及图标资源,适用于Android应用开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章出处: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);	
	}
}

最后附上效果图:


源码下载


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值