SurfaceView 基础用法
2016-01-14 00:28
393 查看
概要
SurfaceView类粗略认识
SurfaceView实战
总结
在看具体代码
这里先明确SurfaceView就是一个View的概念,读代码跟认识人都差不多,我们在和陌生人接触时如果发现双方都认识同一个人,立马会拉近彼此的距离。这里我们也算是找到了一个亲戚,看到SurfaceView是个View,是不是心里对代码亲近了许多。
在看其类层的注释,由于注释很长就不贴出来了,直接说下我从注释里获取的信息。
1. SurfaceView的作用:提供一个可嵌入View的Surface,通过它能控制surface的格式、大小等。类比TextView和text的关系,可以把Surface当成一个text。
2.SurfaceView的特点:Surface是Z方向排列,它在window的下面。SurfaceView会在window上挖个洞将Surface暴露出来。在SurfaceView可见的时候就会创建出Surface,子线程可以将Surface渲染到屏幕上。SurfaceView所属的window在哪个线程运行,SurfaceView以及SurfaceHolder.Callback所属的方法就会在该线程调用。
3. SurfaceView怎么用:通过SurfaceHolder来管理Surface,实现SurfaceHolder.Callback.surfaceCreated和SurfaceHolder.Callback.surfaceDestroyed方法可以跟踪Surface被创建和销毁的事件。
暂时没有在Ubuntu环境下找到好的制作gif图片的工具,静态图将就看,运行效果脑补下。我们要实现的效果是,通过SurfaceView将美女加载显示出来,如果不满意,点击button切换下一位美女。
先放布局文件:
Layout 很简单,主要看CustomSurfaceView,它继承自SurfaceView也是本篇的主角。代码不多,直接给出。
这里有个小坑,自定义View的构造方法,如果直接写
就会报出下面的错误:
最后放出演示用的Activity.
自此就能满心欢喜的看美女咯,但是如果拿错手机的姿势,那么会遇到下面的错误log:
这是因为横竖屏切换canvas对象被重建了,lockCanvas和unlockCanvasAndPost必须被同一个实例对象所调用。打开CustomSurfaceView里面的这段注释可以避免该错误。
另外加载图片的大小有可能会不同,这样当第二次切换的图片比第一张小时,会导致前一次的绘制图像还会露出来,为了让CustomSurfaceView的大小动态的匹配加载的图片。可以修改Activity中的showPicture如下
这里就运用了View中的方法,SurfaceView既然是一个View,那么就可以通过setLayoutParams设置其layout参数。
该篇作为Open GL学习的前瞻篇,后续对这块有了新的认知还会更新。
SurfaceView类粗略认识
SurfaceView实战
总结
概要
刚接触Android开发的同学都被灌输过一个理念,一般情况下View的更新必须要在主线程操作。这也符合我们平时的操作习惯,先有输入事件,然后view响应了输入事件再去更新。但总会遇到需要在子线程更新View的情景,比如玩《节奏大师》这种手游时,View一直在不断的更新,并且更新的频率很高,这种情况如果再放到主线程去处理View的更新事件,就不合适时宜了。Android 为解决该类应用场景,给View家族增加了一个异类—SurfaceView,它能在子线程中去刷新View。本篇博文介绍其基础用法。SurfaceView类粗略认识
分析一个陌生类之前,最好避免开篇就进入其类部,追查各个方法,而是要从宏观的角度查看其继承关系,实现接口,类的注释等,这样避免陷入细节的泥潭走不出来。回到主题,先从api文档看SurfaceView的继承结构,具体如下图:在看具体代码
public class SurfaceView extends View {
这里先明确SurfaceView就是一个View的概念,读代码跟认识人都差不多,我们在和陌生人接触时如果发现双方都认识同一个人,立马会拉近彼此的距离。这里我们也算是找到了一个亲戚,看到SurfaceView是个View,是不是心里对代码亲近了许多。
在看其类层的注释,由于注释很长就不贴出来了,直接说下我从注释里获取的信息。
1. SurfaceView的作用:提供一个可嵌入View的Surface,通过它能控制surface的格式、大小等。类比TextView和text的关系,可以把Surface当成一个text。
2.SurfaceView的特点:Surface是Z方向排列,它在window的下面。SurfaceView会在window上挖个洞将Surface暴露出来。在SurfaceView可见的时候就会创建出Surface,子线程可以将Surface渲染到屏幕上。SurfaceView所属的window在哪个线程运行,SurfaceView以及SurfaceHolder.Callback所属的方法就会在该线程调用。
3. SurfaceView怎么用:通过SurfaceHolder来管理Surface,实现SurfaceHolder.Callback.surfaceCreated和SurfaceHolder.Callback.surfaceDestroyed方法可以跟踪Surface被创建和销毁的事件。
SurfaceView实战
上面扯了点闲蛋,下面进入实战。用一个实例来演示SurfaceView 基础用法。先放一张效果图,美女引狼,知道大家好这口,不解释。暂时没有在Ubuntu环境下找到好的制作gif图片的工具,静态图将就看,运行效果脑补下。我们要实现的效果是,通过SurfaceView将美女加载显示出来,如果不满意,点击button切换下一位美女。
先放布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/switch_img" android:layout_width="wrap_content" android:layout_height="50dp" android:layout_gravity="center" android:paddingBottom="5dp" android:text="点击切换图片" /> <com.azhengye.demosurfaceview.CustomSurfaceView android:id="@+id/surfaceview" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
Layout 很简单,主要看CustomSurfaceView,它继承自SurfaceView也是本篇的主角。代码不多,直接给出。
package com.azhengye.demosurfaceview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder holder = null; private RenderThread renderThread = null;; private boolean isRunning = false; private Bitmap mBitmap = null; public CustomSurfaceView(Context context,AttributeSet att) { super(context,att); holder = getHolder(); holder.addCallback(this); renderThread = new RenderThread(); } public void setBitmap(Bitmap bitmap){ this.mBitmap = bitmap; } @Override public void surfaceCreated(SurfaceHolder holder) { isRunning = true; renderThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { isRunning = false; } class RenderThread extends Thread { @Override public void run() { while (isRunning) { drawPicture(); } super.run(); } private void drawPicture() { Canvas canvas = holder.lockCanvas(); try { drawCanvas(canvas); } catch (Exception e) { e.printStackTrace(); } finally { //if (canvas != null) {//坑啊 holder.unlockCanvasAndPost(canvas); //} } } private void drawCanvas(Canvas canvas) { canvas.drawBitmap(mBitmap, 0f, 0f, null); } } }
这里有个小坑,自定义View的构造方法,如果直接写
public CustomSurfaceView(Context context)
就会报出下面的错误:
E/AndroidRuntime( 7294): Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
最后放出演示用的Activity.
package com.azhengye.demosurfaceview; import com.azhengye.demosurfaceview.R; import android.app.Activity; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.LinearLayout; public class MainActivity extends Activity implements OnClickListener { private int sampleIdx = -1; private int samples[]; private CustomSurfaceView mCustomSurfaceView = null; private static final int NUMSAPLES = 9; private Bitmap mBitmap = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.switch_img).setOnClickListener(this); mCustomSurfaceView = (CustomSurfaceView) findViewById(R.id.surfaceview); initData(); initView(); } private void initData() { samples = new int[NUMSAPLES]; samples[0] = R.drawable.p1; samples[1] = R.drawable.p2; samples[2] = R.drawable.p3; samples[3] = R.drawable.p4; samples[4] = R.drawable.p5; samples[5] = R.drawable.p6; samples[6] = R.drawable.p7; samples[7] = R.drawable.p8; samples[8] = R.drawable.p9; } private void initView() { mBitmap = BitmapFactory.decodeResource(getResources(), samples[0]); mCustomSurfaceView.setBitmap(mBitmap); } @Override public void onClick(View v) { showPicture(); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { mBitmap = (Bitmap) savedInstanceState.get("bitmap"); super.onRestoreInstanceState(savedInstanceState); } private void showPicture() { sampleIdx = (sampleIdx + 1) % samples.length; int imageSample = samples[sampleIdx]; mBitmap = BitmapFactory.decodeResource(getResources(), imageSample); mCustomSurfaceView.setBitmap(mBitmap); } }
自此就能满心欢喜的看美女咯,但是如果拿错手机的姿势,那么会遇到下面的错误log:
E/AndroidRuntime( 6950): Process: com.azhengye.demosurfaceview, PID: 6950 E/AndroidRuntime( 6950): java.lang.IllegalArgumentException: canvas object must be the same instance that was previously returned by lockCanvas E/AndroidRuntime( 6950): at android.view.Surface.unlockCanvasAndPost(Surface.java:279) E/AndroidRuntime( 6950): at android.view.SurfaceView$4.unlockCanvasAndPost(SurfaceView.java:860) E/AndroidRuntime( 6950): at com.azhengye.demosurfaceview.CustomSurfaceView$RenderThread.drawPicture(CustomSurfaceView.java:58) E/AndroidRuntime( 6950): at com.azhengye.demosurfaceview.CustomSurfaceView$RenderThread.run(CustomSurfaceView.java:46)
这是因为横竖屏切换canvas对象被重建了,lockCanvas和unlockCanvasAndPost必须被同一个实例对象所调用。打开CustomSurfaceView里面的这段注释可以避免该错误。
//if (canvas != null) {//坑啊 holder.unlockCanvasAndPost(canvas); //}
另外加载图片的大小有可能会不同,这样当第二次切换的图片比第一张小时,会导致前一次的绘制图像还会露出来,为了让CustomSurfaceView的大小动态的匹配加载的图片。可以修改Activity中的showPicture如下
private void showPicture() { sampleIdx = (sampleIdx + 1) % samples.length; int imageSample = samples[sampleIdx]; mBitmap = BitmapFactory.decodeResource(getResources(), imageSample); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( mBitmap.getWidth(), mBitmap.getHeight()); mCustomSurfaceView.setLayoutParams(layoutParams); mCustomSurfaceView.setBitmap(mBitmap); }
这里就运用了View中的方法,SurfaceView既然是一个View,那么就可以通过setLayoutParams设置其layout参数。
总结
本文总结了对SurfaceView的粗浅运用方法,通过它可以看到子线程也可去渲染一个View对象,同时需要注意线程是在surfaceCreated方法中启动的,SurfaceView可见的时候就会创建出Surface,同时系统通过调用surfaceCreated告知Surface被创建了,然后子线程通过SurfaceHolder拿到canvas填充Surface。该篇作为Open GL学习的前瞻篇,后续对这块有了新的认知还会更新。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories