Android突击:定制控件
继承,组合和绘制。
在安卓里,好多控件都是TextView的子控件容器类:布局类,其实也是个控件,不过他可以包含其他的控件。
组合方式构建:
1,在控件类中定义TextView和EditText类中
2,在控件类的构造方法读取控件的属性
3,根据控件属性值设置TextView和EditText值及二者的相对位置。
这个例子是从线性布局中继承。这个是不用带命名空间的。
所有的控件类都会有context,所有的属性名值即放在res目录下的资源就是用AttributeSet里。
public class LabelEditText extends LinearLayout{private TextView textView;private String labelText;private int labelFontSize;private String labelPosition;public LabelEditText(Context context, AttributeSet attrs){super(context, attrs);// 读取labelText属性的资源IDint resourceId = attrs.getAttributeResourceValue(null, "labelText", 0);// 未获得资源ID,继续读取属性值if (resourceId == 0)labelText = attrs.getAttributeValue(null, "labelText");// 从资源文件中获得labelText属性的值elselabelText = getResources().getString(resourceId);// 如果按两种方式都未获得labelTex属性的值,表示未设置该属性,抛出异常if (labelText == null){throw new RuntimeException("必须设置labelText属性.");}// 获得labelFontSize属性的资源IDresourceId = attrs.getAttributeResourceValue(null, "labelFontSize", 0);// 继续读取labelFontSize属性的值,如果未设置该属性,将属性值设为14if (resourceId == 0)labelFontSize = attrs.getAttributeIntValue(null, "labelFontSize",14);// 从资源文件中获得labelFontSize属性的值elselabelFontSize = getResources().getInteger(resourceId);// 获得labelPosition属性的资源IDresourceId = attrs.getAttributeResourceValue(null, "labelPosition", 0);// 继续读取labelPosition属性的值if (resourceId == 0)labelPosition = attrs.getAttributeValue(null, "labelPosition");// 从资源文件中获得labelPosition属性的值elselabelPosition = getResources().getString(resourceId);// 如果未设置labelPosition属性值,将该属性值设为leftif (labelPosition == null)labelPosition = "left";String infService = Context.LAYOUT_INFLATER_SERVICE;LayoutInflater li;// 获得LAYOUT_INFLATER_SERVICE服务li = (LayoutInflater) context.getSystemService(infService);LinearLayout linearLayout = null;// 根据labelPosition属性的值装载不同的布局文件if("left".equals(labelPosition))linearLayout = (LinearLayout)li.inflate(R.layout.labeledittext_horizontal, this);else if("top".equals(labelPosition))linearLayout = (LinearLayout)li.inflate(R.layout.labeledittext_vertical, this);elsethrow new RuntimeException("labelPosition属性的值只能是left或top.");// 下面的代码从相应的布局文件中获得了TextView对象,并根据LabelTextView的属性值设置TextView的属性textView = (TextView) findViewById(R.id.textview);//textView.setTextSize((float)labelFontSize);textView.setTextSize(labelFontSize);textView.setText(labelText);}}
继承方式构建:
需要使用命名空间。
xmlns:android="http://schemas.android.com/apk/res/android" 这个是系统的命名空间值,在R类里。
public class IconTextView extends TextView{// 命名空间的值private final String namespace = "http://cn.eoe.icon.textview";// 图像资源IDprivate int resourceId = 0;private Bitmap bitmap;public IconTextView(Context context, AttributeSet attrs){super(context, attrs);resourceId = attrs.getAttributeResourceValue(namespace, "iconSrc", 0);if (resourceId > 0)bitmap = BitmapFactory.decodeResource(getResources(), resourceId);}@Overrideprotected void onDraw(Canvas canvas){if (bitmap != null){// 从原图上截取图像的区域,在本例中为整个图像Rect src = new Rect();// 将截取的图像复制到bitmap上的目标区域,在本例中与复制区域相同Rect target = new Rect();src.left = 0;src.top = 0;src.right = bitmap.getWidth();src.bottom = bitmap.getHeight();int textHeight = (int) getTextSize();target.left = 0;// 计算图像复制到目录区域的纵坐标。由于TextView中文本内容并不是从最顶端开始绘制的,因此,需要重新计算绘制图像的纵坐标target.top = (int) ((getMeasuredHeight() - getTextSize()) / 2) + 1;target.bottom = target.top + textHeight;// 为了保证图像不变形,需要根据图像高度重新计算图像的宽度target.right = (int) (textHeight * (bitmap.getWidth() / (float) bitmap.getHeight()));// 开始绘制图像canvas.drawBitmap(bitmap, src, target, getPaint());// 将TextView中的文本向右移动一定的距离(在本例中移动了图像宽度加2个象素点的位置)canvas.translate(target.right + 2, 0);}super.onDraw(canvas);}}
安卓是不区分资源的ID类型的。
浙公网安备 33010602011771号