- 浏览: 139480 次
- 性别:
文章分类
最新评论
当SurfaceHolder对象的类型设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS时就只能拍照不能绘制了。
为了既能通过SurfaceView拍照又能在上面绘制图形,可以通过双SurfaceView层叠的变通方式如下:
用于绘制的SurfaceView,使其透明并位于顶部:
package com.test; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Paint.Style; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; public class SVDraw extends SurfaceView implements SurfaceHolder.Callback { private Bitmap bmp; private String imgPath = ""; protected SurfaceHolder sh; // 专门用于控制surfaceView的 private int width; private int height; // XML文件解析需要调用View的构造函数View(Context , AttributeSet) // 因此自定义SurfaceView中也需要该构造函数 public SVDraw(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub sh = getHolder(); sh.addCallback(this); sh.setFormat(PixelFormat.TRANSPARENT); // 设置为透明 setZOrderOnTop(true);// 设置为顶端 } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int w, int h) { // TODO Auto-generated method stub width = w; height = h; } @Override public void surfaceCreated(SurfaceHolder arg0) { // TODO Auto-generated method stub } @Override public void surfaceDestroyed(SurfaceHolder arg0) { // TODO Auto-generated method stub } void clearDraw() { Canvas canvas = sh.lockCanvas(); canvas.drawColor(Color.BLUE);// 清除画布 sh.unlockCanvasAndPost(canvas); } /** * 绘制 */ public void doDraw() { if (bmp != null) { Canvas canvas = sh.lockCanvas(); canvas.drawColor(Color.TRANSPARENT);// 这里是绘制背景 Paint p = new Paint(); // 笔触 p.setAntiAlias(true); // 反锯齿 p.setColor(Color.RED); p.setStyle(Style.STROKE); canvas.drawBitmap(bmp, 0, 0, p); canvas.drawLine(width / 2 - 100, 0, width / 2 - 100, height, p); canvas.drawLine(width / 2 + 100, 0, width / 2 + 100, height, p); // ------------------------ 画边框--------------------- Rect rec = canvas.getClipBounds(); rec.bottom--; rec.right--; p.setColor(Color.GRAY); // 颜色 p.setStrokeWidth(5); canvas.drawRect(rec, p); // 提交绘制 sh.unlockCanvasAndPost(canvas); } } public void drawLine() { Canvas canvas = sh.lockCanvas(); canvas.drawColor(Color.TRANSPARENT);// 这里是绘制背景 Paint p = new Paint(); // 笔触 p.setAntiAlias(true); // 反锯齿 p.setColor(Color.RED); p.setStyle(Style.STROKE); canvas.drawLine(width / 2 - 100, 0, width / 2 - 100, height, p); canvas.drawLine(width / 2 + 100, 0, width / 2 + 100, height, p); // 提交绘制 sh.unlockCanvasAndPost(canvas); } public String getImgPath() { return imgPath; } public void setImgPath(String imgPath) { this.imgPath = imgPath; // 根据路径载入目标图像 bmp = BitmapFactory.decodeFile(imgPath); } }
用于在SurfaceView(使其位于绘制SurfaceView底部)上拍照及预览的Activity:
package com.test; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.List; import android.app.Activity; import android.content.ContentValues; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.ShutterCallback; import android.hardware.Camera.Size; import android.media.AudioManager; import android.media.ToneGenerator; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.provider.SyncStateContract.Constants; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; public class SurfaceViewDraw extends Activity implements SurfaceHolder.Callback, Camera.PictureCallback { /** Called when the activity is first created. */ private SVDraw svDraw = null; private SurfaceView svCamera = null; protected SurfaceHolder mSurfaceHolder; private Button btnClear; private Button btnOpen; private Button btnClose; private Button btnTakePic; private Button btnDraw; private Camera mCamera; // 这个是hardware的Camera对象 private boolean isOpen = false;// 相机是否打开 private ToneGenerator tone; private String imgPath; private int width; private int height; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); hideStatusBar(); setContentView(R.layout.main); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 强制为横屏 svDraw = (com.test.SVDraw) findViewById(R.id.svDraw); svCamera = (SurfaceView) findViewById(R.id.svCamera); btnClear = (Button) findViewById(R.id.btnClear); btnOpen = (Button) findViewById(R.id.btnOpen); btnClose = (Button) findViewById(R.id.btnClose); btnTakePic = (Button) findViewById(R.id.btnTakePic); btnDraw = (Button) findViewById(R.id.btnDraw); btnClear.setOnClickListener(new ClickEvent()); btnOpen.setOnClickListener(new ClickEvent()); btnClose.setOnClickListener(new ClickEvent()); btnTakePic.setOnClickListener(new ClickEvent()); btnDraw.setOnClickListener(new ClickEvent()); mSurfaceHolder = svCamera.getHolder(); mSurfaceHolder.addCallback(this); // 当设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS后就不能绘图了 mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } class ClickEvent implements View.OnClickListener { @Override public void onClick(View v) { // TODO Auto-generated method stub if (v == btnClear) { stopPreview(); // 停止预览后清屏速度会快一点 svDraw.setVisibility(View.INVISIBLE); startPreview();// 清屏后启动预览 } else if (v == btnOpen) { initCamera(); } else if (v == btnClose) { closeCamera(); } else if (v == btnTakePic) { if (isOpen) { startPreview();// 防止异常 mCamera.takePicture(mShutterCallback, null, null, mjpegCallback); svDraw.setVisibility(View.VISIBLE); svDraw.drawLine();// 拍照后绘制测线 } } else if (v == btnDraw) { svDraw.setVisibility(View.VISIBLE); svDraw.doDraw(); } } } ShutterCallback mShutterCallback = new ShutterCallback() { @Override public void onShutter() { // TODO Auto-generated method stub if (tone == null) // 发出提示用户的声音 tone = new ToneGenerator(AudioManager.STREAM_MUSIC, ToneGenerator.MAX_VOLUME); tone.startTone(ToneGenerator.TONE_PROP_BEEP); } }; /** * Jpeg格式压缩 */ PictureCallback mjpegCallback = new PictureCallback() { @Override // 取得拍照图片 public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub // 拍照前关闭预览 mCamera.stopPreview(); // 取得图像路径 imgPath = saveFile2(data); svDraw.setImgPath(imgPath); } }; /** * draw information on the picture * * @param imgPath */ public void drawInfo(String imgPath) { Bitmap bmp = BitmapFactory.decodeFile(imgPath); if (bmp != null) { Bitmap drawBmp = Bitmap.createBitmap(640, 480, Config.ARGB_8888); Canvas c = new Canvas(drawBmp); Paint p = new Paint(); c.drawBitmap(bmp, 0, 0, p); String familyName = "Arial"; Typeface font = Typeface.create(familyName, Typeface.NORMAL); p.setColor(Color.RED); p.setTypeface(font); p.setTextSize(20); p.setStyle(Paint.Style.STROKE); SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss"); String strDate = dateFormat.format(new Date()); c.drawText(strDate, 10, 30, p); try { saveBmp(drawBmp, imgPath); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * save bmp as jpg by path * * @param bmpPath * @param bmp * @throws IOException */ public void saveBmp(Bitmap bmp, String fileName) throws IOException { File f = new File(fileName); f.createNewFile(); FileOutputStream fOut = null; try { fOut = new FileOutputStream(f); } catch (FileNotFoundException e) { e.printStackTrace(); } bmp.compress(Bitmap.CompressFormat.JPEG, 100, fOut); try { fOut.flush(); } catch (IOException e) { e.printStackTrace(); } try { fOut.close(); } catch (IOException e) { e.printStackTrace(); } } /** * return imgFilePath * * @param data * @return */ private String saveFile2(byte[] data) { File imgFileDir = getDir(); if (!imgFileDir.exists() && !imgFileDir.mkdirs()) { Log.v("directory", "Can't create directory to save image."); return null; } // 图像名称 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); String strDate = dateFormat.format(new Date()); String imgFileName = "img_" + strDate + ".jpg"; // 图像路径 String imgFilePath = imgFileDir.getPath() + File.separator + imgFileName; File imgFile = new File(imgFilePath); try { FileOutputStream fos = new FileOutputStream(imgFile); fos.write(data); fos.close(); Log.v("directory", "New Image saved:" + imgFile); } catch (Exception error) { Log.d(Constants.ACCOUNT_NAME, imgFileName + " not saved: " + error.getMessage()); } //绘制拍照日期等 drawInfo(imgFilePath); return imgFilePath; } /** * * @return */ private File getDir() { File sdDir = Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); // 创建图像需要保存的文件夹 return new File(sdDir, "Photo"); } @Override public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub // data是一个原始的JPEG图像数据, // 在这里我们可以存储图片,很显然可以采用MediaStore // 注意保存图片后,再次调用stopPreview()停止预览,等待测量 Uri imageUri = this.getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues()); try { OutputStream os = this.getContentResolver().openOutputStream( imageUri); os.write(data); os.flush(); os.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } // 拍照后停止预览 mCamera.stopPreview(); } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // TODO Auto-generated method stub width = w; height = h; } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub } /** * 关闭相机 */ public void closeCamera() { if (isOpen) { mCamera.stopPreview(); mCamera.release(); mCamera = null; isOpen = false; } } /** * 停止拍照预览 */ public void stopPreview() { if (isOpen) { mCamera.stopPreview(); } } /** * 启动拍照预览 */ public void startPreview() { if (isOpen) { mCamera.startPreview(); } } /** * 初始化相机 */ public void initCamera() { if (!isOpen) { mCamera = Camera.open(); } if (mCamera != null && !isOpen) { try { Camera.Parameters mParameters = mCamera.getParameters(); mParameters.setPictureFormat(PixelFormat.JPEG); // 设置照片格式 List<Size> sizes = mParameters.getSupportedPreviewSizes(); Size optimalSize = getOptimalPreviewSize(sizes, width, height); mParameters.setPreviewSize(optimalSize.width, optimalSize.height); // 大小 mParameters.setPictureSize(optimalSize.width, optimalSize.height); mParameters.set("jpeg-quality", 100);// 照片质量 // 首先获取系统设备支持的所有颜色特效,有复合我们的,则设置;否则不设置 List<String> colorEffects = mParameters .getSupportedColorEffects(); Iterator<String> colorItor = colorEffects.iterator(); while (colorItor.hasNext()) { String currColor = colorItor.next(); if (currColor.equals(Camera.Parameters.EFFECT_SOLARIZE)) { mParameters .setColorEffect(Camera.Parameters.EFFECT_SOLARIZE); break; } } mCamera.setParameters(mParameters); mCamera.setPreviewDisplay(mSurfaceHolder); mCamera.startPreview(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } isOpen = true; } } private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.05; double targetRatio = (double) w / h; if (sizes == null) return null; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size for (Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } // Cannot find the one match the aspect ratio, ignore the requirement if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); if (isOpen) { closeCamera(); } } // 在 Activity.setCurrentView()之前调用 public void hideStatusBar() { // 隐藏标题 requestWindowFeature(Window.FEATURE_NO_TITLE); // 定义全屏参数 int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; // 获得窗口对象 Window curWindow = this.getWindow(); // 设置Flag标示 curWindow.setFlags(flag, flag); } }
主界面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="horizontal" > <!-- 对于自定义控件要指明的控件的包名与空间名,系统自带的控件不需要指定包名 --> <FrameLayout android:layout_width="640dip" android:layout_height="480dip" android:orientation="vertical" > <SurfaceView android:id="@+id/svCamera" android:layout_width="fill_parent" android:layout_height="fill_parent"/> <com.test.SVDraw android:id="@+id/svDraw" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </FrameLayout> <LinearLayout android:id="@+id/LinearLayout01" android:layout_width="158dip" android:layout_height="fill_parent" android:layout_marginLeft="1dip" android:layout_marginRight="1dip" android:orientation="vertical" android:background="@drawable/main_right_bg"> <Button android:id="@+id/btnOpen" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10dip" android:text="打开相机"/> <Button android:id="@+id/btnClose" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="关闭相机"/> <Button android:id="@+id/btnTakePic" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="拍照" /> <Button android:id="@+id/btnClear" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="预览"/> <Button android:id="@+id/btnDraw" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="绘制"/> </LinearLayout> </LinearLayout>
在res下新建文件夹drawable,并在其下面新建面板背景main_right_bg.xml:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:startColor = "#666666" android:centerColor="#000FFF" android:endColor = "#666666" android:angle = "270"/> <corners android:radius="4dip"/> </shape>
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.test" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <!-- 照相机权限 --> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" /> <!-- 在SDCard中创建与删除文件权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 往SDCard写入数据权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".SurfaceViewDraw" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
开发环境:XP3+Eclipse+Android2.2+JDK6.0
测试环境:Android2.2,5寸屏,分辨率640X480
源代码:http://download.csdn.net/detail/xinzheng_wang/4409755
发表评论
-
Android onTouchEvent, onClick及onLongClick的调用机制
2012-03-08 15:44 902针对屏幕上的一个View控件,Android如何区分应当触发o ... -
android 常用颜色表
2012-03-09 16:01 932<?xml version="1.0" ... -
android ndk 环境搭建及基本编程思路
2012-03-28 16:09 891本文主要是介绍一下android ndk环境搭建及基本编程思路 ... -
windows下eclipse android-ndkr7b环境配置
2012-03-31 19:49 936注意:android-ndkr7b版本中已经集成了cyg ... -
配置 eclipse ndk 环境遇到的问题
2012-04-01 08:52 1078困惑了两天的环境配置问题,按照网上提供的方法去做总是错误 ... -
Android 配置 OpenCV2.3.1
2012-04-01 17:56 2065OpenCV2.3.1-android中大部分重要的AP ... -
Android中dip与px之间单位转换
2012-04-11 16:10 734/** * 根据手机的分辨率从dip 的单位转成为px(像素 ... -
Android 自定义像素AVD模拟器无键盘
2012-04-18 14:26 3405在Android自定义像素(如:800X480)时,AV ... -
在Android虚拟机AVD中安装APK
2012-04-18 15:13 2736为了方便测试,有时候会需要在自定义的Android虚拟机A ... -
Android SurfaceView onDraw()绘图问题
2012-06-29 11:32 10085在继承SurfaceView的类中即使重写了onDraw ... -
Android 自定义渐变背景
2012-07-04 08:36 1385在Eclipse的Android工程的res下建立dra ... -
Android 隐藏系统状态栏和标题栏
2012-07-04 09:06 1276Android中若想 隐藏系统状态栏和标题栏(全屏显示)的 ... -
Android数据的四种存储方式之SharedPreferences、SQLite、ContentProvider和File
2012-07-06 15:32 2865Android系统一共提供了四种数据存储方式,分别 ... -
Android SQLite存取图像
2012-07-06 18:36 953Android SQLite存取图像的简单方法如下: ... -
Android 继承SQLiteOpenHelper自定义DBHelper存取数据与图像
2012-07-10 15:07 1429Android 继承SQLiteOpenHelper自定 ... -
Android使用SQLiteDatabase直接存取数据与图像
2012-07-10 15:15 1111Android使用SQLiteDatabase直接存取数 ... -
Android 系统菜单与自定义菜单
2012-07-25 16:28 2319Android 系统菜单与自 ...
相关推荐
Android双SurfaceView底部拍照,顶部绘图,当SurfaceHolder对象的类型设置为SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS时就只能拍照不能绘制了。为了既能通过SurfaceView拍照又能在上面绘制图形,可以通过双...
在Android开发中,SurfaceView是一种特殊的视图,它允许开发者创建高性能的图形界面,尤其适合处理视频播放或游戏等需要连续刷新的场景。本话题主要探讨如何利用SurfaceView实现图片的缩放和滑动功能。 首先,理解...
总之,通过SurfaceView实现实时显示摄像头视频是Android开发中的一个基础任务,涉及到对Android系统相机API的理解和使用,以及对SurfaceView生命周期的掌握。随着技术的发展,开发者可以选择更高级的API,如Camera2...
本文将详细介绍如何在Android中实现`SurfaceView`的截屏功能。 首先,了解`SurfaceView`的基本原理。`SurfaceView`创建了一个独立的窗口,并在自己的Surface上绘制内容。这使得它可以在后台线程进行高效的绘制,但...
`SurfaceView`是Android提供的一种用于高效显示多媒体数据的视图组件,尤其适合处理像视频流这样的实时数据。本篇文章将深入探讨如何利用`SurfaceView`实现实时显示摄像头视频。 首先,我们需要了解`SurfaceView`的...
Android开发摄像头SurfaceView预览 背景画图(矩形和圆形) 实现(双surfaceview,顶层画矩形框,底层预览视频); UI:三个按钮 预览 摄像 图片保存,预览界面 可以显示(矩形和圆形等图画)
本示例“Android使用SurfaceView实现墨迹天气的风车效果”是一个很好的学习资源,适用于毕业设计或论文研究。这个项目主要展示了如何利用Android的SurfaceView组件来创建类似墨迹天气应用中的风车动画效果。下面将...
在这个“android SurfaceView实现人物动画”的示例程序中,我们将深入探讨如何利用SurfaceView来创建流畅的人物动画。 1. **SurfaceView基本概念** SurfaceView是一个可以独立于应用程序主线程更新的视图,它有...
"Android使用SurfaceView实现墨迹天气的风车效果"是一个典型的案例,它展示了如何利用Android的SurfaceView组件来创建动态、流畅的动画效果,以模拟类似墨迹天气App中的风车转动。SurfaceView是Android系统提供的一...
android使用双缓冲辨析及surfaceview使用例子
总结起来,实现Android圆形相机预览窗口需要对SurfaceView有深入的理解,包括如何自定义视图、绘制路径以及与相机的交互。通过这个过程,开发者不仅可以创建独特的用户界面,还能提升应用程序的创新性和用户体验。在...
这个项目"android surfaceview自定义拍照 绘制头像轮廓"是基于网上现有的示例代码进行了修改,用于实现一个自定义的拍照功能,并且在拍摄的照片上能够绘制出人像的轮廓。下面我们将深入探讨`SurfaceView`以及如何...
在Android应用开发中,SurfaceView是一个非常重要的组件,它提供了在主线程之外进行绘图的能力,使得复杂的动画和游戏能够流畅运行。本压缩包“Android应用源码之Android使用SurfaceView实现墨迹天气的风车效果.zip...
在Android开发中,SurfaceView是一种特殊的视图,它允许开发者在单独的线程中进行高性能的图形绘制,常用于游戏或者视频播放等需要连续更新显示内容的场景。本篇文章将详细探讨如何利用SurfaceView实现动画效果,...
在Android开发中,SurfaceView是一个非常重要的视图组件,它为开发者提供了在应用程序中实现高性能图形渲染的能力。SurfaceView的设计初衷是为了处理那些需要频繁更新且对性能要求较高的场景,如视频播放、游戏画面...
本示例中的“Android自定义SurfaceView——实现画板功能”旨在教你如何利用`SurfaceView`创建一个可以画画的应用。`SurfaceView`是Android系统提供的一种用于高效显示动态图像的视图组件,它拥有自己的渲染线程,...
在安卓应用开发中,SurfaceView 是一个非常重要的组件,它为开发者提供了在应用程序中实现高性能图形渲染的能力。这个源码示例是关于如何利用SurfaceView来实现类似墨迹天气应用中的风车动态效果。让我们深入探讨...
在Android开发中,`SurfaceView`是一个特殊类型的`View`,它允许开发者在应用程序中创建一个独立于应用程序主线程的渲染表面。`SurfaceView`通常用于处理高性能的图形或者视频播放,因为它可以在单独的线程中进行...
Android 双重 SurfaceView 实现弹幕效果 Android 双重 SurfaceView 实现弹幕效果是指在 Android 平台上使用双重 SurfaceView 来实现弹幕效果的技术。弹幕是一种常见的视频播放效果,能让用户在视频播放时发送弹幕...
在Android开发中,SurfaceView是一种特殊的视图,它允许开发者在应用程序中创建高性能的图形界面,特别是用于实现动画效果,如游戏或者视频播放等。SurfaceView拥有自己的绘图表面,与主线程分离,因此在处理复杂的...