课程背景:

在 Android 提供的系统控件不能满足需求的情况下,往往须要自己开发自己定义 View 来满足需求,可是该怎样下手呢。本课程将带你进入自己定义 View 的开发过程,来了解它的一些原理。

核心内容:

1.编写自己的自己定义 View

2.增加逻辑线程

3.提取和封装自己定义 View

4.利用 xml 中定义样式来影响显示效果

编写自己的自己定义 View(上)

本课时主要解说最简单的自己定义 View,然后增加绘制元素(文字、图形等),而且能够像使用系统控件一样在布局中使用。

本课时将要做两件事情:

编写最简单的自己定义View,什么都不显示。可是有View的特性

能够显示自己的元素

本课时的知识要点包含:

最简单的自己定义View。继承View

通过覆盖View的onDraw方法来实现自主显示

利用Canvas和paint来绘制显示元素(文字。几何图形等)


public class MyView extends View{

    private Bitmap bitmap;

    //须要增加两个构造方法(第一个在代码中使用,第二个在布局中使用)
public MyView(Context context) {
super(context);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
} public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
} //通过View提供的onDraw方法增加绘制元素
@Override
protected void onDraw(Canvas canvas) {//Canvas能够绘制文字、集合图形、Bitmap(图片) //The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
Paint paint = new Paint();
paint.setTextSize(30);//设置文字大小
paint.setColor(0xffff0000);//通过16进制的方式设置paint的颜色,以16进制代表的颜色意义是:a,r,g,b
/**
* 绘制文字
* 參数
* text,x,y,paint
*/
canvas.drawText("this is onDraw", 0, 30, paint); //绘制几何元素 /**
* 绘制直线
* 參数
* startX, startY, stopX, stopY, paint
*/
canvas.drawLine(0, 60, 100, 60, paint); paint.setStyle(Style.STROKE);//设置图型样式为空心 /**
* 绘制矩形
* 參数
* left, top, right, bottom, paint
*/
//canvas.drawRect(0, 90, 100, 190, paint); //另外两个绘制矩形方法
//Rect r = new Rect(0, 90, 100, 190);
//canvas.drawRect(r, paint); RectF r = new RectF(0, 90, 100, 190);
//canvas.drawRect(r, paint); /**
* 绘制圆角的矩形
* 參数
* rect, rx, ry, paint
* rx, ry:代表X轴和Y轴的弧度
*/
//canvas.drawRoundRect(r, 10, 10, paint); /**
* 绘制圆形
* 參数:cx, cy, radius, paint
* cx, cy:圆心位置。radius半径
*/
canvas.drawCircle(50, 270, 50, paint); /**
* 绘制图片
* 參数:bitmap, left, top, paint
* left, top:左上坐标
*/
canvas.drawBitmap(bitmap, 0, 350, paint); }
}

上面通过Paint对象能够影响绘制元素的颜色和样式信息

    <!-- 在布局中增加自己定义View,该自己定义View继承了系统的View所以他有系统View的一些属性 -->

    <com.example.myview.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00ff00" />

public class MainActivity extends Activity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main); // 在代码中实例化自己定义view(通过代码的方式增加自己定义view)
setContentView(new MyView(this));
} }

增加逻辑线程

本课时须要让绘制的元素动起来,可是又不堵塞主线程。所以引入逻辑线程。

在子线程更新 UI 是不被同意的。可是 View 提供了方法。让我们来看看吧。

本课时的背景:

怎么让元素动起来。须要什么

让元素动起来的逻辑放在哪里

逻辑怎么影响绘制

怎么让元素看起来流畅

本课时的知识要点包含:

让文字动起来,改变坐标

在线程中改动坐标(增加循环,时间睡眠)

又一次绘制元素(两种方式)

线程休眠时间控制(去除逻辑处理时间)

案例效果:

public class LogicView extends View {

    private Paint paint = new Paint();

    private float rx = 0;
private MyThread thread;
private RectF rectF = new RectF(0, 60, 100, 160);
private float sweepAngle = 0; public LogicView(Context context) {
super(context);
// TODO Auto-generated constructor stub
} public LogicView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} @Override
protected void onDraw(Canvas canvas) { paint.setTextSize(30);
canvas.drawText("LogicView", rx, 30, paint); canvas.drawArc(rectF, 0, sweepAngle, true, paint); if (thread == null) {
thread = new MyThread();
thread.start();
} } class MyThread extends Thread { Random rand = new Random(); @Override
public void run() { while (true) {
rx += 3; if (rx > getWidth()) {// 超出屏幕是回到屏幕
rx = 0 - paint.measureText("LogicView");// measureText方法測量文字的宽度
} // ----------------------------
sweepAngle++;
if (sweepAngle > 360) {
sweepAngle = 0;
} int r = rand.nextInt(256);// 0-255
int g = rand.nextInt(256);// 0-255
int b = rand.nextInt(256);// 0-255
paint.setARGB(255, r, g, b);// 第一个255全然不透明 // 又一次绘制,调用View提供的在线程中更新绘制的方法onDraw
postInvalidate();// 出来该方法还能够Handler的方式自主线程中调用Invalidate()方法 try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
}

回头再查看LogicView发现他的代码还是比較复杂的下一课时将要抽取和封装自己定义View。简化代码和逻辑(用最简单的方式实现最炫的效果

提取和封装自己定义View

本课时主要解说在上个课程的基础上,进行提代替码来构造自己定义 View 的基类。主要目的是:创建新的自己定义 View 时。仅仅需继承此类并仅仅关心绘制和逻辑。其它工作由父类完毕。这样既降低反复编码,也简化了逻辑。

本课时的背景:

为什么封装自己定义View,全然能够ctrl+c和ctrl+v啊

怎样做到简化代码和逻辑

怎样禁止子类改动操作(父类的某些方法不希望子类来改动

演示效果和对照代码逻辑的差异

本课时解说的内容包含:

封装自己定义基类,抽象绘制和逻辑方法

将onDraw变为final方法

利用封装的自己定义View的基类来演示原效果

本课时是基于上一课时的代码进行提取和封装。下面的BaseView就是封装的父类(提取和封装主要从两个方向进行,绘制方面和逻辑方面)为了具体介绍这里分为下面两步:

代码的提取:

public class BaaseView extends View {

    private Paint paint = new Paint();

    private float rx = 0;
private MyThread thread;
private RectF rectF = new RectF(0, 60, 100, 160);
private float sweepAngle = 0;
Random rand = new Random(); public BaaseView(Context context) {
super(context);
// TODO Auto-generated constructor stub
} public BaaseView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} // 提取绘制方面的代码
private void drawSub(Canvas canvas) {
paint.setTextSize(30);
canvas.drawText("LogicView", rx, 30, paint);
canvas.drawArc(rectF, 0, sweepAngle, true, paint);
} @Override
protected void onDraw(Canvas canvas) { if (thread == null) {
thread = new MyThread();
thread.start();
} else {
drawSub(canvas);
} } // 提取逻辑代码
private void logic() {
rx += 3; if (rx > getWidth()) {// 超出屏幕是回到屏幕
rx = 0 - paint.measureText("LogicView");// measureText方法測量文字的宽度
} // ----------------------------
sweepAngle++;
if (sweepAngle > 360) {
sweepAngle = 0;
} int r = rand.nextInt(256);// 0-255
int g = rand.nextInt(256);// 0-255
int b = rand.nextInt(256);// 0-255
paint.setARGB(255, r, g, b);// 第一个255全然不透明
} class MyThread extends Thread { @Override
public void run() { while (true) { logic(); // 又一次绘制。调用View提供的在线程中更新绘制的方法onDraw
postInvalidate();// 出来该方法还能够Handler的方式自主线程中调用Invalidate()方法 try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
}

代码的封装:(怎样进行封装呢?这里就须要对父类进行一些处理,如绘制方面的内容希望在子类中实现父类仅仅进行管理。也就是说drawSub方法不在父类中绘制,这就须要把他变成抽象方法在子类中必须实现它,逻辑方面也相同希望通过子类来实现具体的处理父类中也仅仅进行管理,由于这些方法须要子类中使用所以须要把他的修饰符改为受保护的protected。又由于有了抽象方法说以类也要改为抽象类)

public abstract class BaseView extends View {

    private MyThread thread;

    public BaseView(Context context) {
super(context);
} public BaseView(Context context, AttributeSet attrs) {
super(context, attrs);
} protected abstract void drawSub(Canvas canvas); protected abstract void logic(); @Override
protected void onDraw(Canvas canvas) { if (thread == null) {
thread = new MyThread();
thread.start();
} else {
drawSub(canvas);
} } class MyThread extends Thread { @Override
public void run() { while (true) { logic(); postInvalidate(); try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

到这里一个封装的BaaseView 就完毕了。如今之须要一个子类来进行演示

public class LogicView extends BaseView {

    private Paint paint = new Paint();

    private float rx = 0;
private RectF rectF = new RectF(0, 60, 100, 160);
private float sweepAngle = 0;
Random rand = new Random(); public LogicView(Context context) {
super(context);
// TODO Auto-generated constructor stub
} public LogicView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} @Override
protected void drawSub(Canvas canvas) {
paint.setTextSize(30);
canvas.drawText("LogicView", rx, 30, paint);
canvas.drawArc(rectF, 0, sweepAngle, true, paint);
} @Override
protected void logic() {
rx += 3; if (rx > getWidth()) {// 超出屏幕是回到屏幕
rx = 0 - paint.measureText("LogicView");// measureText方法測量文字的宽度
} // ----------------------------
sweepAngle++;
if (sweepAngle > 360) {
sweepAngle = 0;
} int r = rand.nextInt(256);// 0-255
int g = rand.nextInt(256);// 0-255
int b = rand.nextInt(256);// 0-255
paint.setARGB(255, r, g, b);// 第一个255全然不透明
}
}

以上就完毕了对第二课时(增加逻辑线程)的绘制方面和逻辑方面的代码提取和封装,将该自己定义view增加布局文件里执行后效果与第二课时一样。

可能通过上面的解说还是比較混乱。所以接下来通过继承BaseView再定义一个简单的自己定义View(MyText),效果还是在屏幕上滚动。

public class MyText extends BaseView {

    private Paint paint = new Paint();
private float rx = 0; public MyText(Context context) {
super(context);
} public MyText(Context context, AttributeSet attrs) {
super(context, attrs);
} @Override
protected void drawSub(Canvas canvas) { paint.setTextSize(30);
canvas.drawText("MyText", rx, 03, paint);
} @Override
protected void logic() { rx += 3;
if (rx > getWidth()) {
rx = -paint.measureText("MyText");
}
}
}

到这里事实上BaseView还是有缺陷的,如onDraw在子类中是能够覆盖的。可是在BaseView中onDraw是做了一些处理的,假设在子类中将覆盖的onDraw方法中super.onDraw(canvas)去掉,那子类中的drawSub方法是不执行的。所以须要在父类中禁止子类覆盖onDraw方法,此时就能够使用final修师符。在BaseView的线程中,循环是死循环,可是我们要在离开屏幕的时让循环结束掉,下面就是改动后的完整的BaseView。

public abstract class BaseView extends View {

    private MyThread thread;

    public BaseView(Context context) {
super(context);
} public BaseView(Context context, AttributeSet attrs) {
super(context, attrs);
} 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();
} private boolean running = true; class MyThread extends Thread { @Override
public void run() { while (running) { logic(); postInvalidate(); try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

在 xml 中定义样式来影响显示效果

本课时仅仅有一件事要做:

我不想改动代码。可是我想要不一样的效果

本课时的知识要点包含:

在xml中定义样式和属性

在布局中使用属性(命名空间须要声明)

在代码中解析样式的属性

在代码中使用属性对显示效果产生影响

假如如今有这样一个需求:想绘制相同的文字。可是要绘制不同的行数和是否在屏幕上滚动。(依据上面的知识要点里实现

在xml中定义样式和属性 (res——>value——>attr.xml)

<?xml version="1.0" encoding="utf-8"?

>
<resources> <!-- 定义样式属性 -->
<declare-styleable name="NumText">
<attr name="lineNum" format="integer"></attr>
<attr name="xScroll" format="boolean"></attr>
</declare-styleable> </resources>

在布局中使用属性(命名空间须要声明)

<?

xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:nt="http://schemas.android.com/apk/res/com.example.myview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <com.nf.myview.v4.NumText
android:layout_width="match_parent"
android:layout_height="match_parent"
nt:lineNum="3"
nt:xScroll="true" /> </LinearLayout>

在代码中解析样式的属性 与 在代码中使用属性对显示效果产生影响

public class NumText extends BaseView {

    private Paint paint = new Paint();
private int lineNum = 0;
private float mx = 0;
private boolean xScroll = false; public NumText(Context context) {
super(context);
// TODO Auto-generated constructor stub
} public NumText(Context context, AttributeSet attrs) {
super(context, attrs); // 获取样式属性中的一个列表
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.NumText);
// 解析lineNum,方法參数介绍:index样式中的属性。defValue:默认值
lineNum = ta.getInt(R.styleable.NumText_lineNum, 1);// NumText_lineNum样式属性的标识方法 xScroll = ta.getBoolean(R.styleable.NumText_xScroll, false); ta.recycle();// 释放TypedArray
} @Override
protected void drawSub(Canvas canvas) {
// TODO Auto-generated method stub for (int i = 0; i < lineNum; i++) {
int textSize = 30 + i;
paint.setTextSize(textSize);
canvas.drawText("NumText", mx, textSize + textSize * i, paint);
} } @Override
protected void logic() { if (xScroll) { mx += 3;
if (mx > getWidth()) {
mx = -paint.measureText("NumText");
}
}
}
}

本课程中我们学习了自己定义View基础和原理。

你应当掌握了下面知识:

能够编写自己的自己定义View

知道增加逻辑线程的目的

理解抽象和封装自己定义View基类的目的

能够在xml中定义样式和属性并对显示效果进行影响

你能够使用这些技巧来编写几个自己的自己定义View。能够是包含文字或图形的,然后,通过xml样式属性的使用来影响它们的位置或其它显示效果。假设想继续提高,你能够继续在学习Android的粒子和动画效果系列文章。

自己定义 View 基础和原理的更多相关文章

  1. Android自己定义View基础篇(三)之SwitchButton开关

    自己定义View基础篇(二) 自己定义View基础篇(一) 自己定义View原理 我在解说之前,先来看看效果图,有图有真相:(转换gif图片效果太差) 那来看看真实图片: 假设你要更改样式,请改动例如 ...

  2. 51、自定义View基础和原理

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

  3. 自己定义View Layout过程 - 最易懂的自己定义View原理系列(3)

    前言 自己定义View是Android开发人员必须了解的基础 网上有大量关于自己定义View原理的文章.但存在一些问题:内容不全.思路不清晰.无源代码分析.简单问题复杂化等等 今天,我将全面总结自己定 ...

  4. Android画图系列(二)——自己定义View绘制基本图形

    这个系列主要是介绍下Android自己定义View和Android画图机制.自己能力有限.假设在介绍过程中有什么错误.欢迎指正 前言 在上一篇Android画图系列(一)--自己定义View基础中我们 ...

  5. Path类的最全面具体解释 - 自己定义View应用系列

    前言 自己定义View是Android开发人员必须了解的基础:而Path类的使用在自己定义View绘制中发挥着很关键的数据 网上有大量关于自己定义View中Path类的文章.但存在一些问题:内容不全. ...

  6. 安卓自己定义View进阶-Canvas之绘制基本形状

    Canvas之绘制基本形状 作者微博: @GcsSloop [本系列相关文章] 在上一篇自己定义View分类与流程中我们了解自己定义View相关的基本知识,只是,这些东西依然还是理论,并不能拿来(zh ...

  7. View基础知识

    一.View基础知识 View 是Android中所有控件的基类,是一种界面层的控件的一种抽象,代表了一个控件 1.View的位置参数 View的四个属性:top(左上角纵坐标)     left(左 ...

  8. Android艺术开发探索第四章——View的工作原理(上)

    这章就比较好玩了,主要介绍一下View的工作原理,还有自定义View的实现方法,在Android中,View是一个很重要的角色,简单来说,View是Android中视觉的呈现,在界面上Android提 ...

  9. Android 它们的定义View它BounceProgressBar

    转载请注明出处:http://blog.csdn.net/bbld_/article/details/41246247 [Rocko's blog] 之前几天下载了非常久没用了的桌面版酷狗来用用的时候 ...

随机推荐

  1. KM算法(Kuhn-Munkres)

    算法理论基础: 可行顶点标号 用l(v)表示顶点v的标号,w(uv)表示边(u,v)的权,对于赋权二分图G=(X,Y),若对每条边e=xy,均有l(x)+l(y)>=w(xy),则称这个标号为G ...

  2. Windows提高_2.2第二部分:用户区同步

    第二部分:用户区同步 同步和互斥 同步:就是按照一定的顺序执行不同的线程 互斥:当一个线程访问某一资源的时候,其它线程不能同时访问 多线程产生的问题 #include <stdio.h> ...

  3. Java排序算法全

    目录 Java排序算法代码 零. 排序基类 一. 选择排序 二. 插入排序 三. 希尔排序 四. 归并排序 1. 自顶向下 2. 自底向上 五. 快速排序 1. 基本版 2. 双路切分版 3. 三路切 ...

  4. myeclipse工具常用的用法

    1.  自动提示:窗口->首选项->Java->编辑器->内容辅助->自动激活,在下面的“Java的自动激活触发器里面填上“.abcdefghijklmnopqrstuv ...

  5. P1060 开心的金明(洛谷,动态规划递推,01背包轻微变形题)

    题目链接:P1060 开心的金明 基本思路: 基本上和01背包原题一样,不同点在于这里要的是最大重要度*价格总和,我们之前原题是 f[j]=max(f[j],f[j-v[i]]+p[i]); 那么这里 ...

  6. acm相关(纯转载)

    我觉得很好的文章,之所以放随笔是为了让大家看到这些优秀的博文 acm如何起步 acm重点题型 动态规划题目总结 背包九讲阅读网站

  7. POJ3616 Milking Time【dp】

    Description Bessie is such a hard-working cow. In fact, she is so focused on maximizing her producti ...

  8. Codeforces Round #530 (Div. 2) (前三题题解)

    总评 今天是个上分的好日子,可惜12:30修仙场并没有打... A. Snowball(小模拟) 我上来还以为直接能O(1)算出来没想到还能小于等于0的时候变成0,那么只能小模拟了.从最高的地方进行高 ...

  9. 配置servlet出现java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver

    拷贝一份sqljdbc.jar放到/WEB-INF/lib即可

  10. CCF201512-2 消除类游戏 java(100分)

    试题编号: 201512-2 试题名称: 消除类游戏 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 消除类游戏是深受大众欢迎的一种游戏,游戏在一个包含有n行m列的游戏棋盘上进 ...