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

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

import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View; public class LogicView extends View { // 实例化 画笔。
private Paint paint = new Paint();
//
private float rx = 0;
// 添加线程属性
private MyThread thread;
/**
* 绘制在文字下,30的地方。
* RectF(float left, float top, float right, float bottom)
* 0 30+30 宽度100 60+100
*/
private RectF rectF = new RectF(0, 60, 100, 160);
// 区间角度
private float sweepAngle = 0; // 在布局当中使用。
public LogicView(Context context, AttributeSet attrs) {
super(context, attrs);
} // 在代码当中使用。
public LogicView(Context context) {
super(context);
} @Override
protected void onDraw(Canvas canvas) {
paint.setTextSize(30);
canvas.drawText("LogicView", rx, 30, paint);
/**
* 绘制一个圆。
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
Paint paint)
* sweepAngle:区间角度。
* useCenter:true ,false. 绘制的效果不同。
*/
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()) {
/**
* 走出屏幕后,重新回到屏幕。
* 0 - 文字的宽度,这样 “LogicView”文字,从左边出来的时候,
* 就可以从w开始一点点移出屏幕。
*/
rx = 0 - paint.measureText("LogicView");
} sweepAngle++;
if (sweepAngle > 360) {
sweepAngle = 0;
} // 取到 0-255的随机数。
int r = rand.nextInt(256);
int g = rand.nextInt(256);
int b = rand.nextInt(256); // 设置颜色。
paint.setARGB(255, r, g, b); postInvalidate(); // 重绘
try {
Thread.sleep(30); // 睡眠30毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} }
把以上代码进行封装。
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();
}
}
}
} }
import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet; 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, AttributeSet attrs) {
super(context, attrs);
} public LogicView(Context context) {
super(context);
} @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");
} sweepAngle++;
if (sweepAngle > 360) {
sweepAngle = 0;
} int r = rand.nextInt(256);
int g = rand.nextInt(256);
int b = rand.nextInt(256); paint.setARGB(255, r, g, b);
} }
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));
51、自定义View基础和原理的更多相关文章
- 自己定义 View 基础和原理
课程背景: 在 Android 提供的系统控件不能满足需求的情况下,往往须要自己开发自己定义 View 来满足需求,可是该怎样下手呢.本课程将带你进入自己定义 View 的开发过程,来了解它的一些原理 ...
- 自定义View基础 (1)
前言 自定义View原理是Android开发者必须了解的基础: 在了解自定义View之前,你需要有一定的知识储备: 本文将全面解析关于自定义View中的所有知识基础. 目录 目录 1. View的分类 ...
- Android自定义View基础
自定义控件, 视频教程 http://www.jikexueyuan.com/course/1748.html 1. 编写自定义view 2. 加入逻辑线程 3. 提取和封装自定义view 4. 利用 ...
- android自定义View的绘制原理
每天我们都会使用很多的应用程序,尽管他们有不同的约定,但大多数应用的设计是非常相似的.这就是为什么许多客户要求使用一些其他应用程序没有的设计,使得应用程序显得独特和不同. 如果功能布局要求非常定制化, ...
- 自定义VIew基础
一.坐标 ①.通过View获取坐标,通过调用getLeft().getRight()...方法获取坐标. 1.获取到的是相对于View父控件的位置 2.指的是左上角和右下角的x,y值 3.View还提 ...
- 安卓自定义View基础 --坐标系,角度弧度,颜色
转自:https://www.gcssloop.com/customview/CustomViewIndex/ 1.坐标系 2.角度弧度 3.颜色 一.屏幕坐标系和数学坐标系的区别 由于移动设备一般定 ...
- 自定义View Draw过程(4)
目录 目录 1. 知识基础 具体请看我写的另外一篇文章:自定义View基础 - 最易懂的自定义View原理系列 2. draw过程作用 绘制View视图 3. draw过程详解 同measure.la ...
- 自定义View Layout过程 (3)
目录 目录 1. 知识基础 具体请看我写的另外一篇文章:(1)自定义View基础 - 最易懂的自定义View原理系列 2. 作用 计算View视图的位置. 即计算View的四个顶点位置:Left.To ...
- 自定义View Measure过程(2)
目录 目录 1. 作用 测量View的宽/高 在某些情况下,需要多次测量(measure)才能确定View最终的宽/高: 在这种情况下measure过程后得到的宽/高可能是不准确的: 建议在layou ...
随机推荐
- 【Python3 爬虫】17_爬取天气信息
需求说明 到网站http://lishi.tianqi.com/kunming/201802.html可以看到昆明2018年2月份的天气信息,然后将数据存储到数据库. 实现代码 #-*-coding: ...
- MFC中获取各个窗口之间的句柄或者指针对象的方法
MFC在非常多的对话框操作中,我们常常要用到在一个对话框中调用还有一个对话框的函数或变量.能够用例如以下方法来解决. HWND hWnd=::FindWindow(NULL,_T("S ...
- 检查linux网络的状况
http://hi.baidu.com/dr_wang/blog/item/2952f7458659c306cefca3cc.html
- PHP中文乱码的常见解决方法总结
PHP中文乱码是PHP开发中的常见问题之一.PHP中文乱码有时发生在网页本身,有些产生在于MySQL交互的过程中,有时与操作系统有关.下面进行一番总结. 一.首先是PHP网页的编码 1. php文件本 ...
- 转: 利用RabbitMQ、MySQL实现超大用户级别的消息在/离线收发
由于RabbitMQ中只有队列(queue)才能存储信息,所以用RabbitMQ实现超大用户级别(百万计)的消息在/离线收发需要对每一个用户创建一个永久队列. 但是RabbitMQ节点内存有限,经测试 ...
- 冻结 锁定 固定 行 列 表头 抬头 html table jquery 全兼容常见浏览器
转:http://www.cnblogs.com/sorex/archive/2011/06/30/2093499.html <!DOCTYPE html PUBLIC "-//W3C ...
- Hibernate关联关系(二) Cascade级联
1.cascade定义的是关系两端对象到对象的级联关系:而inverse定义的是关系和对象的级联关系. all : 所有情况下均进行关联操作. none:所有情况下均不进行关联操作.这是默认值. ...
- II7.5配置IIS支持2G文件下载
IIS默认支持下载在20M让IIS7.5支持大文件下载,有两个地方 1.打IIS管理器->asp->点限制属性+->设置最大请求实体主体限制为2147483648 2.打开路径C:\ ...
- 面试、笔试中常用的SQL语句(数据库知识必杀)一共50个!!!
Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,Tname) 教师表 ...
- 多线程-wait/notify/notifyAll
引言 在Java中,可以通过配合调用Object对象的wait,notify和notifyAll来实现线程间的通信. 在线程中调用wait方法,将阻塞带带其他线程的通知(其他线程调用notify或no ...