SurfaceView 实现高性能的绘制
来源:互联网 发布:淘宝商城女装新款时尚 编辑:程序博客网 时间:2024/06/12 00:43
先说说自定义 View 绘图机制的缺陷:
1. View 缺乏双缓冲机制;
2. 当程序需要更新 View 上的图片时,程序必须重绘 View 上显示的整张图片;
3. 新线程无法直接更新 View 组件。
由于 View 存在上述的缺陷,所以通过自定义 View 来实现绘图,尤其是游戏中的绘图时性能并不好。Android 提供了一个 SurfaceView 来代替 View ,在实现游戏绘图方面,SurfaceView 比 View 更加出色。
在 Android UI 开发中一般遵循这样的规定:不要在主线程之外的线程中修改任何与 View 相关的属性。但是 SurfaceView 和 TextureView 这两个类则不遵循这个规定,它们专门设计用来在后台线程中执行绘制命令,并将绘制内容展现在屏幕上。
SurfaceView 一般会与 SurfaceHolder 结合使用。SurfaceView非常独特,与传统 View 的原理有很大差异。当实例化一个 SurfaceView 时,实际上会在 View 的位置创建里一个窗口,所以实际上会有两层窗口,第二层就是 SurfaceHolder 该层位于当前窗口的下方,该层才是真正的视图层。然后 View 控件会在顶层窗口简单地“打一个洞”来透明地显示下方窗口的内容。SurfaceView 还是一种非常静态的视图,无法对动画或任何形式的变换做出很好的响应。
调用 SurfaceView 的 getHolder() 方法即可获取SurfaceView 关联的 SurfaceHolder。SurfaceHolder 提供了如下方法来获取 Canvas 对象。
------> Canvas lockCanvas():锁定整个 SurfaceView 对象,获取该 SurfaceView 上的 Cnavas。
------> Canvas lockCanvas(Rect dirty):锁定 SurfaceView 上 Rect 划分的区域,获取该 SurfaceView 上的 Canvas。
上面的第二个方法获取指定的 Canvas 时,SurfaceView 将只对 Rect 所“圈”出来的区域进行绘图,通过这种方式可以提高画面的更新速度。
当通过 lockCanvas() 获取指定 SurfaceView 上的 Canvas 之后,接下来程序就可以调用 Canvas 进行绘图了, Canvas 绘图完成后通过如下方法来释放绘图、提交所绘制的图形。
------> unlockCanvasAndPost(canvas)
需要注意的是,当调用 SurfaceHolder 的 unlockCanvasAndPost() 方法之后,该方法之前所绘制的图形还处于缓冲区中,下一次 lockCanvas() 方法锁定的区域可能会 “遮挡” 它。
选用 SurfaceView 的情况:一般来说,如果程序或游戏界面的动画元素较多时,而且很多动画元素的移动都需要通过定时器来控制,就可以考虑使用 SurfaceView,而不是 View。
下面这个示例从其他地方摘取,只修改了其中的少许代码。该示例点击屏幕就可以在相应的位置出现图片,点击按钮则清空所有。在该示例中用户可以不限次数的单击黑色区域,不用担心内存问题,因为绘制代码只是用一张位图来绘制所有的图形。效果如下:
布局文件,content_main.xml :
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.crazy.surfaceviewtest.MainActivity" tools:showIn="@layout/activity_main"> <Button android:id="@+id/button_erase" android:text="清除" android:layout_width="match_parent" android:layout_height="wrap_content" /> <SurfaceView android:id="@+id/surface" android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" /></FrameLayout>
MainActivity.java :
package com.crazy.surfaceviewtest;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import java.util.ArrayList;public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener, SurfaceHolder.Callback{ private SurfaceView mSurface; private DrawingThread mThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); findViewById(R.id.button_erase).setOnClickListener(this); mSurface = (SurfaceView)findViewById(R.id.surface); mSurface.setOnTouchListener(this); mSurface.getHolder().addCallback(this); } /** * 当 SurfaceView 被创建时回调该方法 */ @Override public void surfaceCreated(SurfaceHolder holder) { mThread = new DrawingThread(holder, BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)); mThread.start(); } /** * 当一个 SurfaceView 的格式或者大小发生改变时回调该方法 */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mThread.updateSize(width, height); } /** * 当 SurfaceView 将要被销毁时回调该方法 */ @Override public void surfaceDestroyed(SurfaceHolder holder) { mThread.quit(); mThread = null; } @Override public void onClick(View v) { mThread.clearItems(); } @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { mThread.addItem((int)event.getX(), (int)event.getY()); } return true; } /** * HandlerThread 是一个方便的框架辅助类,用来生成后台工作线程以处理收到的消息 */ private static class DrawingThread extends HandlerThread implements Handler.Callback { private static final int MSG_ADD = 100; private static final int MSG_MOVE = 101; private static final int MSG_CLEAR = 102; private int mDrawingWidth, mDrawingHeight; private SurfaceHolder mDrawingSurface; private Paint mPaint; private Handler mReceiver; private Bitmap mIcon; private ArrayList<DrawingItem> mLocations; // 定义一个记录图像是否开始渲染的旗帜 private boolean mRunning; private class DrawingItem { // 当前位置的标识 int x, y; // 动作方向的标识 boolean horizontal, vertical; public DrawingItem(int x, int y, boolean horizontal, boolean vertical) { this.x = x; this.y = y; this.horizontal = horizontal; this.vertical = vertical; } } public DrawingThread(SurfaceHolder holder, Bitmap icon) { super("DrawingThread"); mDrawingSurface = holder; mLocations = new ArrayList<>(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mIcon = icon; } @Override protected void onLooperPrepared() { mReceiver = new Handler(getLooper(), this); // 开始渲染 mRunning = true; mReceiver.sendEmptyMessage(MSG_MOVE); } @Override public boolean quit() { // 退出之前清除所有的消息 mRunning = false; mReceiver.removeCallbacksAndMessages(null); return super.quit(); } @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_ADD: // 在触摸的位置创建一个新的条目,该条目的开始方向是随机的 DrawingItem newItem = new DrawingItem(msg.arg1, msg.arg2, Math.round(Math.random()) == 0, Math.round(Math.random()) == 0); mLocations.add(newItem); break; case MSG_CLEAR: // 删除所有对象 mLocations.clear(); break; case MSG_MOVE: if (!mRunning) return true; // 渲染一帧 // 锁定 SurfaceView,并返回到要绘图的 Canvas Canvas canvas = mDrawingSurface.lockCanvas(); if (canvas == null) { break; } // 首先清空 Canvas // 如果没有这句代码,当图标移动时会在图标之前的位置出现拖尾痕迹 canvas.drawColor(Color.BLACK); // 绘制每个条目 for (DrawingItem item : mLocations) { // 更新位置 item.x += (item.horizontal ? 5 : -5); if (item.x >= (mDrawingWidth - mIcon.getWidth())) { item.horizontal = false; } if (item.x <= 0) { item.horizontal = true; } item.y += (item.vertical ? 5 : -5); if (item.y >= (mDrawingHeight - mIcon.getHeight())) { item.vertical = false; } if (item.y <= 0) { item.vertical = true; } canvas.drawBitmap(mIcon, item.x, item.y, mPaint); } // 解锁 Canvas,并渲染当前的图像 mDrawingSurface.unlockCanvasAndPost(canvas); break; } // 发送下一帧 if (mRunning) { mReceiver.sendEmptyMessage(MSG_MOVE); } return true; } public void updateSize(int width, int height){ mDrawingWidth = width; mDrawingHeight = height; } public void addItem(int x, int y) { // 通过 Message 参数将位置传给处理程序 Message msg = Message.obtain(mReceiver, MSG_ADD, x, y); mReceiver.sendMessage(msg); } public void clearItems(){ mReceiver.sendEmptyMessage(MSG_CLEAR); } }}
如果没有调用 drawColor() 方法,则会出现下面的效果:
1 0
- SurfaceView 实现高性能的绘制
- Android SurfaceView的绘制详解
- Surfaceview的绘制与应用
- SurfaceView实现手势绘制和视频播放
- android SurfaceView绘制实现原理解析
- Android使用自定义View继承SurfaceView实现动态折线图的绘制
- Android使用自定义View继承SurfaceView实现动态折线图的绘制
- DataSet高性能数据传输方式的实现
- java实现高性能的数据同步
- java实现高性能的数据同步
- 高性能定时器时间轮的实现
- java实现高性能的数据同步
- 高性能的内核 Socket 实现 Fastsocket
- 一个高性能无锁哈希表的实现
- 基于 CoreText 实现的高性能 UITableView
- 基于 CoreText 实现的高性能 UITableView
- 高性能的内核 Socket 实现 Fastsocket
- 数据库高可用、性能扩展的实现
- Java 日期的各种操作
- JSON入门(1)
- Qt QStirng 的使用
- 【C#】 多态
- Pentaho技术白皮书中文版(一)----用 Eclipse 构建和调试 Pentaho
- SurfaceView 实现高性能的绘制
- CGI,WCGI
- 1.STM32F4系列通用定时器TIM2~5总结
- 数据库设计三大范式
- 关于github的那点事
- android之左右滑动切换activity
- 数据结构实验之查找五:平方之哈希表
- Theano Multi Layer Perceptron 多层感知机
- NSString为什么用copy