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

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实现的方式,具体可参考:

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

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

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

1、RoundImageDrawable

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

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

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

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

看下用法:

  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 实战 实现圆形、圆角图片 。 足够详细了。

2、CircleImageDrawable

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

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

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

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

  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>

2、继承Item的容器

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

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

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

类似的代码,大家可以看看CompoundButton(CheckBox父类)的源码,它有个checked状态:

  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. }

3、使用

布局文件:

  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. <ImageView
  7. android:id="@+id/id_msg_item_icon"
  8. android:layout_width="30dp"
  9. android:src="@drawable/message_item_icon_bg"
  10. android:layout_height="wrap_content"
  11. android:duplicateParentState="true"
  12. android:layout_alignParentLeft="true"
  13. android:layout_centerVertical="true"
  14. />
  15. <TextView
  16. android:id="@+id/id_msg_item_text"
  17. android:layout_width="match_parent"
  18. android:layout_height="wrap_content"
  19. android:layout_centerVertical="true"
  20. android:layout_toRightOf="@id/id_msg_item_icon" />
  21. </com.zhy.view.MessageListItem>

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

Activity

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

代码很简单,但是可以看到,我们需要在getView里面中去使用调用setMessageReaded方法,当然了其他的一些状态,肯定也要手动触发,比如在ACTION_DOWN中触发pressed等。请勿纠结咋没有使用ViewHolder什么的,自己添加下就行。

本例参考自:Example from github

4、提升我们的UI Perfermance

现在大家越来越注重性能问题,其实没必要那么在乎,但是既然大家在乎了,这里通过Cyril Mottier :master_android_drawables ppt中的一个例子来说明如果利用Drawable来提升我们的UI的性能。

大家看这样一个效果图:

布局文件:

  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. <ImageView
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:layout_gravity="center"
  11. android:layout_marginBottom="24dp"
  12. android:src="@drawable/logo" />
  13. <LinearLayout
  14. android:layout_width="match_parent"
  15. android:layout_height="48dp"
  16. android:layout_gravity="bottom"
  17. android:orientation="horizontal" >
  18. <Button
  19. android:layout_width="0dp"
  20. android:layout_height="fill_parent"
  21. android:layout_weight="1"
  22. android:text="@string/sign_up" />
  23. <Button
  24. android:layout_width="0dp"
  25. android:layout_height="fill_parent"
  26. android:layout_weight="1"
  27. android:text="@string/sign_in" />
  28. </LinearLayout>
  29. </FrameLayout>

可以看到最外层是FrameLayout仅仅是为了设置背景图和padding,这样的布局相信很多人也写过。

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

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

1、首先,我们去除我们最外层的FrameLayout,然后自定义一个drawable的xml,叫做logo.xml

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

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

2、将其作为我们当前Activity的windowBackground

  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>

3、设置到Activity上:

  1. <activity
  2. android:name="LoginActivity"
  3. android:theme="@style/Theme.Default.NoActionBar">

Ok,这样不仅最小化了我们的layout,现在我们的layout里面只有一个LinearLayout和两个按钮;并且提升了用户体验,现在用户的直观效果时:

是不是体验好很多,个人很喜欢这个例子~~

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

Android Drawable 那些不为人知的高效用法的更多相关文章

  1. Android Drawable 那些不为人知的高效使用方法

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43752383,本文出自:[张鸿洋的博客] 1.概述 Drawable在我们平时的 ...

  2. android中Handle类的用法

    android中Handle类的用法 当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无 ...

  3. Android Drawable 与 LayerList综合汇总

    先看需求.要求这样的效果 上代码 <?xml version="1.0" encoding="utf-8"? > <layer-list xm ...

  4. Android Support Library 23.2用法简析

    写在前面的几句话 前几天谷歌发布了android-support-library-23.2支持库,这一次23.2版本增加了一些新的支持库以及新的功能.接下来这篇文章,就是对这些新功能部分做简单的用法介 ...

  5. Android Selector和Shape的用法

    一.Shape的用法 :shape用于设定形状,可以在selector,layout等里面使用,有6个子标签,各属性如下: 填充:设置填充的颜色 间隔:设置四个方向上的间隔 大小:设置大小 圆角:同时 ...

  6. Android View.setId(int id) 用法

    Android View.setId(int id) 用法 当要在代码中动态的添加View并且为其设置id时,如果直接用一个int值时,Studio会警告. 经过查询,动态设置id的方法有两种; 1. ...

  7. Android Intent的几种用法全面总结

    Android Intent的几种用法全面总结 Intent, 用法 Intent应该算是Android中特有的东西.你可以在Intent中指定程序要执行的动作(比如:view,edit,dial), ...

  8. Android Drawable的9种子类 介绍

    原文: Android Drawable的9种子类 介绍   Drawable 在android里面 就是代表着图像,注意是图像 而不是图片. 图片是图像的子集.图像除了可以包含图片以外 还可以包含颜 ...

  9. 【转】Android Drawable Resource学习(十一)、RotateDrawable

    对另一个drawable资源,基于当前的level,进行旋转的drawable. 文件位置: res/drawable/filename.xml文件名即资源名 编译数据类型: 指向 RotateDra ...

随机推荐

  1. 窗体Showmedol 遇到的奇怪异常: cannot make a visible window model

    //窗体Showmedol 遇到的奇怪异常: cannot make a visible window model //背景:ShowModal A窗体,A窗体再ShowModal B窗体:A是透明背 ...

  2. Sprint2团队贡献分

    团队贡献分: 郭志豪:31%  http://www.cnblogs.com/gzh13692021053/ 杨子健:22%http://www.cnblogs.com/yzj666/ 谭宇森:23% ...

  3. 用wget命令下载jdk

    Oracle官网上下载jdk,需要点击accept licence的才能下载,使用下面的命令,直接可以下载.wget --no-check-certificate --no-cookies --hea ...

  4. MVC Razor视图引擎的入门

    首先我们来说说他的给我们开发者带来那些好处吧: Razor语法易于输入,易于阅读,微软当时是这样定义的:简洁,富有表现力和灵活性,支持所有文本编辑器,强大的智能提示功能,单元测试. Rozor文件类型 ...

  5. 我的前端故事----优美的编辑器GitHub Atom

    很多前端的同学都在用sublime text,我之前也在使用,但是后来接触到了Atom,就被它的高颜值深深的吸引了~~不愧是GitHub的工程师哦~审美就是高  Atom 作为一个跨平台的编辑软件,安 ...

  6. block 从B界面向A界面传值

    最近在改公司外包项目的代码,发现了一种block传值的用法很有意思,记录一下 A.B两个页面 在B界面.h中定义 @property (nonatomic,strong) void(^block)(N ...

  7. jquery基本

    对于jquery属性的访问: //对于bool值的属性,元素标签中如果写了这个属性,attr能够获取到,如果没有写,就获取不到. 如:<input type="checkbox&quo ...

  8. java分享第十八天( JAVA利用enum结合testng做数据驱动示例)

    数据驱动是做自动化测试中很重要的一部分,数据源的方案也是百花八门了,比如利用外部文件,直接在@DataProvider中写死等等,我们今天介绍一下利用enum来做数据源,先来看一下enum的写法: p ...

  9. js知识点

    在变量复制方面,基本类型和引用类型也有所不同,基本类型复制的是值本身,而引用类型复制的是地址. 循环引用 一个很简单的例子:一个DOM对象被一个Javascript对象引用,与此同时又引用同一个或其它 ...

  10. 前端自动化工具gulp自动添加版本号

    之前,我介绍了学习安装并配置前端自动化工具Gulp,觉得gulp确实比grunt的配置简单很多,于是我决定再深入学习一下gulp,就去网上查了资料,发现gulp还可以自动添加版本号,这个功能就为我平时 ...