在使用到自定义View的xml布局文件中需要加入xmlns:前缀="http://schemas.android.com/apk/res/你的自定义View所在的包路径".
枚举型的格式:
1 |
<attr name= "orientation" > |
2 |
< enum name= "horizontal" value= "0" /> |
3 |
< enum name= "vertical" value= "1" /> |
- <resources>
- <declare-styleable name="MyView">
- <attr name="textColor" format="color"/>
- <attr name="textSize" format="dimension"/>
- </declare-styleable>
- </resources>
XML文件中使用:
1 |
android:orientation = "vertical" |
标志位、位或运算,格式如下:
01 |
<attr name= "windowSoftInputMode" > |
02 |
<flag name = "stateUnspecified" value = "0" /> |
03 |
<flag name = "stateUnchanged" value = "1" /> |
04 |
<flag name = "stateHidden" value = "2" /> |
05 |
<flag name = "stateAlwaysHidden" value = "3" /> |
06 |
<flag name = "stateVisible" value = "4" /> |
07 |
<flag name = "stateAlwaysVisible" value = "5" /> |
08 |
<flag name = "adjustUnspecified" value = "0x00" /> |
09 |
<flag name = "adjustResize" value = "0x10" /> |
10 |
<flag name = "adjustPan" value = "0x20" /> |
11 |
<flag name = "adjustNothing" value = "0x30" /> |
XML文件中使用:
1
android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
属性定义可以指定多种类型:
1
<
attr
name
=
"background"
format
=
"reference|color"
/>
XML文件中使用:
1
android:background = "@drawable/图片ID|#00FF00"
- <?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
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类
需要绘制到画布上,这就得用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.drawRect(RectF,Paint)方法用于画矩形,第一个参数为图形显示区域,第二个参数为画笔,设置好图形显示区域Rect和画笔Paint后,即可画图;
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坐标值
package com.dengchuan.android.drawable;
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();
}
}
}
package com.dengchuan.android.drawable;
import android.graphics.Canvas;
public interface drawGraphics {
public void draw(Canvas canvas);
}
package com.dengchuan.android.drawable;
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);
}
}
package com.dengchuan.android.drawable;
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);
}
}
package com.dengchuan.android.drawable;
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);
}
}
package com.dengchuan.android.drawable;
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();
}
}
}