View显示流程
1、onMeasure()测量宽高
2、ViewGroup布局,onLayout()
3、draw()视图显示自身的内容
1、绘制背景
2、为显示渐变框做一些准备动作
3、调用自身的onDraw(),(ViewGroup不需要)
4、dispatchDraw()绘制子视图(调用了drawChild()通知孩子draw()自身)
5、画滚动条
View的自定义的认识
要自定义View的显示效果,可以重写其onDraw方法,然后在Canvas上绘制内容即可
public class MyView extends View {
public MyView(Context context) {
super(context);
Log.e("m_tag","MyView(Context context)");
init();
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
Log.e("m_tag","MyView(Context context, AttributeSet attrs)");
init();
}
private Paint paint;
private void init(){
setBackgroundColor(0xff999999);
//创建画笔对象
paint = new Paint();
//设置画笔的颜色
// paint.setColor(0xFFFF0000);
paint.setColor(Color.RED);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画图 : 画布 画笔 颜色
canvas.drawRect(50,100,150,200,paint);
}
}
使用:
<com.xykj.viewdemo.MyView
android:layout_width="match_parent"
android:layout_height="300dp" />
Canvas的基本绘制
//画矩形
//前面4个参数表示矩形的左上右下,第五个参数表示画笔
canvas.drawRect(50,100,150,200,paint);
//创建一个矩形对象
Rect rect = new Rect(150,300,350,400);
canvas.drawRect(rect,paint);
//画一个圆
//(圆心x坐标,圆心y坐标,半径,画笔)
canvas.drawCircle(50,300,50,paint);
Paint的基本设置
设置颜色
paint.setColor(0xFFFF0000);
paint.setColor(Color.RED); //从Color类中获取的颜色值
//消除锯齿
paint.setAntiAlias(true);
//分开设置各个颜色参数0x00-0xff 十进制就是0-255
paint.setARGB(255,20,50,100);
paint.setAlpha(100); //设置透明度
//设置画笔样式STROKE表示空心 FILL表示实心
paint.setStyle(Paint.Style.STROKE);
//空心边框宽度
paint.setStrokeWidth(5);
文本相关
//文本大小
paint.setTextSize(20); //文本大小
paint.setFakeBoldText(true); //加粗
paint.setTextSkewX(45); //倾斜
View的刷新
如果要修改视图显示的内容,需要让视图重新onDraw,然后在画布上画上新的内容即可,要让视图重新画需要使用其刷新方法
主线程刷新:
invalidate();-->使视图重新绘制
结合触摸监听实现刷新
public class RefreshView extends View {
public RefreshView(Context context) {
super(context);
init();
}
public RefreshView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private Paint paint;
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
}
//圆的圆心
private int cx = 50, cy = 100;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画圆
canvas.drawCircle(cx, cy, 50, paint);
}
//视图的触摸监听
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取触摸的行为
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
//手指触摸到视图的瞬间
//得到触控点跟视图左上角点的坐标(相对位置)
int x = (int) event.getX();
// int pX = event.getRawX(); //得到触控点和屏幕左上角的位置
int y = (int) event.getY();
cx = x;
cy = y;
invalidate(); //刷新视图(视图会重新绘制)
break;
case MotionEvent.ACTION_MOVE:
//按下之后离开之前的整个过程(重复执行)
cx = (int) event.getX();
cy = (int) event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
//手指离开视图的瞬间
break;
}
return true;
}
}
子线程刷新,需要使用postInvalidate方法,该方法等效于Handler+invalidate方式
private Thread changeThread;
//随机数(产生0-255之间的数字)
private Random r = new Random();
public void startChangeColor() {
if (null != changeThread && changeThread.isAlive()) {
return;
}
changeThread = new Thread() {
@Override
public void run() {
try {
while(true) {
Thread.sleep(500);
//改变画笔颜色
paint.setARGB(255,r.nextInt(256),r.nextInt(256),r.nextInt(256));
//刷新视图(子线程)
postInvalidate(); //等效于Handler+invalidate
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
changeThread.start();
}
Canvas其他的各种画
//圆角矩形
RectF rectF = new RectF(300,10,400,80);
//(矩形区域,圆角x方向半径,圆角y方向半径)
canvas.drawRoundRect(rectF,20,20,paint);
//画椭圆
RectF oval = new RectF(300,100,400,300);
canvas.drawOval(oval,paint);
RectF arcRectF = new RectF(450,10,550,110);
//画弧线或者扇形
//第一个参数:扇形所在的圆所在的矩形区域
//第二个参数:起始角度
//第三个参数:跨越的总度数
//第四个参数:true表示扇形,false表示弧线
//第五个参数:画笔
canvas.drawArc(arcRectF,-90,90,false,paint);
Path p = new Path();
//指定起始点
p.moveTo(450,100);
//画直线到下一个点
p.lineTo(600,200);
p.lineTo(500,400);
//画贝塞尔曲线
//前面两个表示弧度控制点,后面两个参数表示弧线的终点
p.quadTo(300,300,500,200);
//将最后一个点连接到起始点上
p.close();
//画路径
canvas.drawPath(p,paint);
子线程刷新
子线程中如果不想直接使用Handler提交到主线程刷新,那么也可以直接使用runOnUIThread方法
new Thread() {
@Override
public void run() {
...
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setProgress(pro);
}
});
...
}
}.start();
视图显示流程
1、onMeasure 测量
2、ViewGroup布局,onLayout
3、View显示效果draw
onMeasure,测量
在onMeasure方法中上一级会告诉该视图宽以及高的情况,其中包括模式和尺寸
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}
获取模式方法
int mode = MeasureSpec.getMode(measureSpc)
EXACTLY表示固定值,如math_parent 100dp
AT_MOST:父容器会给一个最大的参考尺寸,
还在可以在该参考区域内任意获取尺寸(可能按照自己的内容来)如:wrap_content
UNSPECIFIED:未指定,一般不会给开发人员来用
获取大小
int size = MeasureSpec.getSize(measureSpc);
设置测量结果(必须要有)
setMeasuredDimension(w, h);
文本绘制
//(绘制的文本,参考点x坐标,参考点y坐标,画笔)
canvas.drawText(str,10,50,paint);
文本高相关的信息的获取
Paint.FontMetrics fm = paint.getFontMetrics();
top:文本顶部作为
bottom:文本底部的位置
ascent:基准线到顶部的距离
descent:基准线到底部的距离
如果需要获取某一段文本的宽高(占据的区域),可以使用paint的getTextBound方法来获取
Rect textBound = new Rect();
//获取文本所占区域(默认参考点是0,0所以得到的top是负数)
//(字符串对象,字符起始下标,字符的结束下标,文本区域)
paint.getTextBounds(str,0,str.length(),textBound);
自定义参考线
drawTextOnPath(String text,Path path, float hOffset,
float vOffset,Paint paint)
参数:
text:表示绘制的文本
path:参考线的路径
hOffset:在水平方向上文本和参考点的偏移,正数往右,负数往左
vOffset:在垂直方向上文本和参考点的偏移,正数往下,负数往上
paint:画笔
图像变换
图像变换需要使用Matrix来处理
scale_x skew_x tran_x
skew_y scale_y tran_y
pers_0 pers_1 pers_2
初始状态
Matrix matrix = new Matrix(); //图像的默认状态(1倍 0度 偏移0,0 倾斜0度)
操作——set:
//设置一个状态
//设置旋转(旋转的角度,旋转中心点的x坐标,旋转的中心点y坐标)
matrix.setRotate(60, bWidth / 2, bHeight / 2);
//设置平移(x方向的偏移量,y方向的偏移量)
matrix.setTranslate(100,200);
//设置缩放(宽的倍数,高的倍数)
matrix.setScale(2,3);
设置会将之前的状态覆盖
post:基于之前的状态,添加新的状态(向后加)
//设置一个状态
matrix.setRotate(60, bWidth / 2, bHeight / 2);
//添加平移
matrix.postTranslate(100,200);
//添加缩放
matrix.postScale(2,3);
pre:添加状态(向前加)
区别:平移(100,200) 然后旋转30度
matrix.postTranslate(100,200);
matrix.postRotate(30);
matrix.setRotate(30);
matrix.preTranslate(100,200);
多点触控
多点触控的行为需要结合MotionEvent.ACTION_MASK来过滤出来
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//单个触控点按下(接触视图瞬间)
Log.e("m_tag","单个down");
break;
case MotionEvent.ACTION_POINTER_DOWN:
//多个触控点按下
Log.e("m_tag","==多个down");
break;
case MotionEvent.ACTION_MOVE:
//只要有触控点触摸在视图上都会执行move
Log.e("m_tag","move");
break;
case MotionEvent.ACTION_UP:
//单个触控点离开的瞬间
Log.e("m_tag","单个up");
break;
case MotionEvent.ACTION_POINTER_UP:
//多个触控点离开
Log.e("m_tag","==多个up");
break;
}
return true;
}