一、编写自己的自定义View
最简单的自定义View,继承View
通过覆盖View的onDraw方法来实现自主显示
利用Canvas和paint来绘制显示元素(文字,几何图形等)

51、自定义View基础和原理_xml

<com.myview.v1.MyView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00ff00" />
public class MyView extends View {

    private Bitmap bitmap;

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        bitmap = BitmapFactory.decodeResource(getResources(),
                R.drawable.ic_launcher);
    }

    public MyView(Context context) {
        super(context);
        bitmap = BitmapFactory.decodeResource(getResources(),
                R.drawable.ic_launcher);
    }

    @Override    // 加入绘制元素
    protected void onDraw(Canvas canvas) {

        Paint paint = new Paint();
        paint.setTextSize(30);
        // a,r,g,b(透明度,红色,绿色,蓝色)
        paint.setColor(0xffff0000);
        /**
         * 绘制文字
         * Android 中绘制文字的方向是,左下。 所以需要把Y坐标改成30下移,就可以显示了。
         * 0, 30 分别为 X Y坐标
         * drawText(String text, float x, float y, Paint paint)
         */
        canvas.drawText("this is onDraw", 0, 30, paint);
        /**
         * drawLine(float startX, float startY, float stopX, float stopY, Paint paint) 
         * 在文字下30的地方绘制一条线,所以开始和结束的Y坐标就是(30+30)。
         */
        canvas.drawLine(0, 60, 100, 60, paint);

        paint.setStyle(Style.STROKE);

        // 通过坐标绘制矩形
        // canvas.drawRect(0, 90, 100, 190, paint);
        // 通过Rect绘制矩形
        // Rect r = new Rect(0, 90, 100, 190);
        // canvas.drawRect(r, paint);
        // 通过RectF绘制矩形
        RectF rect = new RectF(0, 90, 100, 190);
        // canvas.drawRect(rect, paint);
        // 绘制圆角矩形
        // drawRoundRect(RectF rect, float rx, float ry, Paint paint)
        canvas.drawRoundRect(rect, 10, 10, paint);

        // 绘制圆形
        canvas.drawCircle(50, 270, 50, paint);

        canvas.drawBitmap(bitmap, 0, 350, paint);
    }

}

 

二、加入逻辑线程
让文字动起来,改变坐标
在线程中修改坐标(加入循环,时间睡眠)
重新绘制元素(两种方式)
线程休眠时间控制(去除逻辑处理时间)

51、自定义View基础和原理_android_02

 1 import java.util.Random;
 2 import android.content.Context;
 3 import android.graphics.Canvas;
 4 import android.graphics.Paint;
 5 import android.graphics.RectF;
 6 import android.util.AttributeSet;
 7 import android.view.View;
 8 
 9 public class LogicView extends View {
10 
11     // 实例化 画笔。
12     private Paint paint = new Paint();
13     // 
14     private float rx = 0;
15     // 添加线程属性
16     private MyThread thread;
17     /**
18      * 绘制在文字下,30的地方。
19      * RectF(float left, float top, float right, float bottom)
20      *         0           30+30                宽度100       60+100
21      */
22     private RectF rectF = new RectF(0, 60, 100, 160);
23     // 区间角度
24     private float sweepAngle = 0;
25 
26     // 在布局当中使用。
27     public LogicView(Context context, AttributeSet attrs) {
28         super(context, attrs);
29     }
30 
31     // 在代码当中使用。
32     public LogicView(Context context) {
33         super(context);
34     }
35 
36     @Override
37     protected void onDraw(Canvas canvas) {
38         paint.setTextSize(30);
39         canvas.drawText("LogicView", rx, 30, paint);
40         /**
41          * 绘制一个圆。
42          * drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
43               Paint paint)
44          * sweepAngle:区间角度。
45          * useCenter:true ,false. 绘制的效果不同。
46          */
47         canvas.drawArc(rectF, 0, sweepAngle, true, paint);
48 
49         if (thread == null) {
50             thread = new MyThread();
51             thread.start();
52         }
53     }
54 
55     class MyThread extends Thread {
56         // 随机对象,产生随机值。
57         Random rand = new Random();
58         @Override
59         public void run() {
60             while (true) {
61                 rx += 3;
62                 // 当超出屏幕的宽度时。
63                 if (rx > getWidth()) {
64                     /**
65                      * 走出屏幕后,重新回到屏幕。
66                      * 0 - 文字的宽度,这样 “LogicView”文字,从左边出来的时候,
67                      * 就可以从w开始一点点移出屏幕。
68                      */
69                     rx = 0 - paint.measureText("LogicView");
70                 }
71 
72                 sweepAngle++;
73                 if (sweepAngle > 360) {
74                     sweepAngle = 0;
75                 }
76 
77                 // 取到 0-255的随机数。
78                 int r = rand.nextInt(256);
79                 int g = rand.nextInt(256);
80                 int b = rand.nextInt(256);
81 
82                 // 设置颜色。
83                 paint.setARGB(255, r, g, b);
84 
85                 postInvalidate(); // 重绘
86                 try {
87                     Thread.sleep(30); // 睡眠30毫秒
88                 } catch (InterruptedException e) {
89                     e.printStackTrace();
90                 }
91             }
92         }
93     }
94 
95 }

把以上代码进行封装。

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

// 基类
public abstract class BaseView extends View {
    private MyThread thread;
    private boolean running = true;

    public BaseView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public BaseView(Context context) {
        super(context);
    }

    protected abstract void drawSub(Canvas canvas);

    protected abstract void logic();

    @Override
    protected final void onDraw(Canvas canvas) {

        if (thread == null) {
            thread = new MyThread();
            thread.start();
        } else {
            drawSub(canvas);
        }

    }

    @Override
    protected void onDetachedFromWindow() {
        running = false;
        super.onDetachedFromWindow();
    }

    class MyThread extends Thread {
        @Override
        public void run() {
            while (running) {
                logic();
                postInvalidate();
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
 1 import java.util.Random;
 2 import android.content.Context;
 3 import android.graphics.Canvas;
 4 import android.graphics.Paint;
 5 import android.graphics.RectF;
 6 import android.util.AttributeSet;
 7 
 8 public class LogicView extends BaseView {
 9 
10     private Paint paint = new Paint();
11     private float rx = 0;
12     private RectF rectF = new RectF(0, 60, 100, 160);
13     private float sweepAngle = 0;
14     Random rand = new Random();
15 
16     public LogicView(Context context, AttributeSet attrs) {
17         super(context, attrs);
18     }
19 
20     public LogicView(Context context) {
21         super(context);
22     }
23 
24     @Override
25     protected void drawSub(Canvas canvas) {
26         paint.setTextSize(30);
27         canvas.drawText("LogicView", rx, 30, paint);
28         canvas.drawArc(rectF, 0, sweepAngle, true, paint);
29     }
30 
31     @Override
32     protected void logic() {
33         rx += 3;
34 
35         if (rx > getWidth()) {
36             rx = 0 - paint.measureText("LogicView");
37         }
38 
39         sweepAngle++;
40         if (sweepAngle > 360) {
41             sweepAngle = 0;
42         }
43 
44         int r = rand.nextInt(256);
45         int g = rand.nextInt(256);
46         int b = rand.nextInt(256);
47 
48         paint.setARGB(255, r, g, b);
49     }
50 
51 }
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;

 
public class MyText extends BaseView {

    private Paint paint = new Paint();
    private float rx = 0;

    public MyText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyText(Context context) {
        super(context);
    }

    @Override
    protected void drawSub(Canvas canvas) {
        paint.setTextSize(30);
        canvas.drawText("MyText", rx, 30, paint);
    }

    @Override
    protected void logic() {
        rx += 3;
        if (rx > getWidth()) {
            rx = -paint.measureText("MyText");
        }
    }

}

 

三、利用xml中定义样式来影响显示效果
在xml中定义样式和属性
在布局中使用属性(命名空间需要声明)
在代码中解析样式的属性
在代码中使用属性对显示效果产生影响

范例:绘制相同的文字,不同的行数。从左边移动到右边。

布局文件:activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:nt="http://schemas.android.com/apk/res/com.myview"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >   nt:这个名字是自定义的。

    <com.myview.v4.NumText 
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        nt:lineNum="6"
        nt:xScroll="true"/>
</FrameLayout>
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import com.myview.R;
import com.myview.v3.BaseView;

// 效果:竖着一排文字,无限 从左移动到右边
public class NumText extends BaseView {

    private Paint paint = new Paint();
    private int lineNum = 0;
    private int mx = 0;
    private boolean xScroll = false;

    public NumText(Context context) {
        super(context);
    }
    
    public NumText(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 在代码中解析 “样式属性”。
        TypedArray ta = context.obtainStyledAttributes(attrs,
                R.styleable.NumText);
        //  1  是默认的一行。
        lineNum = ta.getInt(R.styleable.NumText_lineNum, 1);
        // 默认为不滚动(文字从左滚动到右边)
        xScroll = ta.getBoolean(R.styleable.NumText_xScroll, false);
        ta.recycle();
    }

    @Override
    protected void drawSub(Canvas canvas) {
        for (int i = 0; i < lineNum; i++) {
            int textSize = 30 + i;
            paint.setTextSize(textSize);
            canvas.drawText("百度百科", mx, textSize + textSize * i, paint);
        }
    }

    @Override
    protected void logic() {
        if (xScroll) {
            mx += 3;
            if (mx > getWidth()) {
                mx = (int) -paint.measureText("百度百科");
            }
        }
    }

}

四、通过代码添加自定义控件。
setContentView(new MyView(this));