Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Drawable 那些不為人知的高效用法分享

Android Drawable 那些不為人知的高效用法分享

編輯:關於Android編程

1、概述

Drawable在我們平時的開發中,基本都會用到,而且給大家非常的有用。那麼什麼是Drawable呢?能夠在canvas上繪制的一個玩意,而且相比於View,並不需要去考慮measure、layout,僅僅只要去考慮如何draw(canavs)。當然了,對於Drawable傳統的用法,大家肯定不陌生 ,今天主要給大家帶來以下幾個Drawable的用法:

1、自定義Drawable,相比View來說,Drawable屬於輕量級的、使用也很簡單。以後自定義實現一個效果的時候,可以改變View first的思想,嘗試下Drawable first。

2、自定義狀態,相信大家對於State Drawable都不陌生,但是有沒有嘗試過去自定義一個狀態呢?

3、利用Drawable提升我們的UI Perfermance , 如何利用Drawable去提升我們的UI的性能。

 

2、Drawable基本概念

一般情況下,除了直接使用放在Drawable下的圖片,其實的Drawable的用法都和xml相關,我們可以使用shape、layer-list等標簽繪制一些背景,還可以通過selector標簽定義View的狀態的效果等。當然了基本每個標簽都對應於一個真正的實體類,關系如下:(圖片來自:Cyril Mottier :master_android_drawables)

\

常見的用法這裡就不舉例了,下面開始看本文的重點。

2、自定義Drawable

關於自定義Drawable,可以通過寫一個類,然後繼承自Drawable , 類似於自定義View,當然了自定義Drawable的核心方法只有一個,那就是draw。那麼自定義Drawable到底有什麼實際的作用呢?能干什麼呢?

相信大家對於圓角、圓形圖片都不陌生,並且我曾經寫過通過自定義View實現的方式,具體可參考:

AndroidBitmapShader 實戰 實現圓形、圓角圖片

Android Xfermode 實戰 實現圓形、圓角圖片

那我今天要告訴你,不需要自定義View,自定義Drawable也能實現,而且更加簡單、高效、使用范圍更廣(你可以作為任何View的背景)。

1、RoundImageDrawable

代碼比較簡單,下面看下RoundImageDrawable

 

[java]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
  1. packagecom.zhy.view;
  2.  
  3. importandroid.graphics.Bitmap;
  4. importandroid.graphics.BitmapShader;
  5. importandroid.graphics.Canvas;
  6. importandroid.graphics.ColorFilter;
  7. importandroid.graphics.Paint;
  8. importandroid.graphics.PixelFormat;
  9. importandroid.graphics.RectF;
  10. importandroid.graphics.Shader.TileMode;
  11. importandroid.graphics.drawable.Drawable;
  12.  
  13. publicclassRoundImageDrawableextendsDrawable
  14. {
  15.  
  16. privatePaintmPaint;
  17. privateBitmapmBitmap;
  18.  
  19. privateRectFrectF;
  20.  
  21. publicRoundImageDrawable(Bitmapbitmap)
  22. {
  23. mBitmap=bitmap;
  24. BitmapShaderbitmapShader=newBitmapShader(bitmap,TileMode.CLAMP,
  25. TileMode.CLAMP);
  26. mPaint=newPaint();
  27. mPaint.setAntiAlias(true);
  28. mPaint.setShader(bitmapShader);
  29. }
  30.  
  31. @Override
  32. publicvoidsetBounds(intleft,inttop,intright,intbottom)
  33. {
  34. super.setBounds(left,top,right,bottom);
  35. rectF=newRectF(left,top,right,bottom);
  36. }
  37.  
  38. @Override
  39. publicvoiddraw(Canvascanvas)
  40. {
  41. canvas.drawRoundRect(rectF,30,30,mPaint);
  42. }
  43.  
  44. @Override
  45. publicintgetIntrinsicWidth()
  46. {
  47. returnmBitmap.getWidth();
  48. }
  49.  
  50. @Override
  51. publicintgetIntrinsicHeight()
  52. {
  53. returnmBitmap.getHeight();
  54. }
  55.  
  56. @Override
  57. publicvoidsetAlpha(intalpha)
  58. {
  59. mPaint.setAlpha(alpha);
  60. }
  61.  
  62. @Override
  63. publicvoidsetColorFilter(ColorFiltercf)
  64. {
  65. mPaint.setColorFilter(cf);
  66. }
  67.  
  68. @Override
  69. publicintgetOpacity()
  70. {
  71. returnPixelFormat.TRANSLUCENT;
  72. }
  73.  
  74. }  

     

    核心代碼就是draw了,but,我們只需要一行~~~~setAlpha、setColorFilter、getOpacity、draw這幾個方法是必須實現的,不過除了draw以為,其他都很簡單。getIntrinsicWidth、getIntrinsicHeight主要是為了在View使用wrap_content的時候,提供一下尺寸,默認為-1可不是我們希望的。setBounds就是去設置下繪制的范圍。

    ok,圓角圖片就這麼實現了,easy 不~~

    看下用法:

     

    [java]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
    1. Bitmapbitmap=BitmapFactory.decodeResource(getResources(),
    2. R.drawable.mv);
    3. ImageViewiv=(ImageView)findViewById(R.id.id_one);
    4. iv.setImageDrawable(newRoundImageDrawable(bitmap));  
      ok,貼一下我們的效果圖,兩個ImageView和一個TextView

       

      \

      可以看到,不僅僅用於ImageView去實現圓角圖片,並且可以作為任何View的背景,在ImageView中的拉伸的情況,配下ScaleType即可。在其他View作為背景時,如果出現拉伸情況,請參考:Android BitmapShader 實戰 實現圓形、圓角圖片。 足夠詳細了。

      2、CircleImageDrawable

      那麼下來,我們再看看自定義圓形Drawable的寫法:

       

      [java]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
      1. packagecom.zhy.view;
      2.  
      3. importandroid.graphics.Bitmap;
      4. importandroid.graphics.BitmapShader;
      5. importandroid.graphics.Canvas;
      6. importandroid.graphics.ColorFilter;
      7. importandroid.graphics.Paint;
      8. importandroid.graphics.PixelFormat;
      9. importandroid.graphics.RectF;
      10. importandroid.graphics.Shader.TileMode;
      11. importandroid.graphics.drawable.Drawable;
      12.  
      13. publicclassCircleImageDrawableextendsDrawable
      14. {
      15.  
      16. privatePaintmPaint;
      17. privateintmWidth;
      18. privateBitmapmBitmap;
      19.  
      20. publicCircleImageDrawable(Bitmapbitmap)
      21. {
      22. mBitmap=bitmap;
      23. BitmapShaderbitmapShader=newBitmapShader(bitmap,TileMode.CLAMP,
      24. TileMode.CLAMP);
      25. mPaint=newPaint();
      26. mPaint.setAntiAlias(true);
      27. mPaint.setShader(bitmapShader);
      28. mWidth=Math.min(mBitmap.getWidth(),mBitmap.getHeight());
      29. }
      30.  
      31. @Override
      32. publicvoiddraw(Canvascanvas)
      33. {
      34. canvas.drawCircle(mWidth/2,mWidth/2,mWidth/2,mPaint);
      35. }
      36.  
      37. @Override
      38. publicintgetIntrinsicWidth()
      39. {
      40. returnmWidth;
      41. }
      42.  
      43. @Override
      44. publicintgetIntrinsicHeight()
      45. {
      46. returnmWidth;
      47. }
      48.  
      49. @Override
      50. publicvoidsetAlpha(intalpha)
      51. {
      52. mPaint.setAlpha(alpha);
      53. }
      54.  
      55. @Override
      56. publicvoidsetColorFilter(ColorFiltercf)
      57. {
      58. mPaint.setColorFilter(cf);
      59. }
      60.  
      61. @Override
      62. publicintgetOpacity()
      63. {
      64. returnPixelFormat.TRANSLUCENT;
      65. }
      66.  
      67. }  
        一樣出奇的簡單,再看一眼效果圖:

         

        \

        ok,關於自定義Drawable的例子over~~~接下來看自定義狀態的。

        上述參考了:Romain Guy's Blog

        3、自定義Drawable State

        關於Drawable State,state_pressed神馬的,相信大家都掌握的特別熟練了。

        那麼接下來,我們有個需求,類似於郵箱,郵件以ListView形式展示,但是我們需要一個狀態去標識出未讀和已讀:so,我們自定義一個狀態state_message_readed。

        效果圖:

        \

        可以看到,如果是已讀的郵件,我們的圖標是打開狀態,且有個淡紅色的背景。那麼如何通過自定義drawable state 實現呢?

        自定義drawable state 需要分為以下幾個步驟:

        1、res/values/新建一個xml文件:drawable_status.xml

         

        [html]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
        1.  
        2.  
        3.  
        4.  
        5.  
        6.  

           

           

          2、繼承Item的容器

          我們這裡Item選擇RelativeLayout實現,我們需要繼承它,然後復寫它的onCreateDrawableState方法,把我們自定義的狀態在合適的時候添加進去。

           

          [java]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
          1. packagecom.zhy.view;
          2.  
          3. importcom.zhy.sample.drawable.R;
          4.  
          5. importandroid.content.Context;
          6. importandroid.util.AttributeSet;
          7. importandroid.widget.RelativeLayout;
          8.  
          9. publicclassMessageListItemextendsRelativeLayout
          10. {
          11.  
          12. privatestaticfinalint[]STATE_MESSAGE_READED={R.attr.state_message_readed};
          13. privatebooleanmMessgeReaded=false;
          14.  
          15. publicMessageListItem(Contextcontext,AttributeSetattrs)
          16. {
          17. super(context,attrs);
          18. }
          19.  
          20. publicvoidsetMessageReaded(booleanreaded)
          21. {
          22. if(this.mMessgeReaded!=readed)
          23. {
          24. mMessgeReaded=readed;
          25. refreshDrawableState();
          26. }
          27. }
          28.  
          29. @Override
          30. protectedint[]onCreateDrawableState(intextraSpace)
          31. {
          32. if(mMessgeReaded)
          33. {
          34. finalint[]drawableState=super
          35. .onCreateDrawableState(extraSpace+1);
          36. mergeDrawableStates(drawableState,STATE_MESSAGE_READED);
          37. returndrawableState;
          38. }
          39. returnsuper.onCreateDrawableState(extraSpace);
          40. }
          41.  
          42. }  
            代碼不復雜,聲明了一個STATE_MESSAGE_READED,然後在mMessgeReaded=true的情況下,通過onCreateDrawableState方法,加入我們自定義的狀態。

             

            類似的代碼,大家可以看看CompoundButton(CheckBox父類)的源碼,它有個checked狀態:

             

            [java]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
            1. @Override
            2. protectedint[]onCreateDrawableState(intextraSpace){
            3. finalint[]drawableState=super.onCreateDrawableState(extraSpace+1);
            4. if(isChecked()){
            5. mergeDrawableStates(drawableState,CHECKED_STATE_SET);
            6. }
            7. returndrawableState;
            8. }  
              3、使用

               

              布局文件:

               

              [html]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
              1. xmlns:tools="http://schemas.android.com/tools"
              2. android:layout_width="match_parent"
              3. android:layout_height="50dp"
              4. android:background="@drawable/message_item_bg">
              5.  
              6. android:id="@+id/id_msg_item_icon"
              7. android:layout_width="30dp"
              8. android:src="@drawable/message_item_icon_bg"
              9. android:layout_height="wrap_content"
              10. android:duplicateParentState="true"
              11. android:layout_alignParentLeft="true"
              12. android:layout_centerVertical="true"
              13. />
              14.  
              15. android:id="@+id/id_msg_item_text"
              16. android:layout_width="match_parent"
              17. android:layout_height="wrap_content"
              18. android:layout_centerVertical="true"
              19. android:layout_toRightOf="@id/id_msg_item_icon"/>
              20.  
              21.  
                很簡單,一個圖標,一個文本;

                 

                Activity

                 

                [java]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
                1. packagecom.zhy.sample.drawable;
                2.  
                3. importcom.zhy.view.MessageListItem;
                4.  
                5. importandroid.app.ListActivity;
                6. importandroid.os.Bundle;
                7. importandroid.view.LayoutInflater;
                8. importandroid.view.View;
                9. importandroid.view.ViewGroup;
                10. importandroid.widget.ArrayAdapter;
                11. importandroid.widget.TextView;
                12.  
                13. publicclassCustomStateActivityextendsListActivity
                14. {
                15. privateMessage[]messages=newMessage[]{
                16. newMessage("Gasbilloverdue",true),
                17. newMessage("Congratulations,you'vewon!",true),
                18. newMessage("Iloveyou!",false),
                19. newMessage("Pleasereply!",false),
                20. newMessage("Youignoringme?",false),
                21. newMessage("Notheardfromyou",false),
                22. newMessage("Electricitybill",true),
                23. newMessage("Gasbill",true),newMessage("Holidayplans",false),
                24. newMessage("Marketingstuff",false),};
                25.  
                26. @Override
                27. protectedvoidonCreate(BundlesavedInstanceState)
                28. {
                29. super.onCreate(savedInstanceState);
                30.  
                31. getListView().setAdapter(newArrayAdapter(this,-1,messages)
                32. {
                33. privateLayoutInflatermInflater=LayoutInflater
                34. .from(getContext());
                35.  
                36. @Override
                37. publicViewgetView(intposition,ViewconvertView,ViewGroupparent)
                38. {
                39. if(convertView==null)
                40. {
                41. convertView=mInflater.inflate(R.layout.item_msg_list,
                42. parent,false);
                43. }
                44. MessageListItemmessageListItem=(MessageListItem)convertView;
                45. TextViewtv=(TextView)convertView
                46. .findViewById(R.id.id_msg_item_text);
                47. tv.setText(getItem(position).message);
                48. messageListItem.setMessageReaded(getItem(position).readed);
                49. returnconvertView;
                50. }
                51.  
                52. });
                53.  
                54. }
                55. }  
                  代碼很簡單,但是可以看到,我們需要在getView裡面中去使用調用setMessageReaded方法,當然了其他的一些狀態,肯定也要手動觸發,比如在ACTION_DOWN中觸發pressed等。請勿糾結咋沒有使用ViewHolder什麼的,自己添加下就行。

                   

                  4、提升我們的UI Perfermance

                  現在大家越來越注重性能問題,其實沒必要那麼在乎,但是既然大家在乎了,這裡通過Cyril Mottier :master_android_drawables ppt中的一個例子來說明如果利用Drawable來提升我們的UI的性能。

                  大家看這樣一個效果圖:

                  \

                  布局文件:

                   

                  [html]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
                  1.  
                  2. android:layout_width="match_parent"
                  3. android:layout_height="match_parent"
                  4. android:background="@color/app_background"
                  5. android:padding="8dp">
                  6.  
                  7. android:layout_width="wrap_content"
                  8. android:layout_height="wrap_content"
                  9. android:layout_gravity="center"
                  10. android:layout_marginBottom="24dp"
                  11. android:src="@drawable/logo"/>
                  12.  
                  13. android:layout_width="match_parent"
                  14. android:layout_height="48dp"
                  15. android:layout_gravity="bottom"
                  16. android:orientation="horizontal">
                  17.  
                  18. android:layout_width="0dp"
                  19. android:layout_height="fill_parent"
                  20. android:layout_weight="1"
                  21. android:text="@string/sign_up"/>
                  22.  
                  23. android:layout_width="0dp"
                  24. android:layout_height="fill_parent"
                  25. android:layout_weight="1"
                  26. android:text="@string/sign_in"/>
                  27.  
                  28.  
                  29.  
                    可以看到最外層是FrameLayout僅僅是為了設置背景圖和padding,這樣的布局相信很多人也寫過。

                     

                    再看看這個布局作為APP啟動時,用戶的直觀效果:

                    \

                    用戶首先看到一個白板,然後顯示出我們的頁面。接下來,我們將利用Drawable改善我們的UI性能以及用戶體驗。

                    1、首先,我們去除我們最外層的FrameLayout,然後自定義一個drawable的xml,叫做logo.xml

                     

                    [html]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
                    1.  
                    2.  
                    3.  
                    4.  
                    5.  
                    6.  
                    7.  
                  30.  
                  31.  
                  32. android:gravity="center"
                  33. android:src="@drawable/logo"/>
                  34.  
                  35.  
                    ok,這個drawable是設置了我們的背景和logo;

                     

                    2、將其作為我們當前Activity的windowBackground

                     

                    [html]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
                    1.  
                    2. name="Theme.Default.NoActionBar"
                    3. parent="@android:style/Theme.Holo.Light.NoActionBar">
                    4. @drawable/login
                    5.  
                    6.  
                  36.  

                     

                    3、設置到Activity上:

                     

                    [html]view plaincopy   在CODE上查看代碼片派生到我的代碼片    
                    1. android:name="LoginActivity"
                    2. android:theme="@style/Theme.Default.NoActionBar">  
                      Ok,這樣不僅最小化了我們的layout,現在我們的layout裡面只有一個LinearLayout和兩個按鈕;並且提升了用戶體驗,現在用戶的直觀效果時:

                       

                      \

                      是不是體驗好很多,個人很喜歡這個例子~~

                       

                      ok,到此我們的文章就over了~~~大多數內容參考自一些牛人寫的例子,例子還是棒棒哒,大家看完本文的同時,也可以去挖掘挖掘一些東西~~

                       

                       

                      源碼點擊下載

                       
                  1. 上一頁:
                  2. 下一頁:
                  熱門文章
                  閱讀排行版
                  Copyright © Android教程網 All Rights Reserved