Android(安卓)自定义 View 之使用 SurfaceView 实现动画
16lz
2021-01-25
本文参考文献:《疯狂Android讲义 : 第2版 》
虽然前面大量介绍了使用自定义 View 来进行绘图,但 View 的绘图机制存在如下缺陷:
- View 缺乏双缓冲机制;
- 当程序需要更新 View 上的图像时,程序必须重绘 View 上显示的整张图片;
- 新线程无法直接更新 View 组件。
由于 View 存在上述缺陷,所以通过自定义 View 来实现绘图,尤其是游戏中的绘图时性能并不好。Android 提供了一个 SurfaceView 来代替 View,在实现游戏绘图方面,SurfaceView 比 View 更加出色,因此一般推荐使用 SurfaceView。
布局文件的内容如下:
<?xml version="1.0" encoding="utf-8"?>
主程序文件的代码如下:
package com.toby.personal.testlistview;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;public class MainActivity extends AppCompatActivity { final private static String TAG = "Toby_Test"; private SurfaceHolder holder; private Paint paint; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); paint = new Paint(); SurfaceView surface = (SurfaceView) findViewById(R.id.show); holder = surface.getHolder(); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { Canvas canvas = holder.lockCanvas(); Bitmap back = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.plane); canvas.drawBitmap(back, 0, 0, null); holder.unlockCanvasAndPost(canvas); holder.lockCanvas(new Rect(0, 0, 0, 0)); holder.unlockCanvasAndPost(canvas); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }); surface.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { int cx = (int) event.getX(); int cy = (int) event.getY(); Canvas canvas = holder.lockCanvas(new Rect(cx - 50, cy - 50, cx + 50, cy + 50)); canvas.save(); canvas.rotate(30, cx, cy); paint.setColor(Color.RED); canvas.drawRect(cx - 40, cy - 40, cx, cy, paint); canvas.restore(); paint.setColor(Color.GREEN); canvas.drawRect(cx, cy, cx + 40, cy + 40, paint); holder.unlockCanvasAndPost(canvas); } return false; } }); }}
实例:基于 SurfaceView 开发示波器
SurfaceView 与普通 View 还有一个重要的区别:View 的绘图必须在当前 UI 线程中进行;但是 SurfaceView 是由 SurfaceHolder 来完成的。这样就避免了阻塞主线程。
下面是主布局代码的内容:
<?xml version="1.0" encoding="utf-8"?>
主程序文件的代码如下:
package com.toby.personal.testlistview;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import java.util.Timer;import java.util.TimerTask;public class MainActivity extends AppCompatActivity { final private static String TAG = "Toby_Test"; private SurfaceHolder holder; private Paint paint; final int HEIGHT = 320; final int WIDTH = 640; final int X_OFFSET = 40; private int cx = X_OFFSET; int centerY = HEIGHT / 2; Timer timer = new Timer(); TimerTask task = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final SurfaceView surface = (SurfaceView) findViewById(R.id.show); holder = surface.getHolder(); paint = new Paint(); paint.setStrokeWidth(3); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { drawBack(holder); } @Override public void surfaceDestroyed(SurfaceHolder holder) { timer.cancel(); } }); } private void drawBack(SurfaceHolder holder) { Canvas canvas = holder.lockCanvas(); canvas.drawColor(Color.GRAY); Paint p = new Paint(); p.setColor(Color.BLACK); p.setStrokeWidth(3); canvas.drawLine(X_OFFSET, centerY, WIDTH, centerY, p); canvas.drawLine(X_OFFSET, 40, X_OFFSET, HEIGHT, p); holder.unlockCanvasAndPost(canvas); holder.lockCanvas(new Rect(0, 0, 0, 0)); holder.unlockCanvasAndPost(canvas); } public void drawSinLine(View view) { doDraw(view); } private void doDraw(final View source) { drawBack(holder); cx = X_OFFSET; if (task != null) { task.cancel(); } task = new TimerTask() { @Override public void run() { final double radian = (cx - X_OFFSET) * 2 * Math.PI / 150; final int cy = source.getId() == R.id.sin ? centerY - (int) (100 * Math.sin(radian)) : centerY - (int) (100 * Math.cos(radian)); paint.setColor(source.getId() == R.id.sin ? Color.GREEN : Color.RED); Canvas canvas = holder.lockCanvas(new Rect(cx, cy - 2, cx + 2, cy + 2)); canvas.drawPoint(cx, cy, paint); cx++; if (cx > WIDTH) { task.cancel(); task = null; } holder.unlockCanvasAndPost(canvas); } }; timer.schedule(task, 0, 30); } public void drawCosLine(View view) { doDraw(view); }}
该示例的运行效果如下:
显示效果更多相关文章
- 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
- 一款常用的 Squid 日志分析工具
- GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
- RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
- Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
- Android(安卓)NDK下编译google protocol buffer(protobuf)
- Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果
- android apk反编译到java源码的实现方法
- 在/data/下创建文件的权限问题