自定义Android组件之带图像的TextV…

http://www.blogjava.net/nokiaguy/archive/2010/04/archive/2010/04/13/318124.html

 本文为新书《Android/OPhone开发完全讲义》的内容连载。《Android/OPhone开发完全讲义》一书将在近期出版,敬请关注。

    在本例中要实现一个可以在文本前方添加一个图像(可以是任何Android系统支持的图像格式)的TextView组件。在编写代码之前,先看一下Android组件的配置代码。

  1. <TextView android:id="@+id/textview1" android:layout_width="fill_parent"
  2.         android:layout_height="wrap_content" android:text="textview1"
  3. />
复制代码

上面的代码配置了一个标准的TextView组件。在这段代码中主要有两部分组成:组件标签(<TextView>)和标签属性(android:idandroid:layout_width等)。需要注意的是,在所有的标签属性前面都需要加了一个命名空间(android)。实际上,android命名空间的值是在Android系统中预定义的,所有Android系统原有的组件在配置时都需要在标签属性前加android
    对于定制组件,可以有如下
3种选择。
    1.  仍然沿用android命名空间。
    2.  改用其他的命名空间。
    3.  不使用命名空间。

虽然上面3种选择从技术上说都没有问题,但作者建议使用第2种方式(尤其是对外发布的组件),这是因为在使用定制组件时,可能需要指定相同名称的属性,在这种情况下,可以通过命名空间来区分这些属性,例如,有两个命名空间:androidmobile,这时可以在各自的命名空间下有相同名称的属性,如android:srcmobile:src。在本例中定义了一个mobile命名空间,因此,在配置本例实现的组件时需要在属性前加mobile
    实现定制组件的一个重要环节就是读取配置文件中相应标签的属性值,由于本例要实现的组件类需要从
TextView类继承,因此,只需要覆盖TextView类中带AttributeSet类型参数的构造方法即可,该构造方法的定义如下:
[code=java]public TextView(Context context, AttributeSet attrs)[/code]在构造方法中可以通过AttributeSet接口的相应getter方法来读取指定的属性值,如果在配置属性时指定了命名空间,需要在使用getter方法获得属性值时指定这个命名空间,如果未指定命名空间,则将命名空间设为null即可。
IconTextView是本例要编写的组件类,该类从TextView继承,在onDraw方法中将TextView中的文本后移,并在文本的前方添加了一个图像,该图像的资源ID通过mobile:iconSrc属性来指定。IconTextView类的代码如下:

  1. package net.blogjava.mobile.widget;

  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.graphics.Canvas;
  6. import android.graphics.Rect;
  7. import android.util.AttributeSet;
  8. import android.widget.TextView;

  9. public
  10. class IconTextView extends TextView
  11. {
  12.     //  

  13. 空间的值
  14.     private final String namespace =  "http://net.blogjava.mobile";
  15.     //  

  16. 图像资源ID的变量
  17.     private int resourceId =0;
  18.     private Bitmap bitmap;
  19.     public IconTextView(Context context, AttributeSet attrs)
  20.     {
  21.         super(context, attrs);
  22.         //  getAttributeResourceValue 方

  23. 用来获得组件属性的值,在本例中需要通过该方法的第1个参数指
  24.          //  定命名空间的值。该方法的第2个参数表示组件属性名
  25. (不
  26. 包括命名空间名称),第3个参数表示默
  27.          //  认值,也就是如果该属性不存在,则返回第3个参数指定的值
  28.         resourceId = attrs.getAttributeResourceValue(namespace, "iconSrc", 0);
  29.         if (resourceId > 0)
  30.               //  

  31. 成功获得图像资源的ID,装载这个图像资源,并创建Bitmap对象
  32.             bitmap = BitmapFactory.decodeResource(getResources(), resourceId);
  33.        }
  34. @Override
  35. protected void onDraw(Canvas canvas)
  36. {
  37.         if (bitmap != null)
  38.         {
  39.             //  

  40. 图上截取图像的区域,在本例中为整个图像
  41.             Rect src = new Rect();
  42.             //  

  43. 取的图像复制到bitmap上的目标区域,在本例中与复制区域相同
  44.             Rect target = new Rect();
  45.             src.left = 0;
  46.             src.top = 0;
  47.             src.right = bitmap.getWidth();
  48.             src.bottom = bitmap.getHeight();
  49.             int textHeight = (int) getTextSize();
  50.             target.left = 0;
  51.             //  

  52. 图像复制到目标区域的纵坐标。由于 TextView组件的文本内容并不是
  53.               //  从最顶端开始绘制的,因此,需

  54. 重新计算绘制图像的纵坐标
  55.             target.top = (int) ((getMeasuredHeight() - getTextSize()) / 2) + 1;
  56.             target.bottom = target.top + textHeight;
  57.             //  

  58. 保证图像不变形,需要根据图像高度重新计算图像的宽度
  59.             target.right = (int) (textHeight * (bitmap.getWidth() / (float) bitmap.getHeight()));
  60.             //  

  61. 绘制图像
  62.             canvas.drawBitmap(bitmap, src, target, getPaint());
  63.             //  
  64. TextView
  65. 中的文本向右移动一定的距离(在本例中移动了图像宽度加2个象素点的位置)
  66.             canvas.translate(target.right + 2, 0);
  67.         }
  68.         super.onDraw(canvas);
  69.     }
  70. }
复制代码


在编写上面代码时需要注意如下3点:
1.  需要指定命名空间的值。该值将在<LinearLayout>标签的xmlns:mobile属性中定义。
2.  如果在配置组件的属性时指定了命名空间,需要在AttributeSet 接口的相应getter方法中的第1个参数指定命名空间的值,而第2个参数只需指定不带命名空间的属性名即可。
3.  TextView类中的onDraw方法一定要在translate方法后面执行,否则系统不会移动TextView中的文本。

下面在main.xml文件中配置了7IconTextView组件,分别设置了不同的字体大小,同时,文本前面的图像也会随着字体大小的变化而放大或缩小,配置代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!--  

  3. 面的标签中通过xmlns:mobile属性定义了一个命名空间  -->
  4. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  5.     xmlns:mobile="http://net.blogjava.mobile" android:orientation="vertical"
  6.     android:layout_width="fill_parent" android:layout_height="fill_parent">
  7. <!--  mobile:iconSrc

  8. 可选属性,如果未设置该属性,则IconTextView与TextView的效果相同  -->
  9. <!-- 由于
  10. IconTextView
  11. 和Main类不在同一个包中,因此,需要显式指定package -->
  12. <net.blogjava.mobile.view.IconTextView
  13.         android:layout_width="fill_parent" android:layout_height="wrap_content"
  14.         android:text=" 第

  15. 个笑脸" mobile:iconSrc="@drawable/small"
  16. />
  17. <net.blogjava.mobile.widget.IconTextView
  18.         android:layout_width="fill_parent" android:layout_height="wrap_content"
  19.         android:text=" 第

  20. 个笑脸" android:textSize="24dp" mobile:iconSrc="@drawable/small"
  21. />
  22. <net.blogjava.mobile.widget.IconTextView
  23.         android:layout_width="fill_parent" android:layout_height="wrap_content"
  24.         android:text=" 第

  25. 个笑脸" android:textSize="36dp" mobile:iconSrc="@drawable/small"
  26. />
  27. <net.blogjava.mobile.widget.IconTextView
  28.         android:layout_width="fill_parent" android:layout_height="wrap_content"
  29.         android:text=" 第

  30. 个笑脸" android:textSize="48dp" mobile:iconSrc="@drawable/small"
  31. />
  32. <net.blogjava.mobile.widget.IconTextView
  33.         android:layout_width="fill_parent" android:layout_height="wrap_content"
  34.         android:text=" 第

  35. 个笑脸" android:textSize="36dp" mobile:iconSrc="@drawable/small"
  36. />
  37. <net.blogjava.mobile.widget.IconTextView
  38.         android:layout_width="fill_parent" android:layout_height="wrap_content"
  39.         android:text=" 第

  40. 个笑脸" android:textSize="24dp" mobile:iconSrc="@drawable/small"
  41. />
  42. <net.blogjava.mobile.widget.IconTextView
  43.         android:layout_width="fill_parent" android:layout_height="wrap_content"
  44.         android:text=" 第

  45. 个笑脸" mobile:iconSrc="@drawable/small"
  46. />
  47. </LinearLayout>
复制代码


运行实例后,将显示如图1所示的效果。

注意:虽然很多人认为组件的属性必须以android命名空间开头,该命名空间的值必须是http://schemas.android.com/apk/res/android。实际上,只是命名空间的值必须是http://schemas.android.com/apk/res/android
而已,命名空间的名称可以是任何值,如下面的代码所示:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!--  
  3. android
  4. 换成了abcd  -->
  5. <LinearLayout xmlns:abcd="http://schemas.android.com/apk/res/android"
  6.     abcd:orientation="vertical" abcd:layout_width="fill_parent"
  7.     abcd:layout_height="fill_parent">

  8. </LinearLayout>
复制代码

乐博Android手机客户端(新浪微博)发布

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值