Android提高之SurfaceView与多线程的混搭实例

前文简单介绍了Android中SurfaceView的基本使用,本文就来介绍一下SurfaceView与多线程的混搭。SurfaceView与多线程混搭,是为了防止动画闪烁而实现的一种多线程应用。android的多线程用法与JAVA的多线程用法完全一样,本文不做多线程方面的介绍了。直接讲解SurfaceView与多线程的混合使用,即开一条线程专门读取图片,另外一条线程专门绘图。

本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:

对比一下可以看出,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都“边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高动画播放的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。

main.xml的源码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent" android:layout_height="fill_parent"
 android:orientation="vertical">

 <LinearLayout android:id="@+id/LinearLayout01"
 android:layout_width="wrap_content" android:layout_height="wrap_content">
 <Button android:id="@+id/Button01" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:text="单个独立线程"></Button>
 <Button android:id="@+id/Button02" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:text="两个独立线程"></Button>
 </LinearLayout>
 <SurfaceView android:id="@+id/SurfaceView01"
 android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView>
</LinearLayout>

Java程序的源码如下:

package com.testSurfaceView;
import java.lang.reflect.Field;
import java.util.ArrayList;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

public class testSurfaceView extends Activity {
 /** Called when the activity is first created. */
 Button btnSingleThread, btnDoubleThread;
 SurfaceView sfv;
 SurfaceHolder sfh;
 ArrayList<Integer> imgList = new ArrayList<Integer>();
 int imgWidth, imgHeight;
 Bitmap bitmap;//独立线程读取,独立线程绘图

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);

 btnSingleThread = (Button) this.findViewById(R.id.Button01);
 btnDoubleThread = (Button) this.findViewById(R.id.Button02);
 btnSingleThread.setOnClickListener(new ClickEvent());
 btnDoubleThread.setOnClickListener(new ClickEvent());
 sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);
 sfh = sfv.getHolder();
 sfh.addCallback(new MyCallBack());// 自动运行surfaceCreated以及surfaceChanged
 }

 class ClickEvent implements View.OnClickListener {

 @Override
 public void onClick(View v) {

  if (v == btnSingleThread) {
  new Load_DrawImage(0, 0).start();//开一条线程读取并绘图
  } else if (v == btnDoubleThread) {
  new LoadImage().start();//开一条线程读取
  new DrawImage(imgWidth + 10, 0).start();//开一条线程绘图
  }
 }
 }
 class MyCallBack implements SurfaceHolder.Callback {
 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width,
  int height) {
  Log.i("Surface:", "Change");
 }
 @Override
 public void surfaceCreated(SurfaceHolder holder) {
  Log.i("Surface:", "Create");
  // 用反射机制来获取资源中的图片ID和尺寸
  Field[] fields = R.drawable.class.getDeclaredFields();
  for (Field field : fields) {
  if (!"icon".equals(field.getName()))// 除了icon之外的图片
  {
   int index = 0;
   try {
   index = field.getInt(R.drawable.class);
   } catch (IllegalArgumentException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   }
   // 保存图片ID
   imgList.add(index);
  }
  }
  // 取得图像大小
  Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
   imgList.get(0));
  imgWidth = bmImg.getWidth();
  imgHeight = bmImg.getHeight();
 }
 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
  Log.i("Surface:", "Destroy");
 }
 }
 /*
 * 读取并显示图片的线程
 */
 class Load_DrawImage extends Thread {
 int x, y;
 int imgIndex = 0;

 public Load_DrawImage(int x, int y) {
  this.x = x;
  this.y = y;
 }
 public void run() {
  while (true) {
  Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
   + imgWidth, this.y + imgHeight));
  Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
   imgList.get(imgIndex));
  c.drawBitmap(bmImg, this.x, this.y, new Paint());
  imgIndex++;
  if (imgIndex == imgList.size())
   imgIndex = 0;
  sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
  }
 }
 };
 /*
 * 只负责绘图的线程
 */
 class DrawImage extends Thread {
 int x, y;
 public DrawImage(int x, int y) {
  this.x = x;
  this.y = y;
 }
 public void run() {
  while (true) {
  if (bitmap != null) {//如果图像有效
   Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
    + imgWidth, this.y + imgHeight));

   c.drawBitmap(bitmap, this.x, this.y, new Paint());

   sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
  }
  }
 }
 };
 /*
 * 只负责读取图片的线程
 */
 class LoadImage extends Thread {
 int imgIndex = 0;
 public void run() {
  while (true) {
  bitmap = BitmapFactory.decodeResource(getResources(),
   imgList.get(imgIndex));
  imgIndex++;
  if (imgIndex == imgList.size())//如果到尽头则重新读取
   imgIndex = 0;
  }
 }
 };
}

希望本文所述示例能对大家进行Android的SurfaceView与多线程的混搭编程有所帮助。

时间: 2014-08-07

Android 自定义SurfaceView详解

本文简单讨论以后Android游戏引擎模板的架构问题.在Android游戏开发教程之二:View类与SurfaceView类中我们已经谈到,SurfaceView类是有很多优势的,所以在Android游戏开发中还是选择SurfaceView. 这里我们直接继承SurfaceView,实现SurfaceHolder.Callback接口,处理surfaceCreated.surfaceChanged以及surfaceDestroyed方法,这里我们并没有把按键控制传入,最终游戏的控制方面仍然由Vi

Android Fragment中使用SurfaceView切换时闪一下黑屏的解决办法

重构了下之前自己的一个新闻客户端,全部使用了Fragment来进行页面切换,只有一个入口Activity作为程序的启动Activity,其中有一个界面需要调用摄像头识别二维码,于是就会用到SurfaceView进行预览,那么问题来了,当切换到对应的Fragment时,屏幕会黑一下,黑了1秒左右就显示出正常的界面,而且这种现象只有第一次进入该Fragment才会出现,之后进入都不会出现,解决方法是无意在github上看到了,试了一下,可以行的通,下面贴出解决方法. 方法一.在Activity的on

Android编程之SurfaceView学习示例详解

本文实例讲述了Android编程之SurfaceView学习示例.分享给大家供大家参考,具体如下: SurfaceView是View的子类,使用的方式与任何View所派生的类都是完全相同的,可以像其他View那样应用动画,并把它们放到布局中. SurfaceView封装的Surface支持使用本章前面所描述的所有标准Canvas方法进行绘图,同时也支持完全的OpenGL ES库. 使用OpenGL,你可以再Surface上绘制任何支持的2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法

解决Android SurfaceView绘制触摸轨迹闪烁问题的方法

本文分享了解决SurfaceView触摸轨迹闪烁问题的方法,供大家参考,具体内容如下 第一种解决SurfaceView触摸轨迹闪烁问题的方法: 由于SurfaceView使用双缓存机制,两张画布轮流显示到屏幕上.那么,要存储触摸轨迹并避免两张画布内容不一致造成的闪烁问题,完全可以利用保存绘制过程并不断重新绘制的方法解决闪烁,而且这样还顺带解决了多次试验中偶尔出现的因为moveTo()函数不能读取到参数执行默认设置(参数设为上次的触摸点)而出现的断线连接闪烁问题,详细代码如下: package c

Android中SurfaceView用法简单实例

本文实例讲述了Android中SurfaceView用法.分享给大家供大家参考,具体如下: 这里贴上一个小程序代码,主要运用SurfaceView来实现在屏幕上画一个圆,你可以通过按方向键和触摸屏幕来改变圆的位置 代码: Activity: package com.view; import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowMa

Android中SurfaceView和view画出触摸轨迹

一.引言          想实现一个空白的画板,上面可以画出手滑动的轨迹,就这么一个小需求.一般就来讲就两种实现方式,view或者surfaceview.下面看看两种是如何实现的. 二.实现原理          先简单说一下实现原理:        (1)用一张白色的Bitmap作为画板        (2)用canvas在bitmap上画线        (3)为了画出平滑的曲线,要用canvas的drawPath(Path,Paint)方法.        (4)同时使用贝塞尔曲线来使曲

Android编程之SurfaceView实例详解

本文实例讲述了Android编程之SurfaceView用法.分享给大家供大家参考,具体如下: 关于surfaceView相关知识: View和SurfaceView主要区别: 1. View只能在UI线程中刷新,而SurfaceView可以在子线程中刷新 2. SurfaceView可以控制刷新频率 SurfaceView几个重要的方法: 1. 继承SurfaceView 后调用getHolder()方法可以获取到mSurfaceHolder对象这个对于可以控制SurfaceView的绘制 2

Android提高之SurfaceView的基本用法实例分析

前文介绍了Android中MediaPlayer用法的时候稍微介绍了SurfaceView,SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器,这次我就来较为详细的介绍SurfaceView的用法.网上介绍SurfaceView的用法有很多,写法也层出不同,例如继承SurfaceView类,或者继承SurfaceHolder.Callback类等,这个可以根据功能实际需要自己选择,本文所述方法就直接在普通的用户界面调用SurfaceHolder的

Android中利用SurfaceView制作抽奖转盘的全流程攻略

一.概述 今天给大家带来SurfaceView的一个实战案例,话说自定义View也是各种写,一直没有写过SurfaceView,这个玩意是什么东西?什么时候用比较好呢? 可以看到SurfaceView也是继承了View,但是我们并不需要去实现它的draw方法来绘制自己,为什么呢? 因为它和View有一个很大的区别,View在UI线程去更新自己:而SurfaceView则在一个子线程中去更新自己:这也显示出了它的优势,当制作游戏等需要不断刷新View时,因为是在子线程,避免了对UI线程的阻塞. 知

Android View类与SurfaceView类详解

Android游戏开发中主要的类除了控制类就是显示类,比较重要也很复杂的就是显示和游戏逻辑的处理.在J2ME中可以通过Display和Canvas来实现显示,而Android中处理显示的是View类.下面为大家简单介绍android.view.View和android.view.SurfaceView. SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView,

Android App中使用SurfaceView制作多线程动画的实例讲解

1. SurfaceView的定义 通常情况程序的View和用户响应都是在同一个线程中处理的,这也是为什么处理长时间事件(例如访问网络)需要放到另外的线程中去(防止阻塞当前UI线程的操作和绘制).但是在其他线程中却不能修改UI元素,例如用后台线程更新自定义View(调用View的在自定义View中的onDraw函数)是不允许的. 如果需要在另外的线程绘制界面.需要迅速的更新界面或则渲染UI界面需要较长的时间,这种情况就要使用SurfaceView了.SurfaceView中包含一个Surface

android图像绘制(四)自定义一个SurfaceView控件

自定义控件(类似按钮等)的使用,自定义一个SurfaceView. 如某一块的动态图(自定义相应),或者类似UC浏览器下面的工具栏. 如下图示例:  自定义类代码: 复制代码 代码如下: public class ImageSurfaceView extends SurfaceView implements Callback{ //用于控制SurfaceView private SurfaceHolder sfh; private Handler handler = new Handler();

教你五分钟实现Android超漂亮的刻度轮播控件实例教程

前言 最近一直在做音视频的工作,已经有大半年没有写应用层的东西了,生怕越来越生疏.正好前段时间接了个外包项目,才得以回顾一下.项目中有一个控件挺简洁漂亮的,而且用到的技术也比较基础,比较适合新手学习,所以单独开源出来,希望能对初学者有所帮助. 截图 截屏 一.自定义View的常用方法 相信每个Android程序员都知道,我们每天的开发工作当中都在不停地跟View打交道,Android中的任何一个布局.任何一个控件其实都是直接或间接继承自View的,如TextView.Button.ImageVi

Angular使用ControlValueAccessor创建自定义表单控件

在 Angular 自定义表单控件,有时你想要的输入不是标准的文本输入.选择或复选框.通过实现ControlValueAccessor 接口并将组件注册为 NG_VALUE_ACCESSOR,您可以将自定义表单控件无缝地集成到模板驱动或响应表单中,就像它是本地表单一样! ControlValueAccessor ControlValueAccessor 是一个接口,充当Angular API 和 DOM 元素之间的桥梁 ControlValueAccessor 是一个连接表单模型和视图(DOM元

Android自定义圆环倒计时控件

本文实例为大家分享了Android自定义圆环倒计时控件的具体代码,供大家参考,具体内容如下 先来一张最终效果图: 主要思路: 在画渐变色圆环的时候,设置一个属性动画,根据属性动画的执行时长,来作为倒计时的时长.监听属性动画的进度,来达到 倒计时的目的. 二话不说,直接贴代码.具体实现思路都在注释上. 自定义属性: <declare-styleable name="CountDownProgressBar"> <attr name="countDown_cir

Android自定义播放器控件VideoView

介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actvity里了,写完一看我靠代码好乱,于是就写了个自定义的播放器控件,支持指定大小,可以横竖屏切换,手动左右滑动快进快退.好了,下面开始. 效果图有点卡,我也不知道为啥..... VideoView介绍 这个是我们实现视频播放最主要的控件,详细的介绍大家百度就去看,这里介绍几个常用的方法. 用于播放视频

Android自定义View之自定义评价打分控件RatingBar实现自定义星星大小和间距

在Android开发中,我们经常会用到对商家或者商品的评价,运用星星进行打分.然而在Android系统中自带的打分控件,RatingBar特别不好用,间距和大小无法改变.所以,我就自定义了一个特别好用的打分控件.在项目中可以直接使用,特别简单.下面直接上图: 效果图 实现原理 其实就是自定义View继承LinearLayout ,然后里面动态加了五个ImageView. 实现代码,有详细的注释 在attrs中声明的可以在xml中设置的变量 <declare-styleable name="

Android自定义日历滑动控件

本文实例为大家分享了Android自定义日历滑动控件的使用方法,供大家参考,具体内容如下 最近公司项目需要做这个需求,自己才疏学浅,总算能写出个大概来,遂在这里记录下来. 分析 先来分析一下: 首先,我们的需求是可以左右点击查看跳转到下一个月,中间的日历控件可以水平滚动选择日期,所以我们中间的日历控件用一个RecycleView来做,左右两位的为ImageVeiw. LRCalendarView 总体流程: 编写LRCalendarView的布局R.layout.calendar_view 新建

Android自定义弹窗提醒控件使用详解

Android中原生的Dialog弹窗提醒控件样式单一,有时候并不能满足我们的项目需求,而且一个工程里面有时候会在多处都用到弹窗提醒的功能,代码会出现大量的冗余,工作之余,就自己实现了这么一个弹窗提醒控件.自定义控件继承自我们的Dialog,样式自定义,弹窗中的文字可通过数组参数初始化,Item个数实现了动态添加,和数组长度一致.对话框底端可展示一个Item(如:确定)或两个Item(如:确定   取消),通过参数设置.废话不多说,直接上代码: 1.自定义对话框的背景样式,在res/values

Android开发之自定义星星评分控件RatingBar用法示例

本文实例讲述了Android开发之自定义星星评分控件RatingBar用法.分享给大家供大家参考,具体如下: 星级评分条RatingBar类似于SeekBar.ProgressBar'等等都可以自定义样式 它的主要用途就比如淘宝.景点 满意度等 这里给出两种自定义效果 如图所示 第一种是通过RatingBar获得分数 第二个是通过RatingBar动态调节控件属性(透明度) 由于RatngBar使用简单 自定义样式方法和 https://www.jb51.net/article/158338.h

Android自定义加载控件实现数据加载动画

本文实例为大家分享了Android自定义加载控件,第一次小人跑动的加载效果眼前一亮,相比传统的PrograssBar高大上不止一点,于是走起,自定义了控件LoadingView去实现动态效果,可直接在xml中使用,具体实现如下 package com.*****.*****.widget; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.util.