自己定义 View 基础和原理
课程背景:
在 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 基础和原理的更多相关文章
- Android自己定义View基础篇(三)之SwitchButton开关
自己定义View基础篇(二) 自己定义View基础篇(一) 自己定义View原理 我在解说之前,先来看看效果图,有图有真相:(转换gif图片效果太差) 那来看看真实图片: 假设你要更改样式,请改动例如 ...
- 51、自定义View基础和原理
一.编写自己的自定义View最简单的自定义View,继承View通过覆盖View的onDraw方法来实现自主显示利用Canvas和paint来绘制显示元素(文字,几何图形等) <com.myvi ...
- 自己定义View Layout过程 - 最易懂的自己定义View原理系列(3)
前言 自己定义View是Android开发人员必须了解的基础 网上有大量关于自己定义View原理的文章.但存在一些问题:内容不全.思路不清晰.无源代码分析.简单问题复杂化等等 今天,我将全面总结自己定 ...
- Android画图系列(二)——自己定义View绘制基本图形
这个系列主要是介绍下Android自己定义View和Android画图机制.自己能力有限.假设在介绍过程中有什么错误.欢迎指正 前言 在上一篇Android画图系列(一)--自己定义View基础中我们 ...
- Path类的最全面具体解释 - 自己定义View应用系列
前言 自己定义View是Android开发人员必须了解的基础:而Path类的使用在自己定义View绘制中发挥着很关键的数据 网上有大量关于自己定义View中Path类的文章.但存在一些问题:内容不全. ...
- 安卓自己定义View进阶-Canvas之绘制基本形状
Canvas之绘制基本形状 作者微博: @GcsSloop [本系列相关文章] 在上一篇自己定义View分类与流程中我们了解自己定义View相关的基本知识,只是,这些东西依然还是理论,并不能拿来(zh ...
- View基础知识
一.View基础知识 View 是Android中所有控件的基类,是一种界面层的控件的一种抽象,代表了一个控件 1.View的位置参数 View的四个属性:top(左上角纵坐标) left(左 ...
- Android艺术开发探索第四章——View的工作原理(上)
这章就比较好玩了,主要介绍一下View的工作原理,还有自定义View的实现方法,在Android中,View是一个很重要的角色,简单来说,View是Android中视觉的呈现,在界面上Android提 ...
- Android 它们的定义View它BounceProgressBar
转载请注明出处:http://blog.csdn.net/bbld_/article/details/41246247 [Rocko's blog] 之前几天下载了非常久没用了的桌面版酷狗来用用的时候 ...
随机推荐
- C++11:using 的各种作用
C++11中using关键字的主要作用是:为一个模板库定义一个别名. 文章链接:派生类中使用using别名改变基类成员的访问权限 一.<Effective Modern C++>里有比较 ...
- How a stack frame works 栈帧的要素与构建步骤
http://en.citizendium.org/wiki/Stack_frame To use a stack frame, a thread keeps two pointers, often ...
- Mybatis学习总结三(动态SQL)
通过mybatis提供的各种标签方法实现动态拼接sql. 一.if 和 where <select id="findUserList" parameterType=" ...
- JAVA基础——集合Iterator迭代器的实现
一.迭代器概述 1.什么是迭代器? 在Java中,有很多的数据容器,对于这些的操作有很多的共性.Java采用了迭代器来为各种容器提供了公共的操作接口.这样使得对容器的遍历操作与其具体的底层实现相隔离, ...
- 【2018百度之星资格赛】F 三原色图 - 最小生成树
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6349 Knowledge Point: 最小生成树算法Prim&Kruskal Summari ...
- 洛谷——P1273 有线电视网
P1273 有线电视网 题目大意: 题目描述 某收费有线电视网计划转播一场重要的足球比赛.他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树 ...
- 「 Luogu P1231 」 教辅的组成
题目大意 有 $\text{N1}$ 本书 $\text{N2}$本练习册 $\text{N3}$本答案,一本书只能和一本练习册和一本答案配对.给你一些书和练习册,书和答案的可能的配对关系.问你最多可 ...
- Oracle创建用户、角色、授权、建表空间
oracle数据库的权限系统分为系统权限与对象权限.系统权限( database system privilege )可以让用户执行特定的命令集.例如,create table权限允许用户创建表,gr ...
- vue中的跨域
proxyTable: { '/zabbix_api': { target: 'http://10.88.22.8',//设置你调用的接口域名和端口号 别忘了加http changeOrigin: t ...
- journals in Fluid Dynamics
annual review of fluid mechanicsjournal of fluid mechanicsphysics of fluidjournal of flow and struct ...