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,自定義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. }
  68.  
一樣出奇的簡單,再看一眼效果圖:

 

\

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.  


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.  
  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什麼的,自己添加下就行。[html]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.  
  2.  
  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.  

可以看到最外層是FrameLayout僅僅是為了設置背景圖和padding,這樣的布局相信很多人也寫過。

 

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

\

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

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

 

[html]view plaincopy   在CODE上查看代碼片派生到我的代碼片
  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10. android:gravity="center"
  11. android:src="@drawable/logo"/>
  12.  
  13.  

ok,這個drawable是設置了我們的背景和logo;[html]view plaincopy在CODE上查看代碼片派生到我的代碼片
  1.  
  2. name="Theme.Default.NoActionBar"
  3. parent="@android:style/Theme.Holo.Light.NoActionBar">
  4. @drawable/login
  5.  

 

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