Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 自定義View背景動畫 流程簡讀 (2)

Android 自定義View背景動畫 流程簡讀 (2)

編輯:關於Android編程

這一篇主要根據上一篇的大致說明,我相信如果看完這一篇,對開發自定義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屬性就知道了.

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved