在开发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即可运行查看效果啦~~

Android 自定义View修炼-Android开发之自定义View开发及实例详解的更多相关文章

  1. (转载)实例详解Android快速开发工具类总结

    实例详解Android快速开发工具类总结 作者:LiJinlun 字体:[增加 减小] 类型:转载 时间:2016-01-24我要评论 这篇文章主要介绍了实例详解Android快速开发工具类总结的相关 ...

  2. 转: Android 软件开发之如何使用Eclipse Debug调试程序详解(七)

    转自: http://www.uml.org.cn/mobiledev/201110092.asp Android 软件开发之如何使用Eclipse Debug调试程序详解(七)   发布于2011- ...

  3. 自定义一个更好用的SwipeRefreshLayout(弹力拉伸效果详解)(转载)

    转自: 自定义一个更好用的SwipeRefreshLayout(弹力拉伸效果详解) 前言 熟悉SwipeRefreshLayout的同学一定知道,SwipeRefreshLayout是android里 ...

  4. Android基础夯实--重温动画(五)之属性动画 ObjectAnimator详解

    只有一种真正的英雄主义 一.摘要 ObjectAnimator是ValueAnimator的子类,它和ValueAnimator一样,同样具有计算属性值的功能,但对比ValueAnimator,它会更 ...

  5. Cocos2d-x 3.X手游开发实例详解

    Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...

  6. 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权

    原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...

  7. 【eclipse插件开发实战】 Eclipse插件开发5——时间插件Timer开发实例详解

    Eclipse插件开发5--时间插件Timer开发实例详解 这里做的TimeHelper插件设定为在菜单栏.工具栏提供快捷方式,需要在相应地方设置扩展点,最后弹出窗体显示时间. 在上一篇文章里创建好了 ...

  8. 免费的HTML5连载来了《HTML5网页开发实例详解》连载(二)

    最近新浪.百度.腾讯.京东.大众点评.淘宝等流行的网站都加大了招聘HTML5的力度,HTML5开发人员成了抢手货,本次连载的是由大众点评前端工程师和一淘网前端工程师基情奉献的<HTML5网页开发 ...

  9. 免费的HTML5连载来了《HTML5网页开发实例详解》连载(六)媒体查询

    响应式设计的另一个重要技术手段是媒体查询.如果只是简单的设计一个流式布局系统,那么可以保证每个网格按比例的放大和缩小,但有可能会使得在小屏幕下(如手机设备)网格太小而严重影响阅读,这样的设计称不上响应 ...

  10. 免费的HTML5连载来了《HTML5网页开发实例详解》连载(五)图解通过Fiddler加速开发

    Fiddler是Windows底下最强大的请求代理调试工具,监控任何浏览器的HTTP/HTTPS流量,窜改客户端请求和服务器响应,解密HTTPS Web会话,图4.44为Fiddler原理示意图. 图 ...

随机推荐

  1. uva 12526 - Cellphone Typing

    字典树,可惜比赛的时候有两句话写倒了: 害得我调了一个小时: 今天不宜做题 = = 代码: #include<cstdio> #include<cstring> #define ...

  2. 【BZOJ 3476】 线段树===

    59  懒惰的奶牛贝西所在的牧场,散落着 N 堆牧草,其中第 i 堆牧草在 ( Xi,Yi ) 的位置,数量有 Ai 个单位.贝西从家移动到某一堆牧草的时候,只能沿坐标轴朝正北.正东.正西.正南这四个 ...

  3. 灰度图像--图像增强 直方图均衡化(Histogram equalization)

    灰度图像--图像增强 直方图均衡化(Histogram equalization) 转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些 ...

  4. 学以致用:让visualstudio爱上sublime

    前言: 经常在vs中工作,但是一些编辑工作却非常喜欢sublime的方式,如果你也是,那我们来当媒婆吧,哈哈. 准备:       Visualstudio一枚        Sublime一枚   ...

  5. -_-#【JS】element.click()

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  6. 【转】如何单独编译Android源代码中的模块--不错

    原文网址:http://blog.csdn.net//article/details/6566662/ 第一次下载好Android源代码工程后,我们通常是在Android源代码工程目录下执行make命 ...

  7. Ajax在PC端可以使用但在手机端不能使用

    ajax代码如下,仔细看看也没有什么错,电脑端可以调用并正确的返回结果,手机端却不可以 function GetSumData(time) { var device = "Phone&quo ...

  8. python三元运算符

    在c.php里面,都有三元运算符,如:   a = b?c:d 意思是 b 的运算结果如果是True,那么整个表达式a就等于c,反之如果b的运算结果是False,那么a就等于d. 这样写起来简洁又高效 ...

  9. MVC和Web API 过滤器Filter [转]

    ASP.NET MVC 支持以下类型的操作筛选器: ·        授权筛选器.这些筛选器用于实现IAuthorizationFilter和做出关于是否执行操作方法(如执行身份验证或验证请求的属性) ...

  10. hive中sql解析出对应表和字段的实现

    import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map ...