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值的格式


1"reference" //引用
2"color" //颜色
3"boolean" //布尔值
4"dimension" //尺寸值
5"float" //浮点值
6"integer" //整型值
7"string" //字符串
8"fraction" //百分数,比如200%

枚举型的格式:

1<attr name="orientation">
2  <enum name="horizontal" value="0" />
3  <enum name="vertical" value="1" />
4</attr>

  1. <resources>  
  2.     <declare-styleable name="MyView">  
  3.         <attr name="textColor" format="color"/>  
  4.         <attr name="textSize" format="dimension"/>  
  5.     </declare-styleable>  
  6. </resources> 
  

XML文件中使用:

1android: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" />
12</attr>

XML文件中使用:

1android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">

属性定义可以指定多种类型:

1<attr name "background" format "reference|color" />

XML文件中使用:

1android:background = "@drawable/图片ID|#00FF00"


 

实例:

[java] view plaincopy
  1. package demo.view.my;  
  2. import android.content.Context;  
  3. import android.content.res.TypedArray;  
  4. import android.graphics.Canvas;  
  5. import android.graphics.Color;  
  6. import android.graphics.Paint;  
  7. import android.graphics.Paint.Style;  
  8. import android.util.AttributeSet;  
  9. import android.view.View;  
  10. /** 
  11.  * 这个是自定义的TextView. 
  12.  * 至少需要重载构造方法和onDraw方法 
  13.  * 对于自定义的View如果没有自己独特的属性,可以直接在xml文件中使用就可以了 
  14.  * 如果含有自己独特的属性,那么就需要在构造函数中获取属性文件attrs.xml中自定义属性的名称 
  15.  * 并根据需要设定默认值,放在在xml文件中没有定义。 
  16.  * 如果使用自定义属性,那么在应用xml文件中需要加上新的schemas, 
  17.  * 比如这里是xmlns:my="http://schemas.android.com/apk/res/demo.view.my" 
  18.  * 其中xmlns后的“my”是自定义的属性的前缀,res后的是我们自定义View所在的包 
  19.  * @author Administrator 
  20.  * 
  21.  */  
  22. public class MyView extends View {  
  23.       
  24.     Paint mPaint; //画笔,包含了画几何图形、文本等的样式和颜色信息  
  25.     public MyView(Context context) {  
  26.         super(context);  
  27.           
  28.     }  
  29.       
  30.     public MyView(Context context, AttributeSet attrs){  
  31.         super(context, attrs);  
  32.         mPaint = new Paint();  
  33.         //TypedArray是一个用来存放由context.obtainStyledAttributes获得的属性的数组  
  34.         //在使用完成后,一定要调用recycle方法  
  35.         //属性的名称是styleable中的名称+“_”+属性名称  
  36.         TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);  
  37.         int textColor = array.getColor(R.styleable.MyView_textColor, 0XFF00FF00); //提供默认值,放置未指定  
  38.         float textSize = array.getDimension(R.styleable.MyView_textSize, 36);  
  39.         mPaint.setColor(textColor);  
  40.         mPaint.setTextSize(textSize);  
  41.           
  42.         array.recycle(); //一定要调用,否则这次的设定会对下次的使用造成影响  
  43.     }  
  44.       
  45.     public void onDraw(Canvas canvas){  
  46.         super.onDraw(canvas);  
  47.         //Canvas中含有很多画图的接口,利用这些接口,我们可以画出我们想要的图形  
  48.         //mPaint = new Paint();  
  49.         //mPaint.setColor(Color.RED);  
  50.         mPaint.setStyle(Style.FILL); //设置填充  
  51.         canvas.drawRect(1010100100, mPaint); //绘制矩形  
  52.           
  53.         mPaint.setColor(Color.BLUE);  
  54.         canvas.drawText("我是被画出来的"10120, mPaint);  
  55.     }  
  56. }  

 

相应的属性文件:

 

[xhtml] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <declare-styleable name="MyView">  
  4.         <attr name="textColor" format="color"/>  
  5.         <attr name="textSize" format="dimension"/>  
  6.     </declare-styleable>  
  7. </resources>  

 

在布局文件中的使用:

 

[xhtml] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   
  3.               xmlns:my="http://schemas.android.com/apk/res/demo.view.my"   
  4.     android:orientation="vertical"  
  5.     android:layout_width="fill_parent"  
  6.     android:layout_height="fill_parent"  
  7.     >  
  8.       
  9.     <demo.view.my.MyView  
  10.         android:layout_width="fill_parent"  
  11.         android:layout_height="wrap_content"   
  12.         my:textColor="#FFFFFFFF"   
  13.         my:textSize="22dp"  
  14.         />  
  15. </LinearLayout>  







关于  Matrix

Matrix ,中文里叫矩阵,高等数学里有介绍,在图像处理方面,主要是用于平面的缩放、平移、旋转等操作。

        首先介绍一下矩阵运算。加法和减法就不用说了,太简单了,对应位相加就好。图像处理,主要用到的是乘法 。下面是一个乘法的公式:

矩阵相乘示例

 

在 Android 里面, Matrix 由 9 个 float 值构成,是一个 3*3 的矩阵。如下图。

 

Android中Matrix矩阵

 

没专业工具,画的挺难看。解释一下,上面的 sinX 和 cosX ,表示旋转角度的 cos 值和 sin 值,注意,旋转角度是按顺时针方向计算的。 translateX 和 translateY 表示 x 和 y 的平移量。 scale 是缩放的比例, 1 是不变, 2 是表示缩放 1/2 ,这样子。

 

[java] view plaincopy
  1.  1public   class  MyView  extends  View {    
  2.  2.     
  3.  3.     private  Bitmap mBitmap;    
  4.  4.     
  5.  5.     private  Matrix mMatrix =  new  Matrix();    
  6.  6.     
  7.  7.     public  MyView(Context context) {    
  8.  8.     
  9.  9.         super (context);    
  10. 10.     
  11. 11.         initialize();    
  12. 12.     
  13. 13.     }    
  14. 14.     
  15. 15.     private   void  initialize() {         
  16. 16.     
  17. 17.         mBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.show)).getBitmap();            
  18. 18.     
  19. 19.         float  cosValue = ( float ) Math.cos(-Math.PI/ 6 );    
  20. 20.     
  21. 21.         float  sinValue = ( float ) Math.sin(-Math.PI/ 6 );    
  22. 22.     
  23. 23.         mMatrix.setValues(    
  24. 24.     
  25. 25.                 new   float []{    
  26. 26.     
  27. 27.                         cosValue, -sinValue, 100 ,    
  28. 28.     
  29. 29.                         sinValue, cosValue, 100 ,    
  30. 30.     
  31. 31.                         0 ,  0 ,  2 });    
  32. 32.     
  33. 33.     }    
  34. 34.     
  35. 35.     @Override   protected   void  onDraw(Canvas canvas) {    
  36. 36.     
  37. 37//      super.onDraw(canvas);  //当然,如果界面上还有其他元素需要绘制,只需要将这句话写上就行了。     
  38. 38.     
  39. 39.         canvas.drawBitmap(mBitmap, mMatrix, null );    
  40. 40.     
  41. 41.     }    
  42. 42.     
  43. 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)的地方,那么可以这样做:

 

[java] view plaincopy
  1. # Matrix m =  new  Matrix();    
  2. #     
  3. # m.postRotate(30 );    
  4. #     
  5. # m.postTranslate(100 ,  100 );    

这样就达到了想要的效果。


    pre是前乘,参数给出的矩阵乘以当前的矩阵。所以操作是在当前矩阵的最前面发生的。例如上面的例子,如果用pre的话

,就要这样:

[java] view plaincopy
  1. 1. Matrix m =  new  Matrix();    
  2. 2.     
  3. 3. m.preTranslate(100 ,  100 );    
  4. 4.     
  5. 5. m.preRotate(30 );    

 

旋转、缩放和倾斜都可以围绕一个中心点来进行,如果不指定,默认情况下,是围绕(0,0)点来进行。

 

[java] view plaincopy
  1.  1package  chroya.demo.graphics;    
  2.  2.     
  3.  3import  android.content.Context;    
  4.  4import  android.graphics.Bitmap;    
  5.  5import  android.graphics.Canvas;    
  6.  6import  android.graphics.Matrix;    
  7.  7import  android.graphics.Rect;    
  8.  8import  android.graphics.drawable.BitmapDrawable;    
  9.  9import  android.util.DisplayMetrics;    
  10. 10import  android.view.MotionEvent;    
  11. 11import  android.view.View;    
  12. 12.     
  13. 13public   class  MyView  extends  View {    
  14. 14.         
  15. 15.     private  Bitmap mBitmap;    
  16. 16.     private  Matrix mMatrix =  new  Matrix();    
  17. 17.         
  18. 18.     public  MyView(Context context) {    
  19. 19.         super (context);    
  20. 20.         initialize();    
  21. 21.     }    
  22. 22.     
  23. 23.     private   void  initialize() {    
  24. 24.             
  25. 25.         Bitmap bmp = ((BitmapDrawable)getResources().getDrawable(R.drawable.show)).getBitmap();    
  26. 26.         mBitmap = bmp;    
  27. 27.         /*首先,将缩放为100*100。这里scale的参数是比例。有一点要注意,如果直接用100/   
  28. 28. bmp.getWidth()的话,会得到0,因为是整型相除,所以必须其中有一个是float型的,直接用100f就好。*/     
  29. 29.         mMatrix.setScale(100f/bmp.getWidth(), 100f/bmp.getHeight());    
  30. 30.                 //平移到(100,100)处     
  31. 31.         mMatrix.postTranslate(100 ,  100 );    
  32. 32.                 //倾斜x和y轴,以(100,100)为中心。     
  33. 33.         mMatrix.postSkew(0 .2f,  0 .2f,  100 ,  100 );    
  34. 34.     }    
  35. 35.         
  36. 36.     @Override   protected   void  onDraw(Canvas canvas) {    
  37. 37//      super.onDraw(canvas);  //如果界面上还有其他元素需要绘制,只需要将这句话写上就行了。     
  38. 38.             
  39. 39.         canvas.drawBitmap(mBitmap, mMatrix, null );    
  40. 40.     }    
  41. 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(); 
           }
     }


}


下载:http://download.csdn.net/detail/qeqeqe236/4507901




原创粉丝点击