AndroidDrawable那些不為人知的高效用法(轉載)

來源:互聯網
上載者:User

標籤:

AndroidDrawable那些不為人知的高效用法
轉載請標明出處: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

package com.zhy.view;import android.graphics.Bitmap;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.ColorFilter;import android.graphics.Paint;import android.graphics.PixelFormat;import android.graphics.RectF;import android.graphics.Shader.TileMode;import android.graphics.drawable.Drawable;public class RoundImageDrawable extends Drawable{private Paint mPaint;private Bitmap mBitmap;private RectF rectF;public RoundImageDrawable(Bitmap bitmap){mBitmap = bitmap;BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP,TileMode.CLAMP);mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setShader(bitmapShader);}@Overridepublic void setBounds(int left, int top, int right, int bottom){super.setBounds(left, top, right, bottom);rectF = new RectF(left, top, right, bottom);}@Overridepublic void draw(Canvas canvas){canvas.drawRoundRect(rectF, 30, 30, mPaint);}@Overridepublic int getIntrinsicWidth(){return mBitmap.getWidth();}@Overridepublic int getIntrinsicHeight(){return mBitmap.getHeight();}@Overridepublic void setAlpha(int alpha){mPaint.setAlpha(alpha);}@Overridepublic void setColorFilter(ColorFilter cf){mPaint.setColorFilter(cf);}@Overridepublic int getOpacity(){return PixelFormat.TRANSLUCENT;}}

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

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

看下用法:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.mv);ImageView iv = (ImageView) findViewById(R.id.id_one);iv.setImageDrawable(new RoundImageDrawable(bitmap));

ok,貼一下我們的,兩個ImageView和一個TextView


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

2、CircleImageDrawable

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

package com.zhy.view;import android.graphics.Bitmap;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.ColorFilter;import android.graphics.Paint;import android.graphics.PixelFormat;import android.graphics.RectF;import android.graphics.Shader.TileMode;import android.graphics.drawable.Drawable;public class CircleImageDrawable extends Drawable{private Paint mPaint;private int mWidth;private Bitmap mBitmap ; public CircleImageDrawable(Bitmap bitmap){mBitmap = bitmap ; BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP,TileMode.CLAMP);mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setShader(bitmapShader);mWidth = Math.min(mBitmap.getWidth(), mBitmap.getHeight());}@Overridepublic void draw(Canvas canvas){canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2, mPaint);}@Overridepublic int getIntrinsicWidth(){return mWidth;}@Overridepublic int getIntrinsicHeight(){return mWidth;}@Overridepublic void setAlpha(int alpha){mPaint.setAlpha(alpha);}@Overridepublic void setColorFilter(ColorFilter cf){mPaint.setColorFilter(cf);}@Overridepublic int getOpacity(){return PixelFormat.TRANSLUCENT;}}

一樣出奇的簡單,再看一眼:


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

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="MessageStatus">        <attr name="state_message_readed" format="boolean" />    </declare-styleable></resources>


2、繼承Item的容器

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

package com.zhy.view;import com.zhy.sample.drawable.R;import android.content.Context;import android.util.AttributeSet;import android.widget.RelativeLayout;public class MessageListItem extends RelativeLayout{private static final int[] STATE_MESSAGE_READED = { R.attr.state_message_readed };private boolean mMessgeReaded = false;public MessageListItem(Context context, AttributeSet attrs){super(context, attrs);}public void setMessageReaded(boolean readed){if (this.mMessgeReaded != readed){mMessgeReaded = readed;refreshDrawableState();}}@Overrideprotected int[] onCreateDrawableState(int extraSpace){if (mMessgeReaded){final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);mergeDrawableStates(drawableState, STATE_MESSAGE_READED);return drawableState;}return super.onCreateDrawableState(extraSpace);}}

代碼不複雜,聲明了一個STATE_MESSAGE_READED,然後在mMessgeReaded=true的情況下,通過onCreateDrawableState方法,加入我們自訂的狀態。

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

 @Override    protected int[] onCreateDrawableState(int extraSpace) {        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);        if (isChecked()) {            mergeDrawableStates(drawableState, CHECKED_STATE_SET);        }        return drawableState;    }

3、使用

布局檔案:

<com.zhy.view.MessageListItem xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="50dp"    android:background="@drawable/message_item_bg" >    <ImageView        android:id="@+id/id_msg_item_icon"        android:layout_width="30dp"        android:src="@drawable/message_item_icon_bg"        android:layout_height="wrap_content"        android:duplicateParentState="true"        android:layout_alignParentLeft="true"        android:layout_centerVertical="true"      />    <TextView        android:id="@+id/id_msg_item_text"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_centerVertical="true"        android:layout_toRightOf="@id/id_msg_item_icon" /></com.zhy.view.MessageListItem>

很簡單,一個表徵圖,一個文本;

Activity

package com.zhy.sample.drawable;import com.zhy.view.MessageListItem;import android.app.ListActivity;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ArrayAdapter;import android.widget.TextView;public class CustomStateActivity extends ListActivity{private Message[] messages = new Message[] {new Message("Gas bill overdue", true),new Message("Congratulations, you‘ve won!", true),new Message("I love you!", false),new Message("Please reply!", false),new Message("You ignoring me?", false),new Message("Not heard from you", false),new Message("Electricity bill", true),new Message("Gas bill", true), new Message("Holiday plans", false),new Message("Marketing stuff", false), };@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);getListView().setAdapter(new ArrayAdapter<Message>(this, -1, messages){private LayoutInflater mInflater = LayoutInflater.from(getContext());@Overridepublic View getView(int position, View convertView, ViewGroup parent){if (convertView == null){convertView = mInflater.inflate(R.layout.item_msg_list,parent, false);}MessageListItem messageListItem = (MessageListItem) convertView;TextView tv = (TextView) convertView.findViewById(R.id.id_msg_item_text);tv.setText(getItem(position).message);messageListItem.setMessageReaded(getItem(position).readed);return convertView;}});}}

代碼很簡單,但是可以看到,我們需要在getView裡面中去使用調用setMessageReaded方法,當然了其他的一些狀態,肯定也要手動觸發,比如在ACTION_DOWN中觸發pressed等。請勿糾結咋沒有使用ViewHolder什麼的,自己添加下就行。

本例參考自:Example from github 

4、提升我們的UI Perfermance 

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

大家看這樣一個:


布局檔案:

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/app_background"    android:padding="8dp" >    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:layout_marginBottom="24dp"        android:src="@drawable/logo" />    <LinearLayout        android:layout_width="match_parent"        android:layout_height="48dp"        android:layout_gravity="bottom"        android:orientation="horizontal" >        <Button            android:layout_width="0dp"            android:layout_height="fill_parent"            android:layout_weight="1"            android:text="@string/sign_up" />        <Button            android:layout_width="0dp"            android:layout_height="fill_parent"            android:layout_weight="1"            android:text="@string/sign_in" />    </LinearLayout></FrameLayout>

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

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


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

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

<?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android="http://schemas.android.com/apk/res/android" >    <item>        <shape android:shape="rectangle" >            <solid android:color="@color/app_background" />        </shape>    </item>    <item android:bottom="48dp">        <bitmap            android:gravity="center"            android:src="@drawable/logo" />    </item></layer-list>

ok,這個drawable是設定了我們的背景和logo;

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

<?xml version="1.0" encoding="utf-8"?><resources>    <style        name="Theme.Default.NoActionBar"        parent="@android:style/Theme.Holo.Light.NoActionBar" >        <item name="android:windowBackground">@drawable/login</item>    </style></resources>

3、設定到Activity上:

<activityandroid:name="LoginActivity"android:theme="@style/Theme.Default.NoActionBar">

Ok,這樣不僅最小化了我們的layout,現在我們的layout裡面只有一個LinearLayout和兩個按鈕;並且提升了使用者體驗,現在使用者的直觀效果時:


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


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


源碼點擊下載


---------------------------------------------------------------------------------

群號:423372824

博主部分視頻已經上線,如果你不喜歡枯燥的文本,請猛戳(初錄,期待您的支援):

視頻目錄位址:本人錄製的視頻教程

公眾號請掃描:

--------------------------------------------------------------------------------------------------------










原文連結本文由豆約翰部落格備份專家遠程一鍵發布

AndroidDrawable那些不為人知的高效用法(轉載)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.