Android 自定義View修煉-Android開發之自定義View開發及實例詳解

在開發Android應用的過程當中,不免須要自定義View,其實自定義View不難,只要瞭解原理,實現起來就沒有那麼難。java

其主要原理就是繼承View,重寫構造方法、onDraw,(onMeasure)等函數。我自定義了個虛擬按鍵的View,效果圖以下:android

首先得本身寫個自定義View類,這裏我寫了個VirtualKeyView類,繼承自View類,實現了構造方法以及onDraw方法,以及實現了鍵盤按鍵的接口事件,實現代碼以下:canvas

package com.czm.customview;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

public class VirtualKeyView extends View{

    private static final boolean bDebug = true;
    private static final String TAG = VirtualKeyView.class.getSimpleName();
    
    private Context mContext;
    
    private Bitmap bmpRound;
    private Bitmap bmpRound_press;
    private Bitmap bmpOk;
    private Bitmap bmpOk_press;

    private int mWidth;//真實寬度
    
    private int widthBg = 584;
    //private int widthItemBg = 292;
    private int widthMid = 220;//中心寬度
    
    private int cir_Centre_X = 292;//圓心位置
    private int cir_Centre_Y = 292;//圓心位置
    private int bigRadius = 292;
    private int smallRadius = 110;
    private int smallCir_X = 182;
    private int smallCir_Y = 182;
    private float scale = 1.0f;
    
    private boolean isInit = false;
    
    private int inputPress = -1;//顯示點擊了哪一個鍵
    

    public VirtualKeyView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        mContext = context;
    }
    
    public VirtualKeyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        mContext = context;
    }

private void initData() {
        
        mWidth = getWidth();
        scale = mWidth*1.0f/widthBg;
        
        cir_Centre_X = (int) (cir_Centre_X*scale);
        cir_Centre_Y = (int) (cir_Centre_Y*scale);
        bigRadius = (int) (bigRadius*scale);
        smallRadius = (int) (smallRadius*scale);
        smallCir_X = (int) (smallCir_X*scale);
        smallCir_Y = (int) (smallCir_Y*scale);
        
        initView();
        isInit = true;
    }
    
    private void initView() {
        if(mWidth == widthBg){
            bmpRound = Utils.decodeCustomRes(mContext, R.drawable.controller_round);
            bmpRound_press= Utils.decodeCustomRes(mContext, R.drawable.controller_round_bg_pressed);
            bmpOk= Utils.decodeCustomRes(mContext, R.drawable.controller_ok);
            bmpOk_press= Utils.decodeCustomRes(mContext, R.drawable.controller_ok_pressed);
        } else {
            int mid = (int) (widthMid*scale);
            Bitmap bitmapTmp = Utils.decodeCustomRes(mContext, R.drawable.controller_round);
            bmpRound = Bitmap.createScaledBitmap(bitmapTmp, mWidth, mWidth, true);
            bitmapTmp.recycle();
            bitmapTmp = null;
            
            bitmapTmp = Utils.decodeCustomRes(mContext, R.drawable.controller_round_bg_pressed);
            bmpRound_press = Bitmap.createScaledBitmap(bitmapTmp, mWidth, mWidth, true);
            bitmapTmp.recycle();
            bitmapTmp = null;

            bitmapTmp = Utils.decodeCustomRes(mContext, R.drawable.controller_ok);
            bmpOk = Bitmap.createScaledBitmap(bitmapTmp, mid, mid, true);
            bitmapTmp.recycle();
            bitmapTmp = null;
            
            bitmapTmp = Utils.decodeCustomRes(mContext, R.drawable.controller_ok_pressed);
            bmpOk_press = Bitmap.createScaledBitmap(bitmapTmp, mid, mid, true);
            bitmapTmp.recycle();
            bitmapTmp = null;
        }
        System.gc();
    }
    
    public void  recycle() {
        if (bmpRound != null && !bmpRound.isRecycled()) {
            bmpRound.recycle();
            bmpRound = null;
        }
        if (bmpRound_press != null && !bmpRound_press.isRecycled()) {
            bmpRound_press.recycle();
            bmpRound_press = null;
        }
        if (bmpOk != null && !bmpOk.isRecycled()) {
            bmpOk.recycle();
            bmpOk = null;
        }
        if (bmpOk_press != null && !bmpOk_press.isRecycled()) {
            bmpOk_press.recycle();
            bmpOk_press = null;
        }
        System.gc();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        if(!isInit){
            initData();
        }
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
                | Paint.FILTER_BITMAP_FLAG));// 抗鋸齒
        drawCir(canvas);
    }
    
    private void drawCir(Canvas canvas) {
        switch (inputPress) {
        case -1:
            //無按鍵
            canvas.drawBitmap(bmpRound, 0, 0, null);
            canvas.drawBitmap(bmpOk, smallCir_X , smallCir_Y, null);
            break;
        case KeyEvent.KEYCODE_DPAD_CENTER:
            //中心Ok鍵
            canvas.drawBitmap(bmpRound, 0, 0, null);
            canvas.drawBitmap(bmpOk_press, smallCir_X , smallCir_Y, null);
            break;
        case KeyEvent.KEYCODE_DPAD_UP:
            //
            drawBg(canvas,180);
            canvas.drawBitmap(bmpOk, smallCir_X , smallCir_Y, null);
            break;
        case KeyEvent.KEYCODE_DPAD_DOWN:
            //
            drawBg(canvas,0);
            canvas.drawBitmap(bmpOk, smallCir_X , smallCir_Y, null);
            break;
        case KeyEvent.KEYCODE_DPAD_LEFT:
            //
            drawBg(canvas,90);
            canvas.drawBitmap(bmpOk, smallCir_X , smallCir_Y, null);
            break;
        case KeyEvent.KEYCODE_DPAD_RIGHT:
            //
            drawBg(canvas,270);
            canvas.drawBitmap(bmpOk, smallCir_X , smallCir_Y, null);
            break;
            
        default:
            break;
        }
    }
    
    private void drawBg(Canvas canvas,int rotate) {
        canvas.save();    //保存canvas狀態
        canvas.rotate(rotate,cir_Centre_X,cir_Centre_Y);
        canvas.drawBitmap(bmpRound_press, 0, 0, null);//這裏畫的是旋轉後的
        canvas.restore();// 恢復canvas狀態
    }
    
    public boolean onTouchEvent(MotionEvent event) {
        if (bDebug)
            Log.d(TAG, "event.getAction() = " + event.getAction());
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
                // 計算圓選擇的哪一個字母
                int x1 = (int) (event.getX() - cir_Centre_X);
                int y1 = (int) (event.getY() - cir_Centre_Y);
                if (bDebug)
                    Log.d(TAG, "x= " + x1 + ", y = " + y1);
                int x2 = x1 * x1;
                int y2 = y1 * y1;
                int bigRadius2 = bigRadius * bigRadius;
                int smallRadius2 = smallRadius * smallRadius;
                if (x2 + y2 < bigRadius2) {
                    // 表示在畫圓形以內,纔有繼續計算的必要
                    if (x2 + y2 < smallRadius2) {
                        // 若是再小圓內
                        setOnKeyDown(KeyEvent.KEYCODE_DPAD_CENTER);
                    } else if (y1 > x1) {
                        if (y1 > -x1) {
                            //
                            setOnKeyDown(KeyEvent.KEYCODE_DPAD_DOWN);
                        } else {
                            //
                            setOnKeyDown(KeyEvent.KEYCODE_DPAD_LEFT);
                        }
                    } else if (y1 < x1) {
                        if (y1 > -x1) {
                            //
                            setOnKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT);
                        } else {
                            //
                            setOnKeyDown(KeyEvent.KEYCODE_DPAD_UP);
                        }
                    }
                }
                // isShowCir = false;
                postInvalidate();
            return true;
        }

        if (event.getAction() == MotionEvent.ACTION_UP) {
                // 計算圓選擇的哪一個字母

                int x1 = (int) (event.getX() - cir_Centre_X);
                int y1 = (int) (event.getY() - cir_Centre_Y);
                if (bDebug)
                    Log.d(TAG, "x= " + x1 + ", y = " + y1);
                int x2 = x1 * x1;
                int y2 = y1 * y1;
                int bigRadius2 = bigRadius * bigRadius;
                int smallRadius2 = smallRadius * smallRadius;
                if (x2 + y2 < bigRadius2) {
                    // 表示在畫圓形以內,纔有繼續計算的必要
                    if (x2 + y2 < smallRadius2) {
                        // 若是再小圓內
                        setOnKeyUp(KeyEvent.KEYCODE_DPAD_CENTER);
                    } else if (y1 > x1) {
                        if (y1 > -x1) {
                            //
                            setOnKeyUp(KeyEvent.KEYCODE_DPAD_DOWN);
                        } else {
                            //
                            setOnKeyUp(KeyEvent.KEYCODE_DPAD_LEFT);
                        }
                    } else if (y1 < x1) {
                        if (y1 > -x1) {
                            //
                            setOnKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT);
                        } else {
                            //
                            setOnKeyUp(KeyEvent.KEYCODE_DPAD_UP);
                        }
                    }

                }
                postInvalidate();
        }
        return true;
    }
    
    private void setOnKeyDown(int keyCode) {
        if(bDebug)
            Log.d(TAG, "keyCode = "+keyCode);
        inputPress = keyCode;
        if(myIntputCallBack != null){
            myIntputCallBack.intputDown(keyCode);
        }
    }
    
    private void setOnKeyUp(int keyCode) {
        inputPress = -1;
        if(myIntputCallBack != null){
            myIntputCallBack.intputUp(keyCode);
        }
    }
    
    /******************* 輸入回調函數 ********************/
    private IntputCallBack myIntputCallBack = null;

    public interface IntputCallBack {
        void intputDown(int keyCode);
        void intputUp(int keyCode);
    }

    public void setOnInputCallBack(IntputCallBack myIntputCallBack) {
        this.myIntputCallBack = myIntputCallBack;
    }
}

在xml佈局文件裏面如何使用上面自定義的View視圖控件呢?很簡單就是 和 使用<TextView> 同樣,只需寫全路徑便可:本例中以下:app

在main.xml的UI佈局文件中加入:ide

<com.czm.customview.VirtualKeyView
        android:id="@+id/virtualKeyView"
        android:layout_marginTop="20dp"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:layout_centerHorizontal="true"
         />

在看看 如何在java類文件中使用 該 VirtualKeyView視圖控件呢,這個時候就和 普通的TextView視圖同樣使用了,本例中函數

在MainActivity中使用以下:佈局

package com.czm.customview;


import com.czm.customview.VirtualKeyView.IntputCallBack;

import android.os.Bundle;
import android.app.Activity;
import android.view.KeyEvent;
import android.widget.Toast;

public class MainActivity extends Activity {

    private VirtualKeyView virtualKeyView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        virtualKeyView = (VirtualKeyView) findViewById(R.id.virtualKeyView);

        virtualKeyView.setOnInputCallBack(new IntputCallBack() {

            @Override
            public void intputUp(int keyCode) {

            }

            @Override
            public void intputDown(int keyCode) {
                if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                    showToast("您按了 上 鍵");
                } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                    showToast("您按了 下 鍵");
                } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
                    showToast("您按了 左 鍵");
                } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
                    showToast("您按了 右 鍵");
                } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
                    showToast("您按了 OK 鍵");
                }

            }
        });

    }
    private void showToast(String msg){
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        virtualKeyView.recycle();
    }

}

Utils.java文件中的decodeCustomRes方法實現以下:post

package com.czm.customview;

import java.io.InputStream;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class Utils {
    public static Bitmap decodeCustomRes(Context c, int res) {
        InputStream is = c.getResources().openRawResource(res);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inSampleSize = 1;//表示原尺寸加載圖片,不縮放
        Bitmap bmp = BitmapFactory.decodeStream(is, null, options);
        return bmp;
    }
}

 

到此爲止,自定義的View完畢了,Ctrl+F11便可運行查看效果啦~~this

相關文章
相關標籤/搜索