隨筆 - 312, 文章 - 14, 評(píng)論 - 1393, 引用 - 0
          數(shù)據(jù)加載中……

          新書內(nèi)容連載(1):自定義Android組件之帶圖像的TextView


          本文為原創(chuàng),如需轉(zhuǎn)載,請(qǐng)注明作者和出處,謝謝!

          本文為新書《Android/OPhone 開發(fā)完全講義》的內(nèi)容連載。《Android/OPhone開發(fā)完全講義》一書現(xiàn)已出版,敬請(qǐng)關(guān)注。

          購(gòu) 買:互動(dòng)網(wǎng)

          《Android/OPhone 開發(fā)完全講義》目錄

          源代碼下載


          在本例中要實(shí)現(xiàn)一個(gè)可以在文本前方添加一個(gè)圖像(可以是任何Android系統(tǒng)支持的圖像格式)的TextView組件。在編寫代碼之前,先看一下Android組件的配置代碼。

          <TextView android:id="@+id/textview1" android:layout_width="fill_parent"
                  android:layout_height
          ="wrap_content" android:text="textview1" />

          上面的代碼配置了一個(gè)標(biāo)準(zhǔn)的TextView組件。在這段代碼中主要有兩部分組成:組件標(biāo)簽(<TextView>)和標(biāo)簽屬性(android:idandroid:layout_width等)。需要注意的是,在所有的標(biāo)簽屬性前面都需要加了一個(gè)命名空間(android)。實(shí)際上,android命名空間的值是在Android系統(tǒng)中預(yù)定義的,所有Android系統(tǒng)原有的組件在配置時(shí)都需要在標(biāo)簽屬性前加android
              對(duì)于定制組件,可以有如下
          3種選擇。
              1.  仍然沿用android命名空間。
              2.  改用其他的命名空間。
              3.  不使用命名空間。

          雖然上面3種選擇從技術(shù)上說都沒有問題,但作者建議使用第2種方式(尤其是對(duì)外發(fā)布的組件),這是因?yàn)樵谑褂枚ㄖ平M件時(shí),可能需要指定相同名稱的屬性,在這種情況下,可以通過命名空間來區(qū)分這些屬性,例如,有兩個(gè)命名空間:androidmobile,這時(shí)可以在各自的命名空間下有相同名稱的屬性,如android:srcmobile:src。在本例中定義了一個(gè)mobile命名空間,因此,在配置本例實(shí)現(xiàn)的組件時(shí)需要在屬性前加mobile
              實(shí)現(xiàn)定制組件的一個(gè)重要環(huán)節(jié)就是讀取配置文件中相應(yīng)標(biāo)簽的屬性值,由于本例要實(shí)現(xiàn)的組件類需要從
          TextView類繼承,因此,只需要覆蓋TextView類中帶AttributeSet類型參數(shù)的構(gòu)造方法即可,該構(gòu)造方法的定義如下:

          public TextView(Context context, AttributeSet attrs)

          在構(gòu)造方法中可以通過AttributeSet接口的相應(yīng)getter方法來讀取指定的屬性值,如果在配置屬性時(shí)指定了命名空間,需要在使用getter方法獲得屬性值時(shí)指定這個(gè)命名空間,如果未指定命名空間,則將命名空間設(shè)為null即可。

          IconTextView是本例要編寫的組件類,該類從TextView繼承,在onDraw方法中將TextView中的文本后移,并在文本的前方添加了一個(gè)圖像,該圖像的資源ID通過mobile:iconSrc屬性來指定。IconTextView類的代碼如下:

          package net.blogjava.mobile.widget;

          import android.content.Context;
          import android.graphics.Bitmap;
          import android.graphics.BitmapFactory;
          import android.graphics.Canvas;
          import android.graphics.Rect;
          import android.util.AttributeSet;
          import android.widget.TextView;

          public class IconTextView extends TextView
          {
              
          //  命名空間的值
              private final String namespace = "http://net.blogjava.mobile";
              
          //  保存圖像資源ID的變量
              private int resourceId = 0;
              
          private Bitmap bitmap;
              
          public IconTextView(Context context, AttributeSet attrs)
              {
                  
          super(context, attrs);
                  
          //  getAttributeResourceValue方法用來獲得組件屬性的值,在本例中需要通過該方法的第1個(gè)參數(shù)指
                   
          //  定命名空間的值。該方法的第2個(gè)參數(shù)表示組件屬性名(不包括命名空間名稱),第3個(gè)參數(shù)表示默
                   
          //  認(rèn)值,也就是如果該屬性不存在,則返回第3個(gè)參數(shù)指定的值
                  resourceId = attrs.getAttributeResourceValue(namespace, "iconSrc"0);
                  
          if (resourceId > 0)
                        
          //  如果成功獲得圖像資源的ID,裝載這個(gè)圖像資源,并創(chuàng)建Bitmap對(duì)象
                      bitmap = BitmapFactory.decodeResource(getResources(), resourceId);
              }
              @Override
              
          protected void onDraw(Canvas canvas)
              {
                  
          if (bitmap != null)
                  {
                      
          //  從原圖上截取圖像的區(qū)域,在本例中為整個(gè)圖像
                      Rect src = new Rect();
                      
          //  將截取的圖像復(fù)制到bitmap上的目標(biāo)區(qū)域,在本例中與復(fù)制區(qū)域相同
                      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;
                      
          //  計(jì)算圖像復(fù)制到目標(biāo)區(qū)域的縱坐標(biāo)。由于TextView組件的文本內(nèi)容并不是
                        
          //  從最頂端開始繪制的,因此,需要重新計(jì)算繪制圖像的縱坐標(biāo)
                      target.top = (int) ((getMeasuredHeight() - getTextSize()) / 2+ 1;
                      target.bottom 
          = target.top + textHeight;
                      
          //  為了保證圖像不變形,需要根據(jù)圖像高度重新計(jì)算圖像的寬度
                      target.right = (int) (textHeight * (bitmap.getWidth() / (float) bitmap.getHeight()));
                      
          //  開始繪制圖像
                      canvas.drawBitmap(bitmap, src, target, getPaint());
                      
          //  將TextView中的文本向右移動(dòng)一定的距離(在本例中移動(dòng)了圖像寬度加2個(gè)象素點(diǎn)的位置)
                      canvas.translate(target.right + 20);
                  }
                  
          super.onDraw(canvas);
              }
          }

          在編寫上面代碼時(shí)需要注意如下3點(diǎn):
          1.  需要指定命名空間的值。該值將在<LinearLayout>標(biāo)簽的xmlns:mobile屬性中定義。
          2.  如果在配置組件的屬性時(shí)指定了命名空間,需要在AttributeSet 接口的相應(yīng)getter方法中的第1個(gè)參數(shù)指定命名空間的值,而第2個(gè)參數(shù)只需指定不帶命名空間的屬性名即可。
          3.  TextView類中的onDraw方法一定要在translate方法后面執(zhí)行,否則系統(tǒng)不會(huì)移動(dòng)TextView中的文本。

          下面在main.xml文件中配置了7個(gè)IconTextView組件,分別設(shè)置了不同的字體大小,同時(shí),文本前面的圖像也會(huì)隨著字體大小的變化而放大或縮小,配置代碼如下:

          <?xml version="1.0" encoding="utf-8"?>
          <!--  在下面的標(biāo)簽中通過xmlns:mobile屬性定義了一個(gè)命名空間  -->
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:mobile
          ="http://net.blogjava.mobile" android:orientation="vertical"
              android:layout_width
          ="fill_parent" android:layout_height="fill_parent">
               
          <!--  mobile:iconSrc是可選屬性,如果未設(shè)置該屬性,則IconTextView與TextView的效果相同  -->
              
          <!-- 由于IconTextView和Main類不在同一個(gè)包中,因此,需要顯式指定package -->
              
          <net.blogjava.mobile.view.IconTextView
                  android:layout_width
          ="fill_parent" android:layout_height="wrap_content"
                  android:text
          ="第一個(gè)笑臉" mobile:iconSrc="@drawable/small" />
              
          <net.blogjava.mobile.widget.IconTextView
                  android:layout_width
          ="fill_parent" android:layout_height="wrap_content"
                  android:text
          ="第二個(gè)笑臉" android:textSize="24dp" mobile:iconSrc="@drawable/small" />
              
          <net.blogjava.mobile.widget.IconTextView
                  android:layout_width
          ="fill_parent" android:layout_height="wrap_content"
                  android:text
          ="第三個(gè)笑臉" android:textSize="36dp" mobile:iconSrc="@drawable/small" />
              
          <net.blogjava.mobile.widget.IconTextView
                  android:layout_width
          ="fill_parent" android:layout_height="wrap_content"
                  android:text
          ="第四個(gè)笑臉" android:textSize="48dp" mobile:iconSrc="@drawable/small" />
              
          <net.blogjava.mobile.widget.IconTextView
                  android:layout_width
          ="fill_parent" android:layout_height="wrap_content"
                  android:text
          ="第五個(gè)笑臉" android:textSize="36dp" mobile:iconSrc="@drawable/small" />
              
          <net.blogjava.mobile.widget.IconTextView
                  android:layout_width
          ="fill_parent" android:layout_height="wrap_content"
                  android:text
          ="第六個(gè)笑臉" android:textSize="24dp" mobile:iconSrc="@drawable/small" />
              
          <net.blogjava.mobile.widget.IconTextView
                  android:layout_width
          ="fill_parent" android:layout_height="wrap_content"
                  android:text
          ="第七個(gè)笑臉" mobile:iconSrc="@drawable/small" />
          </LinearLayout> 

          運(yùn)行本實(shí)例后,將顯示如圖1所示的效果。

          注意:雖然很多人認(rèn)為組件的屬性必須以android命名空間開頭,該命名空間的值必須是http://schemas.android.com/apk/res/android。實(shí)際上,只是命名空間的值必須是http://schemas.android.com/apk/res/android而已,命名空間的名稱可以是任何值,如下面的代碼所示:

          <?xml version="1.0" encoding="utf-8"?>
          <!--  將android換成了abcd  -->
          <LinearLayout xmlns:abcd="http://schemas.android.com/apk/res/android"
              abcd:orientation
          ="vertical" abcd:layout_width="fill_parent"
              abcd:layout_height
          ="fill_parent">
               
          </LinearLayout>




          Android開發(fā)完全講義(第2版)(本書版權(quán)已輸出到臺(tái)灣)

          http://product.dangdang.com/product.aspx?product_id=22741502



          Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


          新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

          posted on 2010-04-29 09:09 銀河使者 閱讀(3199) 評(píng)論(2)  編輯  收藏 所屬分類: 原創(chuàng)移動(dòng)(mobile)GoogleAndroid/OPhone

          評(píng)論

          # re: 新書內(nèi)容連載(1):自定義Android組件之帶圖像的TextView  回復(fù)  更多評(píng)論   

          按開始的空間上空間啊
          2010-04-30 12:07 | 樂蜂網(wǎng)

          # re: 新書內(nèi)容連載(1):自定義Android組件之帶圖像的TextView  回復(fù)  更多評(píng)論   

          樓主比較細(xì)心,注釋這么詳細(xì)。
          學(xué)習(xí)了。
          2010-05-03 00:57 | 爪天下
          主站蜘蛛池模板: 玛纳斯县| 宁波市| 湘阴县| 汉阴县| 白玉县| 肇源县| 梅河口市| 南陵县| 福海县| 汝阳县| 乌什县| 长春市| 霍林郭勒市| 龙里县| 霸州市| 出国| 满洲里市| 瑞金市| 英吉沙县| 广宗县| 铜山县| 嘉祥县| 襄汾县| 林甸县| 盐池县| 隆林| 绿春县| 竹北市| 新龙县| 湾仔区| 玉溪市| 桂平市| 冕宁县| 抚松县| 资中县| 张家口市| 孟州市| 扶余县| 清丰县| 榕江县| 千阳县|