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

android自定义drawable的state属性 android自定义drawable的state属性 在drawable中使用selector在开发中太常用了,不过用的最多是根据系统提供的一些状态来选择图片,比如:android:state_checked、android:state_pressed等等,其实这些状态也是可以自定义的,比方说天气状况有很多种情况,天晴、多云、下雨等等,那能不能根据这些状况来显示不同的示意图呢?这里是指通过 sel... 阅读详情
转载请标明出处: http://blog.youkuaiyun.com/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.   
  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 不~~

看下用法:

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

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.   
  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状态:

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

  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 

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.   
  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性能以及用户体验。

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

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和两个按钮;并且提升了用户体验,现在用户的直观效果时:


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



onCreateDrawableState&mergeDrawableStates Demo android 使用drawable将显示状态和数据分离,你所不知道的简单实现方式.在Android 的实际开发中,我们经常会遇到这样的情况:需要根据不同逻辑判断来显示,icon/color,将判断逻辑加在adapter的getview方法中,总感觉不符合mvc模式,也影响代码的阅读。使用选择器来处理,这个思路是对了,但是还缺一点,下面我们先来看看缺点什么 立即下载

相关推荐

Android自定义Drawable的代码例子

Android自定义Drawable的代码例子。用于演示自定义Drawable的功能,包括自定义形状、自定义水印、自定义淡出等几种类型的Drawable。

(原创)自定义Drawable

在日常的android开发中,我们会经常用到drawable资源 比如设置icon: 一般的drawable我们都是一张图 但其实我们也可以像自定义View一样 去自定义我们的Drawable 自己定义的Drawable 也可以绘制文字,形状,以及加入一些图片 做成自己需要的样子 本篇文章就通过实战来教你如何去自定义一个Drawable 先看下效果图:..................

Android_xiong_st的博客 2849

FID指标实战:如何用Python快速计算生成图像的质量分数(附完整代码)

本文详细介绍了如何使用Python计算Fréchet Inception Distance(FID)指标,以评估生成图像的质量。通过完整的代码示例和优化技巧,帮助开发者快速实现FID计算,并解读分数背后的实际意义,适用于GAN和扩散模型的性能评估。

weixin_28718487的博客 376

2024年安卓最新Android Drawable 那些不为人知的高效用法(1),3面直接拿到offer

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。

2401_84558870的博客 787

Goldwasser-Micali 公钥加密系统

1、二次剩余问题 对于整数n ,定义Zn∗={a∈Zn,gcd(a,n)=1}Z^*_n=\{ a∈Z_n,gcd(a,n) =1\}Zn∗​={a∈Zn​,gcd(a,n)=1} 。当存在d∈Znd \in Z_nd∈Zn​ ,使得 d2≡a mod nd^2 \equiv a \, mod \,nd2≡amodn ,称 a 为模 n 的二次剩余;否则称 a 为模 n的二次非剩余。判断 a 是否为模 n 的二次剩余的问题称为模n 的二次剩余问题。 \rule[-10pt]{14.3cm}{0.05em}

不染心的博客 4312

RAG——从0到1快速搭建RAG应用

本文介绍了基于LangChain框架构建RAG(检索增强生成)应用的技术方案,涵盖核心模块选型与实现流程。技术栈包括:LangChain应用框架、pypdf文档解析、RecursiveCharacterTextSplitter文本分块、bge-small-zh-v1.5向量模型、Faiss向量库和通义千问大模型。文章详细说明了开发环境配置步骤,包括虚拟环境创建、依赖库安装和模型下载。核心代码分为索引、检索、生成三大流程,索引流程完成PDF文档解析、分块和向量化存储;检索流程实现查询向量化和相似度匹配;生成流

小志的博客 590
上一篇: Android 事件处理全面剖析
下一篇: android.onKey事件的return
xiaoxiandroid
博客等级 码龄12年 12粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值