在开发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; } }
到此为止,自定义的View完毕了,Ctrl+F11即可运行查看效果啦~~