在開發Android應用的過程中,難免需要自定義View,其實自定義View不難,只要了解原理,實現起來就沒有那麼難。
其主要原理就是繼承View,重寫構造方法、onDraw,(onMeasure)等函數。我自定義了個虛擬按鍵的View,
首先得自己寫個自定義View類,這裡我寫了個VirtualKeyView類,繼承自View類,實現了構造方法以及onDraw方法,以及實現了鍵盤按鍵的接口事件,實現代碼如下:
復制代碼
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> 一樣,只需寫全路徑即可:本例中如下:
在main.xml的UI布局文件中加入:
復制代碼
<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方法實現如下:
復制代碼
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;
}
}