Android 自訂View背景動畫 流程簡讀 <2>

來源:互聯網
上載者:User

標籤:

這一篇主要根據上一篇的大致說明,我相信如果看完這一篇,對開發自訂View將會有很大的協助,

先介紹ColorStateList和StateListDrawable兩個類:

ColorStateList說明:https://developer.android.com/reference/android/content/res/ColorStateList.html

StateListDrawable說明:https://developer.android.com/reference/android/graphics/drawable/StateListDrawable.html

這兩個共同的特點是根據狀態的變化變換View的背景,ColorStateList一般是背景顏色更新.比如:

XML file saved at res/color/button_text.xml:

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <item android:state_pressed="true"          android:color="#ffff0000"/> <!-- pressed -->    <item android:state_focused="true"          android:color="#ff0000ff"/> <!-- focused -->    <item android:color="#ff000000"/> <!-- default --></selector>

然後在布局中使用:

<Button    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:text="@string/button_text"    android:textColor="@color/button_text" />

這個地方都是android原生Button來完成解析Button_text.xml來更新父類View的背景/前景的調整,或者其他調整!這個是字型會隨著點擊變色.

如果是自訂的View,如何來設定這些操作了,下面看一看

<1> : 建立一個android studio工程:PumpKinDrawable:

主類程式:

package org.durian.pumpkindrawable;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.ImageView;import org.durian.pumpkindrawable.view.ButtonColorDrawable;import org.durian.pumpkindrawable.view.PumpKinDrawableView;public class PumpKinMainActivity extends AppCompatActivity {    private ImageView imageView1;    private ButtonColorDrawable bcdrawable;    private PumpKinDrawableView pumpkinview;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_pump_kin_main);        imageView1=(ImageView)findViewById(R.id.imagestate);        bcdrawable=new ButtonColorDrawable();        imageView1.setBackground(bcdrawable);        imageView1.setClickable(true);        pumpkinview=(PumpKinDrawableView)findViewById(R.id.pumpkinview);        pumpkinview.setClickable(true);    }}

對應布局檔案:裡面的圖片自行給一張放到drawable中

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    android:orientation="vertical"    tools:context="org.durian.pumpkindrawable.PumpKinMainActivity">    <org.durian.pumpkindrawable.view.PumpKinDrawableView        android:id="@+id/pumpkinview"        android:clickable="true"        android:layout_width="250dp"        android:layout_height="250dp" />    <ImageView        android:id="@+id/image"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@drawable/weather"/>    <ImageView        android:id="@+id/imagestate"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@drawable/weather"/></LinearLayout>

那麼自訂的View如下:

package org.durian.pumpkindrawable.view;import android.content.res.ColorStateList;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.ColorFilter;import android.graphics.Paint;import android.graphics.PixelFormat;import android.graphics.RectF;import android.graphics.drawable.Animatable;import android.graphics.drawable.Drawable;import android.util.Log;/** * Project name : PumpKinDrawable * Created by zhibao.liu on 2016/4/29. * Time : 14:58 * Email [email protected] * Action : durian */public class ButtonColorDrawable extends Drawable {    private Paint mBGPaint;    private int[] mNoAnimationColor;    private ColorStateList mColorStateList;    private int[][] btStatus;    public ButtonColorDrawable() {        mNoAnimationColor = new int[]{Color.BLUE, Color.GREEN, Color.GRAY};        mBGPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mBGPaint.setColor(Color.BLUE);        mBGPaint.setStrokeWidth(5);        mBGPaint.setStyle(Paint.Style.FILL);        mBGPaint.setAntiAlias(true);        //負號表示false,最後那個空數組呢,代表的是除開前面這兩個狀態以外的狀態,這個一定要放到最後,不信你放到第一個試試有什麼後果        btStatus = new int[][]{{-android.R.attr.state_pressed}, {android.R.attr.state_pressed}, {}};        //把前面建立好的狀態對應的顏色素組塞到這個狀態和顏色對應的隊列裡面        mColorStateList = new ColorStateList(btStatus, mNoAnimationColor);    }    /**     * Draw in its bounds (set via setBounds) respecting optional effects such     * as alpha (set via setAlpha) and color filter (set via setColorFilter).     *     * @param canvas The canvas to draw into     */    @Override    public void draw(Canvas canvas) {        android.util.Log.i("pumpkin","draw ... ");        canvas.drawRoundRect(new RectF(getBounds()), 30, 30, mBGPaint);    }    /**     * 設定為true之後,drawable才能接受控制項的狀態     *     * @return     */    @Override    public boolean isStateful() {        return true;    }    @Override    protected boolean onStateChange(int[] state) {        //當狀態改變的時候,擷取目前狀態對應的顏色,這個顏色和狀態的關係就是構造裡面設定的那個        android.util.Log.i("pumpkin","onStateChange ... ");        int currentColor = mColorStateList.getColorForState(state, Color.WHITE);        mBGPaint.setColor(currentColor);        invalidateSelf();        return true;    }    /**     * Specify an alpha value for the drawable. 0 means fully transparent, and     * 255 means fully opaque.     *     * @param alpha     */    @Override    public void setAlpha(int alpha) {    }    @Override    public void setColorFilter(ColorFilter colorFilter) {    }    @Override    public int getOpacity() {        return PixelFormat.TRANSLUCENT;    }}


這樣運行結果:

點擊前:



點擊後:

昨天我們看了View的原始碼,只要View被點擊就會產生KeyEvent,最終調用:

protected void drawableStateChanged() {        Drawable d = mBackground;        if (d != null && d.isStateful()) {            d.setState(getDrawableState());        }    }

然後就會調用:

public boolean setState(final int[] stateSet) {        if (!Arrays.equals(mStateSet, stateSet)) {            mStateSet = stateSet;            return onStateChange(stateSet);        }        return false;    }


從而我們點擊UI的時候就會執行ButtonColorDrawable的:

protected boolean onStateChange(int[] state)

在這個方法裡面如果需要更新UI,則:

invalidateSelf();


public void invalidateSelf() {        final Callback callback = getCallback();        if (callback != null) {            callback.invalidateDrawable(this);        }    }

在這裡面回調調用重新整理View視圖.
重新整理就開始調用draw方法:

@Override    public void draw(Canvas canvas) {        android.util.Log.i("pumpkin","draw ... ");        canvas.drawRoundRect(new RectF(getBounds()), 30, 30, mBGPaint);    }

從而實現背景顏色變化.

下面來看看StateListDrawable 如何?背景變化的:在上面的工程添加下面的類:

package org.durian.pumpkindrawable.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.drawable.Drawable;import android.graphics.drawable.StateListDrawable;import android.os.AsyncTask;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.View;import org.durian.pumpkindrawable.R;/** * Project name : PumpKinDrawable * Created by zhibao.liu on 2016/4/29. * Time : 17:09 * Email [email protected] * Action : durian */public class PumpKinDrawableView extends View {    private Context mContext;    private Drawable mBackground;    private boolean mCanSizeChanged=true;    private Paint mPaint;    public PumpKinDrawableView(Context context) {        super(context);        initView(context);    }    public PumpKinDrawableView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView(context);    }    public PumpKinDrawableView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        initView(context);    }    public PumpKinDrawableView(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context);    }    public void initView(Context context){        mContext=context;        StateListDrawable statelistDrawable = new StateListDrawable();        int pressed = android.R.attr.state_pressed;        int windowfocused = android.R.attr.state_window_focused;        int enabled = android.R.attr.state_enabled;        int stateFoucesd = android.R.attr.state_focused;        statelistDrawable.addState(                new int[] { pressed, windowfocused },                mContext.getResources().getDrawable(                        R.drawable.deskclock));        statelistDrawable.addState(new int[] { -pressed, windowfocused },                mContext.getResources()                        .getDrawable(R.drawable.weather));        mBackground = statelistDrawable;        mBackground.setCallback(this);        setBackgroundDrawable(null);        mPaint=new Paint();        mPaint.setColor(Color.YELLOW);    }    @Override    protected void drawableStateChanged() {        super.drawableStateChanged();        android.util.Log.i("pumpkin","drawableStateChanged ... ");        Drawable d = mBackground;        if (d != null && d.isStateful()) {            d.setState(getDrawableState());//            drawbackground();        }    }    @Override    protected boolean verifyDrawable(Drawable who) {        android.util.Log.i("pumpkin","verifyDrawable ... ");        return who == mBackground || super.verifyDrawable(who);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        android.util.Log.i("pumpkin","onDraw ... ");    }    @Override    public void draw(Canvas canvas) {        super.draw(canvas);        android.util.Log.i("pumpkin","draw ... ");        if (mBackground != null) {            if (mCanSizeChanged) {                // 設定邊界範圍                mBackground.setBounds(0, 0, getRight() - getLeft(), getBottom()                        - getTop());                mCanSizeChanged = false;            }            if ((getScrollX() | getScrollY()) == 0) // 是否位移            {                mBackground.draw(canvas); // 繪製目前狀態對應的圖片                //canvas.drawCircle(250, 250, radio, mPaint);            } else {                canvas.translate(getScrollX(), getScrollY());                mBackground.draw(canvas); // 繪製目前狀態對應的圖片                canvas.translate(-getScrollX(), -getScrollY());            }        }    }    /*int radio=0;    int speechexpand=1;    boolean drawstatus=false;    public void drawbackground(){        if(drawstatus) {        }else{            drawstatus=true;            return;        }        radio=0;        if(task!=null){            if(!task.isCancelled()){                task.cancel(true);            }        }        task=new Task();        task.execute();    }    private Handler mHandler=new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            invalidate();        }    };    private Task task;    private class Task extends AsyncTask{        @Override        protected Object doInBackground(Object[] params) {            for(int i=0;i<30;i++) {                radio += speechexpand;                try {                    Thread.sleep(1);                } catch (InterruptedException e) {                    e.printStackTrace();                }                mHandler.sendEmptyMessage(0);            }            return null;        }    };*/}

同樣,點擊後

->drawableStateChanged

if (d != null && d.isStateful()) {            d.setState(getDrawableState());//            drawbackground();        }
注意這裡的setState引髮狀態變化,從而引發後面的View重新整理操作,源碼見上面的.

->verifyDrawable

->ondraw
->draw

經過這一路流程,View實現了背景重新整理,運行效果:

點擊前:


點擊後:


這一篇一定要注意的地方是,所有的一切都是以程式邏輯方式更新背景的動畫或者顏色,以及看清平時配置到xml中的背景是如何在程式中操縱的,

所以上面的圖片都是背景,觀者可以再設定imageView中xml的src屬性就知道了.








Android 自訂View背景動畫 流程簡讀 <2>

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.