Android自定义简单的View
来源:互联网 发布:庞博 知乎 编辑:程序博客网 时间:2024/05/01 06:45
Android自定义View实现很简单
继承View,重写构造函数、onDraw,(onMeasure)等函数。
如果自定义的View需要有自定义的属性,需要在values下建立attrs.xml。在其中定义你的属性。
在使用到自定义View的xml布局文件中需要加入xmlns:前缀="http://schemas.android.com/apk/res/你的自定义View所在的包路径".
在使用自定义属性的时候,使用前缀:属性名,如my:textColor="#FFFFFFF"。、
format值的格式
实例:
- package demo.view.my;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Paint.Style;
- import android.util.AttributeSet;
- import android.view.View;
- /**
- * 这个是自定义的TextView.
- * 至少需要重载构造方法和onDraw方法
- * 对于自定义的View如果没有自己独特的属性,可以直接在xml文件中使用就可以了
- * 如果含有自己独特的属性,那么就需要在构造函数中获取属性文件attrs.xml中自定义属性的名称
- * 并根据需要设定默认值,放在在xml文件中没有定义。
- * 如果使用自定义属性,那么在应用xml文件中需要加上新的schemas,
- * 比如这里是xmlns:my="http://schemas.android.com/apk/res/demo.view.my"
- * 其中xmlns后的“my”是自定义的属性的前缀,res后的是我们自定义View所在的包
- * @author Administrator
- *
- */
- public class MyView extends View {
- Paint mPaint; //画笔,包含了画几何图形、文本等的样式和颜色信息
- public MyView(Context context) {
- super(context);
- }
- public MyView(Context context, AttributeSet attrs){
- super(context, attrs);
- mPaint = new Paint();
- //TypedArray是一个用来存放由context.obtainStyledAttributes获得的属性的数组
- //在使用完成后,一定要调用recycle方法
- //属性的名称是styleable中的名称+“_”+属性名称
- TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);
- int textColor = array.getColor(R.styleable.MyView_textColor, 0XFF00FF00); //提供默认值,放置未指定
- float textSize = array.getDimension(R.styleable.MyView_textSize, 36);
- mPaint.setColor(textColor);
- mPaint.setTextSize(textSize);
- array.recycle(); //一定要调用,否则这次的设定会对下次的使用造成影响
- }
- public void onDraw(Canvas canvas){
- super.onDraw(canvas);
- //Canvas中含有很多画图的接口,利用这些接口,我们可以画出我们想要的图形
- //mPaint = new Paint();
- //mPaint.setColor(Color.RED);
- mPaint.setStyle(Style.FILL); //设置填充
- canvas.drawRect(10, 10, 100, 100, mPaint); //绘制矩形
- mPaint.setColor(Color.BLUE);
- canvas.drawText("我是被画出来的", 10, 120, mPaint);
- }
- }
相应的属性文件:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <declare-styleable name="MyView">
- <attr name="textColor" format="color"/>
- <attr name="textSize" format="dimension"/>
- </declare-styleable>
- </resources>
在布局文件中的使用:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:my="http://schemas.android.com/apk/res/demo.view.my"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <demo.view.my.MyView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- my:textColor="#FFFFFFFF"
- my:textSize="22dp"
- />
- </LinearLayout>
Matrix ,中文里叫矩阵,高等数学里有介绍,在图像处理方面,主要是用于平面的缩放、平移、旋转等操作。
首先介绍一下矩阵运算。加法和减法就不用说了,太简单了,对应位相加就好。图像处理,主要用到的是乘法 。下面是一个乘法的公式:
在 Android 里面, Matrix 由 9 个 float 值构成,是一个 3*3 的矩阵。如下图。
没专业工具,画的挺难看。解释一下,上面的 sinX 和 cosX ,表示旋转角度的 cos 值和 sin 值,注意,旋转角度是按顺时针方向计算的。 translateX 和 translateY 表示 x 和 y 的平移量。 scale 是缩放的比例, 1 是不变, 2 是表示缩放 1/2 ,这样子。
- 1. public class MyView extends View {
- 2.
- 3. private Bitmap mBitmap;
- 4.
- 5. private Matrix mMatrix = new Matrix();
- 6.
- 7. public MyView(Context context) {
- 8.
- 9. super (context);
- 10.
- 11. initialize();
- 12.
- 13. }
- 14.
- 15. private void initialize() {
- 16.
- 17. mBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.show)).getBitmap();
- 18.
- 19. float cosValue = ( float ) Math.cos(-Math.PI/ 6 );
- 20.
- 21. float sinValue = ( float ) Math.sin(-Math.PI/ 6 );
- 22.
- 23. mMatrix.setValues(
- 24.
- 25. new float []{
- 26.
- 27. cosValue, -sinValue, 100 ,
- 28.
- 29. sinValue, cosValue, 100 ,
- 30.
- 31. 0 , 0 , 2 });
- 32.
- 33. }
- 34.
- 35. @Override protected void onDraw(Canvas canvas) {
- 36.
- 37. // super.onDraw(canvas); //当然,如果界面上还有其他元素需要绘制,只需要将这句话写上就行了。
- 38.
- 39. canvas.drawBitmap(mBitmap, mMatrix, null );
- 40.
- 41. }
- 42.
- 43. }
运行结果如下:
以左上角为顶点,缩放一半,逆时针旋转30度,然后沿x轴和y轴分别平移50个像素,代码 里面写的是100,为什么是平移50呢,因为缩放了一半。
大家可以自己设置一下Matrix的值,或者尝试一下两个Matrix相乘,得到的值设置进去,这样才能对Matrix更加熟练。
这里讲的直接赋值的方式也许有点不好理解,不过还好, andrid 提供了对矩阵的更方便的方法
Matrix的操作,总共分为translate(平移),rotate(旋转),scale(缩放)和skew(倾斜)四种,每一种变换在
Android的API里都提供了set, post和pre三种操作方式,除了translate,其他三种操作都可以指定中心点。
set是直接设置Matrix的值,每次set一次,整个Matrix的数组都会变掉。
post是后乘,当前的矩阵乘以参数给出的矩阵。可以连续多次使用post,来完成所需的整个变换。例如,要将一个图片旋
转30度,然后平移到(100,100)的地方,那么可以这样做:
- # Matrix m = new Matrix();
- #
- # m.postRotate(30 );
- #
- # m.postTranslate(100 , 100 );
这样就达到了想要的效果。
pre是前乘,参数给出的矩阵乘以当前的矩阵。所以操作是在当前矩阵的最前面发生的。例如上面的例子,如果用pre的话
,就要这样:
- 1. Matrix m = new Matrix();
- 2.
- 3. m.preTranslate(100 , 100 );
- 4.
- 5. m.preRotate(30 );
旋转、缩放和倾斜都可以围绕一个中心点来进行,如果不指定,默认情况下,是围绕(0,0)点来进行。
- 1. package chroya.demo.graphics;
- 2.
- 3. import android.content.Context;
- 4. import android.graphics.Bitmap;
- 5. import android.graphics.Canvas;
- 6. import android.graphics.Matrix;
- 7. import android.graphics.Rect;
- 8. import android.graphics.drawable.BitmapDrawable;
- 9. import android.util.DisplayMetrics;
- 10. import android.view.MotionEvent;
- 11. import android.view.View;
- 12.
- 13. public class MyView extends View {
- 14.
- 15. private Bitmap mBitmap;
- 16. private Matrix mMatrix = new Matrix();
- 17.
- 18. public MyView(Context context) {
- 19. super (context);
- 20. initialize();
- 21. }
- 22.
- 23. private void initialize() {
- 24.
- 25. Bitmap bmp = ((BitmapDrawable)getResources().getDrawable(R.drawable.show)).getBitmap();
- 26. mBitmap = bmp;
- 27. /*首先,将缩放为100*100。这里scale的参数是比例。有一点要注意,如果直接用100/
- 28. bmp.getWidth()的话,会得到0,因为是整型相除,所以必须其中有一个是float型的,直接用100f就好。*/
- 29. mMatrix.setScale(100f/bmp.getWidth(), 100f/bmp.getHeight());
- 30. //平移到(100,100)处
- 31. mMatrix.postTranslate(100 , 100 );
- 32. //倾斜x和y轴,以(100,100)为中心。
- 33. mMatrix.postSkew(0 .2f, 0 .2f, 100 , 100 );
- 34. }
- 35.
- 36. @Override protected void onDraw(Canvas canvas) {
- 37. // super.onDraw(canvas); //如果界面上还有其他元素需要绘制,只需要将这句话写上就行了。
- 38.
- 39. canvas.drawBitmap(mBitmap, mMatrix, null );
- 40. }
- 41. }
需要绘制到画布上,这就得用Canvas类了。在Android中既然把Canvas当做画布,那么就可以在画布上绘制我们想要的任何东西。除了在画布上绘制之外,还需要设置一些关于画布的属性,比如,画布的颜色、尺寸等。下面来分析Android中Canvas有哪些功能,Canvas提供了如下一些方法:
Canvas(): 创建一个空的画布,可以使用setBitmap()方法来设置绘制具体的画布。
Canvas(Bitmap bitmap): 以bitmap对象创建一个画布,则将内容都绘制在bitmap上,因此bitmap不得为null。
Canvas(GL gl): 在绘制3D效果时使用,与OpenGL相关。
drawColor: 设置Canvas的背景颜色。
setBitmap: 设置具体画布。
clipRect: 设置显示区域,即设置裁剪区。
isOpaque:检测是否支持透明。
rotate: 旋转画布
setViewport: 设置画布中显示窗口。
skew: 设置偏移量。
上面列举了几个常用的方法。在游戏开发中,我们可能需要对某个精灵执行旋转、缩放和一些其它操作。我们可以通过旋转画布来实现,但是旋转画布时会旋转画布上的所有对象,而我们只是需要旋转其中的一个,这时就需要用到save 方法来锁定需要操作的对象,在操作之后通过 restore 方法来解除锁定,下面我们先来看一下运行效果吧。
我们对左边的矩形执行了旋转操作,而没有旋转右边的矩形,由于我们设置了裁剪区域,因此左边的矩形只能看到一部分,下面让我们来看看代码 这里我只贴出了我们自己的 View类 GameView Activity类不贴出了 就在里边 new 一个GameView类 然后设置布局 然后main.xml也不贴出来了 因为它根本就没用到。
Java代码
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
public class GameView extends View implements Runnable {
/* 声明Paint对象 */
private Paint mPaint = null;
public GameView(Context context) {
super(context);
/* 构建对象 */
mPaint = new Paint();
/* 开启线程 */
new Thread(this).start();
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
/* 设置画布的颜色 */
canvas.drawColor(Color.BLACK);
/* 设置取消锯齿效果 */
mPaint.setAntiAlias(true);
/* 设置裁剪区域 */
canvas.clipRect(10, 10, 280, 260);
/* 线锁定画布 */
canvas.save();
/* 旋转画布 */
canvas.rotate(45.0f);
/* 设置颜色及绘制矩形 */
mPaint.setColor(Color.RED);
canvas.drawRect(new Rect(15, 15, 140, 70), mPaint);
/* 解除画布的锁定 */
canvas.restore();
/* 设置颜色及绘制另一个矩形 */
mPaint.setColor(Color.GREEN);
canvas.drawRect(new Rect(150, 75, 260, 120), mPaint);
}
// 触笔事件
public boolean onTouchEvent(MotionEvent event) {
return true;
}
// 按键按下事件
public boolean onKeyDown(int keyCode, KeyEvent event) {
return true;
}
// 按键弹起事件
public boolean onKeyUp(int keyCode, KeyEvent event) {
return false;
}
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
return true;
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 使用postInvalidate可以直接在线程中更新界面
postInvalidate();
}
}
}
canvas.drawRoundRect(RectF, float, float, Paint) 方法用于画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
canvas.drawLine(startX, startY, stopX, stopY, paint):前四个参数的类型均为float,最后一个参数类型为Paint。表示用画笔paint从点(startX,startY)到点(stopX,stopY)画一条直线;
canvas.drawArc(oval, startAngle, sweepAngle, useCenter, paint):第一个参数oval为RectF类型,即圆弧显示区域,startAngle和sweepAngle均为float类型,分别表示圆弧起始角度和圆弧度数,3点钟方向为0度,useCenter设置是否显示圆心,boolean类型,paint为画笔;
canvas.drawCircle(float,float, float, Paint)方法用于画圆,前两个参数代表圆心坐标,第三个参数为圆半径,第四个参数是画笔;
清楚这些函数的用法之后,我们是否就噼里啪啦地敲代码了呢?别急,我们来搞个设计。既然这些函数都是用来画图的,也就是说它们有共性——画。所有我们应该设计一个接口interface,对于这次任务,只需要一个成员方法就足够了。对于每一个图形,是只用一个方法画,还是将画图封装成类呢?我建议是封装成类。因为说不定你明天就会嫌弃它不会动,想它动起来,或者你过两天又希望在机器人的每个部位加点什么。所以我将每一个图形封装成类,都实现一个名叫drawGraphics的接口。最后,要记得给UI创建一个线程哦。
就这样我开始动手做了,但是很快就发现问题了。什么问题?在定位的时候,也就是设置每个图形的显示区域时,我自以为这里的Rect跟Java的Rectangle是一样的,但我错了。原来这厮跟MFC中的RECT结构才是一家人,害我折腾了许久。
Rect(int left,int top,int right,int bottom)
left
矩形左上角X坐标值
top
矩形左上角Y坐标值
right
矩形右下角X坐标值
bottom
矩形右下角Y坐标值
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
public class GameView extends View implements Runnable{
//声明Paint对象
private Paint mPaint= null;
private drawGraphics drawGraphics= null;
public GameView(Context context) {
super(context);
//开启线程
new Thread(this).start();
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
//构建对象
mPaint= new Paint();
//设置画布为黑色背景
canvas.drawColor(Color.BLUE);
//消除锯齿
mPaint.setAntiAlias(true);
//设置图形为空心
// mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStyle(Paint.Style.FILL);
//绘制机器人的头部和眼睛
drawGraphics= new DrawCircle(mPaint);
drawGraphics.draw(canvas);
//绘制机器人的天线
drawGraphics= new DrawLine(mPaint);
drawGraphics.draw(canvas);
//绘制机器人的身子和四肢
drawGraphics= new DrawRect(mPaint);
drawGraphics.draw(canvas);
}
@Override
public void run() {
// TODOAuto-generated method stub
while(!Thread.currentThread().isInterrupted()) {
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
// TODO: handle exception
Thread.currentThread().interrupt();
}
//使用postInvalidate 可以直接在线程中更新界面
postInvalidate();
}
}
}
import android.graphics.Canvas;
public interface drawGraphics {
public void draw(Canvas canvas);
}
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
public class DrawLine implements drawGraphics{
private Paint paint= null;
public DrawLine(){
paint= new Paint();
}
public DrawLine(Paint paint){
this.paint= paint;
}
@Override
public void draw(Canvas canvas) {
// TODOAuto-generated method stub
paint.setAntiAlias(true);
//绘制直线
paint.setColor(Color.GREEN);
//设置线条粗细
paint.setStrokeWidth(12);
canvas.drawLine(120,40,170,90, paint);
canvas.drawLine(320,90,370,40, paint);
}
}
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
public class DrawRect implements drawGraphics{
private Paint paint= null;
public DrawRect(){
paint= new Paint();
}
public DrawRect(Paint paint){
this.paint= paint;
}
@Override
public void draw(Canvas canvas) {
// TODOAuto-generated method stub
//定义圆角矩形对象
RectF rectF1 = new RectF(120,170,370,500);
RectF rectF2 = new RectF(40,150,90,400);
RectF rectF3 = new RectF(390,150,440,400);
RectF rectF4 = new RectF(140,520,200,650);
RectF rectF5 = new RectF(290,520,350,650);
paint.setAntiAlias(true);
//设置画笔颜色为BLUE
paint.setColor(Color.GREEN);
//在画布上绘制圆角矩形/圆弧/直线
canvas.drawRoundRect(rectF1, 30, 30, paint);
canvas.drawRoundRect(rectF2, 20, 20, paint);
canvas.drawRoundRect(rectF3, 20, 20, paint);
canvas.drawRoundRect(rectF4, 20, 20, paint);
canvas.drawRoundRect(rectF5, 20, 20, paint);
}
}
import android.app.Activity;
import android.os.Bundle;
public class GameStart extends Activity {
private GameView mGameView= null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mGameView= new GameView(this);
setContentView(mGameView);
}
}
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
public class GameView extends View implements Runnable{
//声明Paint对象
private Paint mPaint= null;
private drawGraphics drawGraphics= null;
public GameView(Context context) {
super(context);
//开启线程
new Thread(this).start();
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
//构建对象
mPaint= new Paint();
//设置画布为黑色背景
canvas.drawColor(Color.BLUE);
//消除锯齿
mPaint.setAntiAlias(true);
//设置图形为空心
// mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStyle(Paint.Style.FILL);
//绘制机器人的头部和眼睛
drawGraphics= new DrawCircle(mPaint);
drawGraphics.draw(canvas);
//绘制机器人的天线
drawGraphics= new DrawLine(mPaint);
drawGraphics.draw(canvas);
//绘制机器人的身子和四肢
drawGraphics= new DrawRect(mPaint);
drawGraphics.draw(canvas);
}
@Override
public void run() {
// TODOAuto-generated method stub
while(!Thread.currentThread().isInterrupted()) {
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
// TODO: handle exception
Thread.currentThread().interrupt();
}
//使用postInvalidate 可以直接在线程中更新界面
postInvalidate();
}
}
}
- Android自定义简单的View
- Android自定义简单的View
- Android自定义简单的View
- Android自定义View 一<最简单的自定义View>
- android简单自定义view
- Android 简单自定义View
- 简单举例说明android自定义view的方法
- Android 自定义View -- 简单的倒计时器
- 一个简单的Android自定义view详解
- Android简单自定义view的实现
- android 自定义控件---简单的加载View
- 一个简单的Android自定义View
- Android 实现一个简单的自定义View
- android 自定义view的简单实例
- 简单的view 自定义
- 简单的自定义View
- 简单的自定义view
- Android学习:简单自定义View
- Ftp函数常用协议
- 使用内部线程维护变量持久更新
- Visual Studio 2012 Ultimate RTM 体验(附下载地址和KEY)
- 块设备驱动(用ram来虚拟disk进行操作)
- hdu Strange fuction二分法不具有单调性
- Android自定义简单的View
- PHP Document 代码注释规范
- NULL指针、零指针、野指针
- POJ 1847 Tram 优先队列+bfs
- hdu4371 Alice and Bob---博弈------多校联合8
- Struts1的应用
- 申请Google Map Android API Key
- SharePoint 2007图片轮播part源代码
- TOMCAT服务器证书的导入