Android Drawable 那些鲜为人知的高效用法

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43752383,本文出自:【张鸿洋的博客】html

一、概述

Drawable在咱们平时的开发中,基本都会用到,并且给你们很是的有用。那么什么是Drawable呢?可以在canvas上绘制的一个玩意,并且相比于View,并不须要去考虑measure、layout,仅仅只要去考虑如何draw(canavs)。固然了,对于Drawable传统的用法,你们确定不陌生 ,今天主要给你们带来如下几个Drawable的用法:java

一、自定义Drawable,相比View来讲,Drawable属于轻量级的、使用也很简单。之后自定义实现一个效果的时候,能够改变View first的思想,尝试下Drawable first。android

二、自定义状态,相信你们对于State Drawable都不陌生,可是有没有尝试过去自定义一个状态呢?git

三、利用Drawable提高咱们的UI Perfermance , 如何利用Drawable去提高咱们的UI的性能。github

 

二、Drawable基本概念

通常状况下,除了直接使用放在Drawable下的图片,其实的Drawable的用法都和xml相关,咱们可使用shape、layer-list等标签绘制一些背景,还能够经过selector标签订义View的状态的效果等。固然了基本每一个标签都对应于一个真正的实体类,关系以下:(图片来自:Cyril Mottier :master_android_drawables)canvas

常见的用法这里就不举例了,下面开始看本文的重点。app

二、自定义Drawable

关于自定义Drawable,能够经过写一个类,而后继承自Drawable , 相似于自定义View,固然了自定义Drawable的核心方法只有一个,那就是draw。那么自定义Drawable到底有什么实际的做用呢?能干什么呢?ide

相信你们对于圆角、圆形图片都不陌生,而且我曾经写过经过自定义View实现的方式,具体可参考:布局

Android BitmapShader 实战 实现圆形、圆角图片性能

Android Xfermode 实战 实现圆形、圆角图片

那我今天要告诉你,不须要自定义View,自定义Drawable也能实现,并且更加简单、高效、使用范围更广(你能够做为任何View的背景)。

一、RoundImageDrawable

代码比较简单,下面看下RoundImageDrawable

[java] view plain copy
  1. package com.zhy.view;  
  2.   
  3. import android.graphics.Bitmap;  
  4. import android.graphics.BitmapShader;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.ColorFilter;  
  7. import android.graphics.Paint;  
  8. import android.graphics.PixelFormat;  
  9. import android.graphics.RectF;  
  10. import android.graphics.Shader.TileMode;  
  11. import android.graphics.drawable.Drawable;  
  12.   
  13. public class RoundImageDrawable extends Drawable  
  14. {  
  15.   
  16.     private Paint mPaint;  
  17.     private Bitmap mBitmap;  
  18.   
  19.     private RectF rectF;  
  20.   
  21.     public RoundImageDrawable(Bitmap bitmap)  
  22.     {  
  23.         mBitmap = bitmap;  
  24.         BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP,  
  25.                 TileMode.CLAMP);  
  26.         mPaint = new Paint();  
  27.         mPaint.setAntiAlias(true);  
  28.         mPaint.setShader(bitmapShader);  
  29.     }  
  30.   
  31.     @Override  
  32.     public void setBounds(int left, int top, int right, int bottom)  
  33.     {  
  34.         super.setBounds(left, top, right, bottom);  
  35.         rectF = new RectF(left, top, right, bottom);  
  36.     }  
  37.   
  38.     @Override  
  39.     public void draw(Canvas canvas)  
  40.     {  
  41.         canvas.drawRoundRect(rectF, 30, 30, mPaint);  
  42.     }  
  43.   
  44.     @Override  
  45.     public int getIntrinsicWidth()  
  46.     {  
  47.         return mBitmap.getWidth();  
  48.     }  
  49.   
  50.     @Override  
  51.     public int getIntrinsicHeight()  
  52.     {  
  53.         return mBitmap.getHeight();  
  54.     }  
  55.   
  56.     @Override  
  57.     public void setAlpha(int alpha)  
  58.     {  
  59.         mPaint.setAlpha(alpha);  
  60.     }  
  61.   
  62.     @Override  
  63.     public void setColorFilter(ColorFilter cf)  
  64.     {  
  65.         mPaint.setColorFilter(cf);  
  66.     }  
  67.   
  68.     @Override  
  69.     public int getOpacity()  
  70.     {  
  71.         return PixelFormat.TRANSLUCENT;  
  72.     }  
  73.   
  74. }  

核心代码就是draw了,but,咱们只须要一行~~~~setAlpha、setColorFilter、getOpacity、draw这几个方法是必须实现的,不过除了draw觉得,其余都很简单。getIntrinsicWidth、getIntrinsicHeight主要是为了在View使用wrap_content的时候,提供一下尺寸,默认为-1可不是咱们但愿的。setBounds就是去设置下绘制的范围。

ok,圆角图片就这么实现了,easy 不~~

看下用法:

[java] view plain copy
  1. Bitmap bitmap = BitmapFactory.decodeResource(getResources(),  
  2.                 R.drawable.mv);  
  3.         ImageView iv = (ImageView) findViewById(R.id.id_one);  
  4.         iv.setImageDrawable(new RoundImageDrawable(bitmap));  


ok,贴一下咱们的效果图,两个ImageView和一个TextView

能够看到,不只仅用于ImageView去实现圆角图片,而且能够做为任何View的背景,在ImageView中的拉伸的状况,配下ScaleType便可。在其余View做为背景时,若是出现拉伸状况,请参考:Android BitmapShader 实战 实现圆形、圆角图片 。 足够详细了。

二、CircleImageDrawable

那么下来,咱们再看看自定义圆形Drawable的写法:

[java] view plain copy
  1. package com.zhy.view;  
  2.   
  3. import android.graphics.Bitmap;  
  4. import android.graphics.BitmapShader;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.ColorFilter;  
  7. import android.graphics.Paint;  
  8. import android.graphics.PixelFormat;  
  9. import android.graphics.RectF;  
  10. import android.graphics.Shader.TileMode;  
  11. import android.graphics.drawable.Drawable;  
  12.   
  13. public class CircleImageDrawable extends Drawable  
  14. {  
  15.   
  16.     private Paint mPaint;  
  17.     private int mWidth;  
  18.     private Bitmap mBitmap ;   
  19.   
  20.     public CircleImageDrawable(Bitmap bitmap)  
  21.     {  
  22.         mBitmap = bitmap ;   
  23.         BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP,  
  24.                 TileMode.CLAMP);  
  25.         mPaint = new Paint();  
  26.         mPaint.setAntiAlias(true);  
  27.         mPaint.setShader(bitmapShader);  
  28.         mWidth = Math.min(mBitmap.getWidth(), mBitmap.getHeight());  
  29.     }  
  30.   
  31.     @Override  
  32.     public void draw(Canvas canvas)  
  33.     {  
  34.         canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2, mPaint);  
  35.     }  
  36.   
  37.     @Override  
  38.     public int getIntrinsicWidth()  
  39.     {  
  40.         return mWidth;  
  41.     }  
  42.   
  43.     @Override  
  44.     public int getIntrinsicHeight()  
  45.     {  
  46.         return mWidth;  
  47.     }  
  48.   
  49.     @Override  
  50.     public void setAlpha(int alpha)  
  51.     {  
  52.         mPaint.setAlpha(alpha);  
  53.     }  
  54.   
  55.     @Override  
  56.     public void setColorFilter(ColorFilter cf)  
  57.     {  
  58.         mPaint.setColorFilter(cf);  
  59.     }  
  60.   
  61.     @Override  
  62.     public int getOpacity()  
  63.     {  
  64.         return PixelFormat.TRANSLUCENT;  
  65.     }  
  66.   
  67. }  


同样出奇的简单,再看一眼效果图:

ok,关于自定义Drawable的例子over~~~接下来看自定义状态的。

上述参考了:Romain Guy's Blog

三、自定义Drawable State

关于Drawable State,state_pressed神马的,相信你们都掌握的特别熟练了。

那么接下来,咱们有个需求,相似于邮箱,邮件以ListView形式展现,可是咱们须要一个状态去标识出未读和已读:so,咱们自定义一个状态state_message_readed。

效果图:

能够看到,若是是已读的邮件,咱们的图标是打开状态,且有个淡红色的背景。那么如何经过自定义drawable state 实现呢?

自定义drawable state 须要分为如下几个步骤:

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

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <declare-styleable name="MessageStatus">  
  4.         <attr name="state_message_readed" format="boolean" />  
  5.     </declare-styleable>  
  6. </resources>  

 

二、继承Item的容器

咱们这里Item选择RelativeLayout实现,咱们须要继承它,而后复写它的onCreateDrawableState方法,把咱们自定义的状态在合适的时候添加进去。

[java] view plain copy
  1. package com.zhy.view;  
  2.   
  3. import com.zhy.sample.drawable.R;  
  4.   
  5. import android.content.Context;  
  6. import android.util.AttributeSet;  
  7. import android.widget.RelativeLayout;  
  8.   
  9. public class MessageListItem extends RelativeLayout  
  10. {  
  11.   
  12.     private static final int[] STATE_MESSAGE_READED = { R.attr.state_message_readed };  
  13.     private boolean mMessgeReaded = false;  
  14.   
  15.     public MessageListItem(Context context, AttributeSet attrs)  
  16.     {  
  17.         super(context, attrs);  
  18.     }  
  19.   
  20.     public void setMessageReaded(boolean readed)  
  21.     {  
  22.         if (this.mMessgeReaded != readed)  
  23.         {  
  24.             mMessgeReaded = readed;  
  25.             refreshDrawableState();  
  26.         }  
  27.     }  
  28.   
  29.     @Override  
  30.     protected int[] onCreateDrawableState(int extraSpace)  
  31.     {  
  32.         if (mMessgeReaded)  
  33.         {  
  34.             final int[] drawableState = super  
  35.                     .onCreateDrawableState(extraSpace + 1);  
  36.             mergeDrawableStates(drawableState, STATE_MESSAGE_READED);  
  37.             return drawableState;  
  38.         }  
  39.         return super.onCreateDrawableState(extraSpace);  
  40.     }  
  41.   
  42. }  


代码不复杂,声明了一个STATE_MESSAGE_READED,而后在mMessgeReaded=true的状况下,经过onCreateDrawableState方法,加入咱们自定义的状态。

相似的代码,你们能够看看CompoundButton(CheckBox父类)的源码,它有个checked状态:

[java] view plain copy
  1. @Override  
  2.    protected int[] onCreateDrawableState(int extraSpace) {  
  3.        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);  
  4.        if (isChecked()) {  
  5.            mergeDrawableStates(drawableState, CHECKED_STATE_SET);  
  6.        }  
  7.        return drawableState;  
  8.    }  


三、使用

布局文件:

[html] view plain copy
  1. <com.zhy.view.MessageListItem xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="50dp"  
  5.     android:background="@drawable/message_item_bg" >  
  6.   
  7.     <ImageView  
  8.         android:id="@+id/id_msg_item_icon"  
  9.         android:layout_width="30dp"  
  10.         android:src="@drawable/message_item_icon_bg"  
  11.         android:layout_height="wrap_content"  
  12.         android:duplicateParentState="true"  
  13.         android:layout_alignParentLeft="true"  
  14.         android:layout_centerVertical="true"  
  15.       />  
  16.   
  17.     <TextView  
  18.         android:id="@+id/id_msg_item_text"  
  19.         android:layout_width="match_parent"  
  20.         android:layout_height="wrap_content"  
  21.         android:layout_centerVertical="true"  
  22.         android:layout_toRightOf="@id/id_msg_item_icon" />  
  23.   
  24. </com.zhy.view.MessageListItem>  


很简单,一个图标,一个文本;

Activity

[java] view plain copy
  1. package com.zhy.sample.drawable;  
  2.   
  3. import com.zhy.view.MessageListItem;  
  4.   
  5. import android.app.ListActivity;  
  6. import android.os.Bundle;  
  7. import android.view.LayoutInflater;  
  8. import android.view.View;  
  9. import android.view.ViewGroup;  
  10. import android.widget.ArrayAdapter;  
  11. import android.widget.TextView;  
  12.   
  13. public class CustomStateActivity extends ListActivity  
  14. {  
  15.     private Message[] messages = new Message[] {  
  16.             new Message("Gas bill overdue", true),  
  17.             new Message("Congratulations, you've won!", true),  
  18.             new Message("I love you!", false),  
  19.             new Message("Please reply!", false),  
  20.             new Message("You ignoring me?", false),  
  21.             new Message("Not heard from you", false),  
  22.             new Message("Electricity bill", true),  
  23.             new Message("Gas bill", true), new Message("Holiday plans", false),  
  24.             new Message("Marketing stuff", false), };  
  25.   
  26.     @Override  
  27.     protected void onCreate(Bundle savedInstanceState)  
  28.     {  
  29.         super.onCreate(savedInstanceState);  
  30.   
  31.         getListView().setAdapter(new ArrayAdapter<Message>(this, -1, messages)  
  32.         {  
  33.             private LayoutInflater mInflater = LayoutInflater  
  34.                     .from(getContext());  
  35.   
  36.             @Override  
  37.             public View getView(int position, View convertView, ViewGroup parent)  
  38.             {  
  39.                 if (convertView == null)  
  40.                 {  
  41.                     convertView = mInflater.inflate(R.layout.item_msg_list,  
  42.                             parent, false);  
  43.                 }  
  44.                 MessageListItem messageListItem = (MessageListItem) convertView;  
  45.                 TextView tv = (TextView) convertView  
  46.                         .findViewById(R.id.id_msg_item_text);  
  47.                 tv.setText(getItem(position).message);  
  48.                 messageListItem.setMessageReaded(getItem(position).readed);  
  49.                 return convertView;  
  50.             }  
  51.   
  52.         });  
  53.   
  54.     }  
  55. }  


代码很简单,可是能够看到,咱们须要在getView里面中去使用调用setMessageReaded方法,固然了其余的一些状态,确定也要手动触发,好比在ACTION_DOWN中触发pressed等。请勿纠结咋没有使用ViewHolder什么的,本身添加下就行。

本例参考自:Example from github 

四、提高咱们的UI Perfermance 

如今你们愈来愈注重性能问题,其实不必那么在意,可是既然你们在意了,这里经过Cyril Mottier :master_android_drawables ppt中的一个例子来讲明若是利用Drawable来提高咱们的UI的性能。

你们看这样一个效果图:

布局文件:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="@color/app_background"  
  6.     android:padding="8dp" >  
  7.   
  8.     <ImageView  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_gravity="center"  
  12.         android:layout_marginBottom="24dp"  
  13.         android:src="@drawable/logo" />  
  14.   
  15.     <LinearLayout  
  16.         android:layout_width="match_parent"  
  17.         android:layout_height="48dp"  
  18.         android:layout_gravity="bottom"  
  19.         android:orientation="horizontal" >  
  20.   
  21.         <Button  
  22.             android:layout_width="0dp"  
  23.             android:layout_height="fill_parent"  
  24.             android:layout_weight="1"  
  25.             android:text="@string/sign_up" />  
  26.   
  27.         <Button  
  28.             android:layout_width="0dp"  
  29.             android:layout_height="fill_parent"  
  30.             android:layout_weight="1"  
  31.             android:text="@string/sign_in" />  
  32.     </LinearLayout>  
  33.   
  34. </FrameLayout>  


能够看到最外层是FrameLayout仅仅是为了设置背景图和padding,这样的布局相信不少人也写过。

再看看这个布局做为APP启动时,用户的直观效果:

用户首先看到一个白板,而后显示出咱们的页面。接下来,咱们将利用Drawable改善咱们的UI性能以及用户体验。

一、首先,咱们去除咱们最外层的FrameLayout,而后自定义一个drawable的xml,叫作logo.xml

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >  
  3.     <item>  
  4.   
  5.         <shape android:shape="rectangle" >  
  6.             <solid android:color="@color/app_background" />  
  7.         </shape>  
  8.     </item>  
  9.   
  10.     <item android:bottom="48dp">  
  11.         <bitmap  
  12.             android:gravity="center"  
  13.             android:src="@drawable/logo" />  
  14.     </item>  
  15. </layer-list>  


ok,这个drawable是设置了咱们的背景和logo;

二、将其做为咱们当前Activity的windowBackground

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <style  
  4.         name="Theme.Default.NoActionBar"  
  5.         parent="@android:style/Theme.Holo.Light.NoActionBar" >  
  6.         <item name="android:windowBackground">@drawable/login</item>  
  7.     </style>  
  8. </resources>  

三、设置到Activity上:

[html] view plain copy
  1. <activity  
  2. android:name="LoginActivity"  
  3. android:theme="@style/Theme.Default.NoActionBar">  


Ok,这样不只最小化了咱们的layout,如今咱们的layout里面只有一个LinearLayout和两个按钮;而且提高了用户体验,如今用户的直观效果时:

是否是体验好不少,我的很喜欢这个例子~~

 

ok,到此咱们的文章就over了~~~大多数内容参考自一些牛人写的例子,例子仍是棒棒哒,你们看完本文的同时,也能够去挖掘挖掘一些东西~~

 

源码点击下载

相关文章
相关标签/搜索