`
nanjingjiangbiao_T
  • 浏览: 2593587 次
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

Android--相机预览及拍照临时文件/SurfaceView

 
阅读更多

如果做游戏或者视频相关开发的时候,将会用到SurfaceView。对于SurfaceView,首先需要了解一下它自己的位置:

extendsView

java.lang.Object

android.view.View

android.view.SurfaceView

由手册可知:

SurfaceView是View类的继承类,这个View里内嵌了一个专门用于绘制的Surface,这个可以类似的理解成为一个在View里的Canvas。你可以控制这个Surface的格式和尺寸。Surfaceview类则控制这个Surface在屏幕上的正确位置。

《Android高级编程》里这样说:

在一般情况下,应用程序的View都是在相同的GUI线程中绘制的。这个主应用程序线程同时也用来处理所有的用户交互(例如按钮单击或者文本输入)。

对于一个View的onDraw()方法,不能够满足将其移动到后台线程中去。因为从后台线程修改一个GUI元素会被显式地禁止的。

当需要快速地更新View的UI,或者当前渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的操作,或者那些要求快速更新或者高速帧率的地方,例如使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

1. 何时应该使用SurfaceView

SurfaceView使用的方式与任何View所派生的类都是完全相同的。可以像其他View那样应用动画,并把它们放到布局中。

SurfaceView封装的Surface支持所有标准的Canvas方法进行绘图,同时也支持完全的OpenGL ES 库。

使用OpenGL,你可以在Surface上绘制任何支持2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依靠硬件加速(可用的时候)来极大地提高性能。

对于显示动态的3D图像来说,例如,那些使用Google Earth 功能的应用程序,或者那些提供沉浸体验的交互式游戏,Surface特别有用。它还是实时显示摄像头预览的最佳选择。

2. 创建一个新的SurfaceView控件

创建一个新的SurfaceView控件需要创建一个新的扩展了SurfaceView的类,并实现SurfaceHolder.Callback。

SurfaceHolder回调可以在底层的Surface被创建和销毁的时候通知View,并传递给它SurfaceHolder对象的引用,其中包含了当前有效的Surface。

一个典型的SurfaceView 设计模型包括一个由Thread所派生的类,它可以接收对当前的SurfaceHolder的引用,并独立地更新它。

3. 使用SurfaceView创建3D控件

Android完全支持OpenGL ES 3D 渲染框架,其中包含了对设备的硬件加速的支持。SurfaceView控件提供了一个表面,可以在它上面渲染你的OpenGL场景。

那么我们在使用的时候可以这样使用:

被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

可以直接从内存或硬件设备比如相机等取得图像数据,是个非常重要的绘图容器。

它的特性是:可以在主线程之外的线程中向屏幕绘图。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。

如何去使用一个SurfaceView:

首先继承SurfaceView并实现SurfaceHolder.Callback接口。因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始。可以被直接复制到显存从而显示出来,这使得显示速度会非常快,而在Surface 被销毁之前必须结束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。

需要重写的方法

(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}

//在surface的大小发生改变时激发

(2)public void surfaceCreated(SurfaceHolder holder){}

//在创建时激发,一般在这里调用画图的线程。

(3)public void surfaceDestroyed(SurfaceHolder holder) {}

//销毁时激发,一般在这里将画图的线程停止、释放。

整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()获得SurfaceHolder对象 ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。

关于SurfaceHolder:

这里用到了一个类SurfaceHolder,可以把它当成surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。
几个需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。


下面给出一个应用的实例,本实例实现的是相机的预览和拍照功能,其中,为了节省存储空间,当退出应用程序时,拍照文件会自动删除,这里把它作为临时文件保存。

下面是它的实现截图:


由于是在模拟器中运行,所以显示的是无内存卡。

下面给出实现本实例的源码:

1.首先,我们需要做权限的声明

<uses-permission android:name="android.permission.CAMERA"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2.给出布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:background="@drawable/white"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
>
  <TextView
    android:id="@+id/myTextView1"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:textColor="@drawable/blue" 
    android:text="@string/hello"
  />
  <SurfaceView
    android:id="@+id/mSurfaceView1"
    android:visibility="visible" 
    android:layout_width="320px" 
    android:layout_height="240px">
  </SurfaceView> 
  <LinearLayout
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
  >
  <Button
    android:id="@+id/myButton1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/str_button1"/>
  <Button
    android:id="@+id/myButton2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/str_button2"/>
  <Button
    android:id="@+id/myButton3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/str_take_picture"/>
  </LinearLayout>
  <ImageView
    android:id="@+id/myImageView1" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_gravity="center" />
</LinearLayout>


3.主程序代码

package irdc.ex07_15;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import android.app.Activity;
/* 延含学习 */
//import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;

/* 引用Camera类 */
import android.hardware.Camera;

/* 引用PictureCallback为取得拍照后的事件 */
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

/* 使Activity实现SurfaceHolder.Callback */
public class EX07_15 extends Activity implements SurfaceHolder.Callback
{
  /* 建立私有Camera对象 */
  private Camera mCamera01;
  private Button mButton01, mButton02, mButton03;
  
  /* 当成review照下来的相片之用 */
  private ImageView mImageView01;
  private TextView mTextView01;
  private static String TAG = "HIPPO_DEBUG";
  private SurfaceView mSurfaceView01;
  private SurfaceHolder mSurfaceHolder01;
  //private int intScreenX, intScreenY;
  
  /* 默认相机预览模式为false */
  private boolean bIfPreview = false;
  
  /* 将照下来的图片保存在此 */
  private String strCaptureFilePath = "/sdcard/camera_snap.jpg";
  
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    
    /* 使应用程序全屏运行,不使用title bar */
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.main);
    
    /* 判断存储卡是否存在 */
    if(!checkSDCard())
    {
      /* 提醒User未安装存储卡 */
      mMakeTextToast
      (
        getResources().getText(R.string.str_err_nosd).toString(),
        true
      );
    }
    
    /* 取得屏幕解析像素 */
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    //intScreenX = dm.widthPixels;
    //intScreenY = dm.heightPixels;
    //Log.i(TAG, Integer.toString(intScreenX));
    
    /* 延含学习 */
    //import android.content.pm.ActivityInfo;
    //this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    
    mTextView01 = (TextView) findViewById(R.id.myTextView1);
    mImageView01 = (ImageView) findViewById(R.id.myImageView1);
    
    /* 以SurfaceView作为相机Preview之用 */
    mSurfaceView01 = (SurfaceView) findViewById(R.id.mSurfaceView1);
    
    /* 绑定SurfaceView,取得SurfaceHolder对象 */
    mSurfaceHolder01 = mSurfaceView01.getHolder();
    
    /* Activity必须实现SurfaceHolder.Callback */
    mSurfaceHolder01.addCallback(EX07_15.this);
    
    /* 额外的设置预览大小设定,在此不使用 */
    //mSurfaceHolder01.setFixedSize(320, 240);
    
    /*
     * 以SURFACE_TYPE_PUSH_BUFFERS(3)
     * 作为SurfaceHolder显示类型 
     * */
    mSurfaceHolder01.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    
    mButton01 = (Button)findViewById(R.id.myButton1);
    mButton02 = (Button)findViewById(R.id.myButton2);
    mButton03 = (Button)findViewById(R.id.myButton3);
    
    /* 开启相机及Preview */
    mButton01.setOnClickListener(new Button.OnClickListener()
    {
      @Override
      public void onClick(View v)
      {
        // TODO Auto-generated method stub
        
        /* 自定义初始化开启相机函数 */
        initCamera();
      }
    });
    
    /* 停止Preview及相机 */
    mButton02.setOnClickListener(new Button.OnClickListener()
    {
      @Override
      public void onClick(View arg0)
      {
        // TODO Auto-generated method stub
        
        /* 自定义重置相机,并关闭相机预览函数 */
        resetCamera();
      }
    });
    
    /* 拍照 */
    mButton03.setOnClickListener(new Button.OnClickListener()
    {
      @Override
      public void onClick(View arg0)
      {
        // TODO Auto-generated method stub
        
        /* 当存储卡存在才允许拍照,保存临时图像文件 */
        if(checkSDCard())
        {
          /* 自定义拍照函数 */
          takePicture();
        }
        else 
        {
          /* 存储卡不存在的显示提示 */
          mTextView01.setText
          (
            getResources().getText(R.string.str_err_nosd).toString()
          );
        }
      }
    });
  }
  
  /* 告订初始相机函数 */
  private void initCamera()
  {
    if(!bIfPreview)
    {
      /* 若相机非?预览模式,则开启相机 */
      try
      {
        /* 
         * The Heap 是应用程序?手机里执行所配置的空间
         * 当超过预设的16 MB(每几个应用程序)时就会导致 "Out of memory"的错误
         * 目前看来是Cupcake(AVD 1.5)?会发生 
         * Connect E from ICameraClient 0x....
         * new client (0x...) sttempting to connect - rejected
         * */
        mCamera01 = Camera.open();
      }
      catch(Exception e)
      {
        Log.e(TAG, e.getMessage());
      }
    }
    
    if (mCamera01 != null && !bIfPreview)
    {
      try
      {
        Log.i(TAG, "inside the camera");
        mCamera01.setPreviewDisplay(mSurfaceHolder01);
        /* 建立Camera.Parameters对象 */
        Camera.Parameters parameters = mCamera01.getParameters();
        /* 设定相片格式为JPEG */
        parameters.setPictureFormat(PixelFormat.JPEG);
        //parameters.setPreviewSize(w, h);
        List<Camera.Size> s=parameters.getSupportedPreviewSizes();
        
        try
        {
          if(s!=null)
          {
            for(int i=0;i<s.size();i++)
            {
              Log.i(TAG,""+(((Camera.Size)s.get(i)).height)+"/"+(((Camera.Size)s.get(i)).width));
            }
          }
          parameters.setPreviewSize(320, 240);
          //parameters.setPreviewSize(176, 144);
        
          /* ?2.0模拟器中,设置不支持的PreviewSize将造成Exception */
          s=parameters.getSupportedPictureSizes();
          try
          {
            if(s!=null)
            {
              for(int i=0;i<s.size();i++)
              {
                Log.i(TAG,""+(((Camera.Size)s.get(i)).height)+"/"+(((Camera.Size)s.get(i)).width));
              }
            }
            /* ?2.0模拟器中,设置不支持的PictureSize将造成Exception */
            parameters.setPictureSize(512, 384);
            //parameters.setPictureSize(213, 350);
            /* 将Camera.Parameters设定予Camera */
            mCamera01.setParameters(parameters);
            /* setPreviewDisplay唯几的参数为SurfaceHolder */
            mCamera01.setPreviewDisplay(mSurfaceHolder01);
            /* 立即运行Preview */
            mCamera01.startPreview();
            
            bIfPreview = true;
            Log.i(TAG, "startPreview");
          }
          catch (Exception e)
          {
            Log.i(TAG, e.toString());
            e.printStackTrace();
          }   
        }
        catch (Exception e)
        {
          Toast.makeText
          (
            EX07_15.this,
            "initCamera error.",
            Toast.LENGTH_LONG
          ).show();
          e.printStackTrace();
        }
      }
      catch (IOException e)
      {
        // TODO Auto-generated catch block
        mCamera01.release();
        mCamera01 = null;
        Log.i(TAG, e.toString());
        e.printStackTrace();
      }
    }
  }
  
  /* 拍照撷取影像 */ 
  private void takePicture() 
  {
    if (mCamera01 != null && bIfPreview) 
    {
      /* 调用takePicture()方法拍照 */
      mCamera01.takePicture(shutterCallback, rawCallback, jpegCallback);
    }
  }
  
  /* 相机重置 */
  private void resetCamera()
  {
    if (mCamera01 != null && bIfPreview)
    {
      mCamera01.stopPreview();
      
      /* 延含学习,释放Camera对象 */
      //mCamera01.release();
      //mCamera01 = null;
      
      Log.i(TAG, "stopPreview");
      bIfPreview = false;
    }
  }
   
  private ShutterCallback shutterCallback = new ShutterCallback() 
  { 
    public void onShutter() 
    { 
      // Shutter has closed 
    } 
  }; 
   
  private PictureCallback rawCallback = new PictureCallback() 
  { 
    public void onPictureTaken(byte[] _data, Camera _camera) 
    { 
      // TODO Handle RAW image data 
    } 
  }; 

  private PictureCallback jpegCallback = new PictureCallback() 
  {
    public void onPictureTaken(byte[] _data, Camera _camera)
    {
      // TODO Handle JPEG image data
      
      /* onPictureTaken传入的第几个参数即为相片的byte */
      Bitmap bm = BitmapFactory.decodeByteArray(_data, 0, _data.length); 
      
      /* 创建文件 */
      File myCaptureFile = new File(strCaptureFilePath);
      try
      {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
        /* 采用压缩转档方法 */
        bm.compress(Bitmap.CompressFormat.JPEG, 80, bos);
        
        /* 调用flush()方法,更新BufferStream */
        bos.flush();
        
        /* 结束OutputStream */
        bos.close();
        
        /* 将拍照下来且保存完毕的图文件,显示出来 */ 
        mImageView01.setImageBitmap(bm);
        
        /* 显示完图片,立即重置相机,并关闭预览 */
        resetCamera();
        
        /* 再重新启动相机继续预览 */
        initCamera();
      }
      catch (Exception e)
      {
        Log.e(TAG, e.getMessage());
        Log.e(TAG, e.toString());
      }
    }
  };
  
  /* 自定义文件函数 */
  private void delFile(String strFileName)
  {
    try
    {
      File myFile = new File(strFileName);
      if(myFile.exists())
      {
        myFile.delete();
      }
    }
    catch (Exception e)
    {
      Log.e(TAG, e.toString());
      e.printStackTrace();
    }
  }
  
  public void mMakeTextToast(String str, boolean isLong)
  {
    if(isLong==true)
    {
      Toast.makeText(EX07_15.this, str, Toast.LENGTH_LONG).show();
    }
    else
    {
      Toast.makeText(EX07_15.this, str, Toast.LENGTH_SHORT).show();
    }
  }
  
  private boolean checkSDCard()
  {
    /* 判断存储卡是否存在 */
    if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
    {
      return true;
    }
    else
    {
      return false;
    }
  }
  
  @Override
  public void surfaceChanged(SurfaceHolder surfaceholder, int format, int w, int h)
  {
    // TODO Auto-generated method stub
    Log.i(TAG, "Surface Changed1");
  }
  
  @Override
  public void surfaceCreated(SurfaceHolder surfaceholder)
  {
    // TODO Auto-generated method stub
    Log.i(TAG, "Surface Changed2");
  }
  
  @Override
  public void surfaceDestroyed(SurfaceHolder surfaceholder)
  {
    // TODO Auto-generated method stub
    /* 当Surface不存在,就需要删除图片 */
    try
    {
      delFile(strCaptureFilePath);
      mCamera01.stopPreview();
      mCamera01.release();
      mCamera01 = null;
      Log.i(TAG, "Surface Destroyed");
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
  }

  @Override
  protected void onPause()
  {
    // TODO Auto-generated method stub
    try
    {
      resetCamera();
      mCamera01.release();
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
    super.onPause();
  }
  
}



分享到:
评论

相关推荐

    Android圆形相机预览窗口,圆形SurfaceView

    Android圆形相机预览窗口,圆形SurfaceView

    Android代码-Android相机预览拍照录像滤镜详解

    本系列会包括Android Camera预览,拍照,视频录制,播放,滤镜及渲染等,还会对相机系统原理做深入分析。 一、相机预览 序号 项目名称 内容简介 1 GLSurfacePreview GLSurfaceView OpenGL相机预览,直接绘制...

    android camera mirror 使用SurfaceView自定义相机视频实现预览时左右镜像,实现镜子效果

    android camera mirror 前置后置相机视频预览时左右镜像,实现镜子效果 基于以下网址实现的android studio项目源代码包,可以直接打开使用 https://blog.csdn.net/mbrose/article/details/80167781 代码做了一...

    Android多种方式实现相机圆形预览的示例代码

    一、为预览控件设置圆角 为控件设置ViewOutlineProvider public RoundTextureView(Context context, AttributeSet attrs) { super(context, attrs); setOutlineProvider(new ViewOutlineProvider() { @Override ...

    android 自定义相机拍照

    android自定义相机的实现,主要实现了通过surfaceview跟camera实现拍照,预览,图片的保存。能够进行前后摄像头的切换,自动和手动聚焦,设置闪光灯模式 ,人脸识别等 详细请见博客地址 ...

    自定义照相机

    自定义照相机,在界面中预览,拍照,并显示拍照后的图片

    Android 实例实现自定义Camera和前后置摄像头切换以及图片缩小放大预览

    3.实现Camera拍照后图片缩小显示以及正常预览; 4.实现Camera拍照后图片保存; 在具体实现代码之前,我们先来了解一下Android api对实现自定义Camera的介绍。 根据api的介绍,对于Camera应用可以简单总结以下几个...

    android Camera自动拍照、自动对焦并获取指定大小图片

    1.首先android6.0以上版本要动态申请相机和文件读写权限 2.废话不啰嗦,上代码: /** * 摄像头画面采集界面 */ public class DistinguishActivity extends BaseActivity implements SurfaceHolder.Callback, ...

    Android自定义照相机倒计时拍照

    自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: 两个TextView是用来显示提示信息和倒计时的秒数的 相关教程:Android开发从相机或相册获取图片裁剪 Android启动相机拍照并返回图片 &lt;...

    android 7自定义相机预览及拍照功能

    本文实例为大家分享了Android实现摄像头切换,拍照及保存到相册,预览等功能,解决android7拍照之后不能连续预览的问题、参数设置相关问题以及前后摄像头语言颠倒等问题。 import android.Manifest; import android...

    Google Android SDK开发范例大全(PDF完整版4)(4-4)

    7.15 相机预览及拍照临时文件——Camera及PictureCallback事件 第8章 当Android与Internet接轨 8.1 HTTPGET/POST传递参数——HTTP连接示范 8.2 在程序里浏览网页——WebView.loadUrl 8.3 嵌入HTML标记的程序——...

    Google Android SDK开发范例大全(PDF高清完整版1)(4-1)

    7.15 相机预览及拍照临时文件——Camera及PictureCallback事件 第8章 当Android与Internet接轨 8.1 HTTPGET/POST传递参数——HTTP连接示范 8.2 在程序里浏览网页——WebView.loadUrl 8.3 嵌入HTML标记的程序——...

    Android 相机控制:自定义相机Test实例.rar

    Android 相机控制:自定义相机Test实例 ,author 孙尚磊,可实现设置相机的各种参数,对焦的方式,照片的类型,对焦监听,拍照成功回调函数,验证手机是否有sdcard,相片预览,相机与SurfaceView绑定,预览的图片...

    Google Android SDK开发范例大全(PDF高清完整版3)(4-3)

    7.15 相机预览及拍照临时文件——Camera及PictureCallback事件 第8章 当Android与Internet接轨 8.1 HTTPGET/POST传递参数——HTTP连接示范 8.2 在程序里浏览网页——WebView.loadUrl 8.3 嵌入HTML标记的程序——...

    Google Android SDK开发范例大全(第3版) 1/5

    超酷的娱乐多媒体设计:触控移动事件、建立手势、手势判断控制、屏幕保护程序、启动动画、mp3播放器、3gp影片播放、相机预览拍照、自动对焦、调整音量、信息指令遥控手机、录音控制、自定义动画按钮等 Web Service...

    Google Android SDK开发范例大全(第3版) 4/5

    超酷的娱乐多媒体设计:触控移动事件、建立手势、手势判断控制、屏幕保护程序、启动动画、mp3播放器、3gp影片播放、相机预览拍照、自动对焦、调整音量、信息指令遥控手机、录音控制、自定义动画按钮等 Web Service...

    Google Android SDK开发范例大全(第3版) 3/5

    超酷的娱乐多媒体设计:触控移动事件、建立手势、手势判断控制、屏幕保护程序、启动动画、mp3播放器、3gp影片播放、相机预览拍照、自动对焦、调整音量、信息指令遥控手机、录音控制、自定义动画按钮等 Web Service...

    Google Android SDK开发范例大全(第3版) 5/5

    超酷的娱乐多媒体设计:触控移动事件、建立手势、手势判断控制、屏幕保护程序、启动动画、mp3播放器、3gp影片播放、相机预览拍照、自动对焦、调整音量、信息指令遥控手机、录音控制、自定义动画按钮等 Web Service...

    android自定义相机

    android自定义相机拍照,通过SurfaceView设置自定义预览

Global site tag (gtag.js) - Google Analytics