View
自定义视图主要涉及四个方面:绘图、交互、性能和封装
绘图
主要涉及两个对象:画布(Canvas)和画笔(Paint),画布主要解决画什么的问题,在画布上可以绘制各种各样的图形,可以通过移动、放大、缩小等来实现不同的效果;画笔主要解决如何画、怎么画的问题,通过控制画笔的大小,线条的粗细,路径上所要实现的特殊效果(填充、文字大小、字体等)
交互
涉及触摸(TouchEvent)和动画(Animation),设计触摸或手势动作过程中引发的动画效果,如过渡、渐变等
性能
绘图的onDraw()方法工作在主线程上,要考虑主线程安全问题,遇到复杂的绘制内容可能会导致用户界面卡顿,此时可以考虑使用SurfaceView开辟出另一个绘画的表面,使用副线程绘图
封装
一方面可以将一些属性(Attributes)封装起来,在自定义视图时就可以通过这些属性去设置视图的一些特性;另一方面考虑到视图的通用性,需要计算它们的尺寸(Measure),如果是在特定的场合使用,可以不考虑尺寸问题,但如果是泛用性广的视图,类似于控件,就需要考虑尺寸问题
帧速率
需要在16ms内绘制完所有内容,否则会掉帧
生命周期
measure():测量自身尺寸
layout():将要放置的位置
因为有60帧的限制,只能通过dispatchToDraw()调度告诉系统将要在下一个帧来临时绘制图形
通过invalidate()通知系统在下一帧来临时刷新视图
FindMe(路径与遮罩)
找到隐藏在阴影下的脸,实现效果如下:
- 创建一个FindMe类继承于View,在R.layout.activity_main.xml中拖入一个view,类型改为FindMe,填充整个屏幕
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
<com.example.myblog.FindMe
android:id="@+id/findMe"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
- 在FindMe中设置一个drawableToBitamp方法将图片资源转换为位图对象用于接下来的绘制
private Bitmap drawableToBitamp(Drawable drawable){
Bitmap bitmap = null;
//图片位深,PixelFormat.OPAQUE代表没有透明度,RGB_565就是没有透明度的位深,否则就用ARGB_8888
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
//创建一个空的Bitmap,大小为100X100
bitmap = Bitmap.createBitmap(100,100,config);
//在bitmap上创建一个画布
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, 100, 100);
drawable.draw(canvas);
return bitmap;
}
- 定义变量faceX和faceY用来记录图片的位置,写一个randomPosition函数来记录图片的随机位置
private void randomPosition(){
Random random = new Random();
//图片尺寸为100X100,设置-100来使图片不离开屏幕范围内
faceX = random.nextInt(getWidth()-100);
faceY = random.nextInt(getHeight()-100);
}
- 在onDraw方法中将图片画在屏幕上
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制的目标为图片,画笔可以为空
canvas.drawBitmap(drawableToBitamp(getResources().getDrawable(R.drawable.ic_baseline_face_24)), faceX, faceY, null);
Paint paint = new Paint();
//绘制鼠标移动路径
canvas.drawPath(path, paint);
}
- 处理鼠标事件,ACTION_DOWN鼠标按下,ACTION_MOVE鼠标移动,ACTION_UP鼠标松开
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
//鼠标按下时,生成一个随机位置赋给图片
randomPosition();
//每次改变时,需要重置路径
path.reset();
path.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW);
path.addCircle(event.getX(), event.getY(), 100, Path.Direction.CCW);
//每次处理完事件后需刷新视图
invalidate();
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
path.reset();
//鼠标移动时,创建一个跟随路径,用一个黑色矩形填满屏幕,中间挖出一个圆形孔
path.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW);
//鼠标位置作为圆心
path.addCircle(event.getX(), event.getY(), 100, Path.Direction.CCW);
invalidate();
}else if(event.getAction() == MotionEvent.ACTION_UP){
path.reset();
invalidate();
}
performClick();
return true;
}
@Override
public boolean performClick() {
return super.performClick();
}
SurfaceView
在View上进行绘图,View和用户界面共用一个绘图的表面,因此不能绘制太过复杂的东西,否则会拖慢用户界面,造成用户界面卡顿,影响用户体验
SurfaceView在另外一个独立的表面上进行绘制,和用户界面无关,可以高效刷新界面,常用于游戏开发中
例如,绘制一个两万个圈的同心圆,用View绘制可以通过ProgressBar明显监测到卡顿,而SurfaceView十分流畅
View
@Override
protected void onDraw(Canvas canvas) {
Random random = new Random();
canvas.drawColor(Color.BLACK);
int x = 0;
while (x < 20000) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(colors[random.nextInt(4)]);
canvas.drawCircle(centerX, centerY, x, paint);
x++;
}
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
centerX = event.getX();
centerY = event.getY();
invalidate();
return super.onTouchEvent(event);
}
SurfaceView
@Override
public boolean onTouchEvent(MotionEvent event) {
centerX = event.getX();
centerY = event.getY();
Random random = new Random();
Canvas canvas = getHolder().lockCanvas();
canvas.drawColor(Color.BLACK);
int x = 0;
while (x<20000){
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(colors[random.nextInt(4)]);
canvas.drawCircle(centerX,centerY,x, paint);
x++;
}
getHolder().unlockCanvasAndPost(canvas);
return super.onTouchEvent(event);
}
作者:许宝文
原文链接 https://blog.csdn.net/weixin_61586825/article/details/128166946