日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Android实时取景:用SurfaceView实现

發(fā)布時(shí)間:2024/2/2 综合教程 43 生活家
生活随笔 收集整理的這篇文章主要介紹了 Android实时取景:用SurfaceView实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

update: 這篇blog沒有處理android sdk api>=23時(shí)的動(dòng)態(tài)權(quán)限問題。建議直接使用這一篇:Android SurfaceView Tutorial With Example


對(duì)于基于攝像頭的Android應(yīng)用,實(shí)時(shí)取景是一個(gè)基本前提,通過前置或后置攝像頭持續(xù)獲取捕獲到的內(nèi)容,可以進(jìn)一步做處理(人臉檢測(cè)、美顏、濾鏡等)。

所謂實(shí)時(shí)取景,簡(jiǎn)單說就是調(diào)用android的攝像頭,把攝像頭捕獲的內(nèi)容顯示在apk的界面上。只要應(yīng)用不關(guān)閉,相機(jī)就持續(xù)捕獲,apk上看到的就是實(shí)時(shí)的取景了。

采用SurfaceView和Camera來做這件事。
是SDK自帶的SurfaceView類而不是實(shí)現(xiàn)它的子類;在布局XML文件中使用SurfaceView而不是FrameLayout。因此,代碼量很少也很容易理解。

從View到SurfaceView

android應(yīng)用,和用戶交互的GUI界面,是搭載在Activity上的。Activity創(chuàng)建的時(shí)候,往往會(huì)做setContentView(R.id.main_layout),這是根據(jù)xml布局文件設(shè)定要預(yù)先確定好的各種view對(duì)象,這些組件在xml中進(jìn)行設(shè)計(jì)、設(shè)定。當(dāng)然也可以在Java代碼中進(jìn)一步動(dòng)態(tài)增加view對(duì)象。相當(dāng)于layout作為各種view的容器。

android sdk自帶了很多view的子類供使用。

View本身:繼承自java.lang.Object類,實(shí)現(xiàn)了Drawable.Callback, KeyEvent.Callback, AccessibilitiyEventSource接口。
直接子類有:
AnalogClock:模擬時(shí)鐘,有3個(gè)指針那種。
ImageView:顯示圖像。其實(shí),任何Drawable對(duì)象都可以用ImageView來顯示。
KeyboardView:內(nèi)置鍵盤。
MediaRouteButton:媒體路由按鈕(不太懂。似乎和多媒體、網(wǎng)絡(luò)路由相關(guān))
ProgressBar:(可視化)進(jìn)度條展示。
Space:空白視圖。輕量級(jí)視圖。作用:在不同組件之間插入縫隙、間隔。
SurfaceView:提供了一個(gè)專門用于繪制的Surface。Surface的格式、尺寸可以控制。SurfaceView控制這個(gè)surface的繪制位置。。。中文翻譯
TextView:文本視圖。
TextureView:紋理視圖。sdk4.0之后的API中可用。常被拿來和SurfaceView比較。
ViewGroup:用來容納其他view對(duì)象。是layout(布局)和view containers(視圖容器)的基類。
ViewStub:視圖存根。大小為0、不可見,用來占坑的,apk運(yùn)行時(shí)把坑交給資源。

間接子類有:
AbsListView
AbsSeekBar
AbsSpinner
AbsoluteLayout
AdapterView
AdapterViewAnimator
AdapterViewFlipper
以及其他56個(gè)間接子類。

可以看到,SurfaceView和TextureView兩個(gè)view子類,都能用于實(shí)時(shí)取景框的顯示。但是考慮到TextureView需要開啟硬件加速的支持,不考慮。以及,目前看來SurfaceView本身也能勝任實(shí)時(shí)取景的任務(wù)。

代碼

layout文件:surfaceview_main.xml

看到很多教程用的都是FrameLayout,而不是SurfaceView。我很不理解:為什么不用SurfaceView呢?不好用嗎?
anyway,我這里就用SurfaceView了,在我測(cè)試過的代碼中是完全可用的。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
tools:context="com.example.chris.myapplication.MainActivity">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"/>

</LinearLayout>

java代碼 ChrisActivity.java

Surface、SurfaceView、SurfaceHolder這三者相當(dāng)于MVC的存在,Surface是數(shù)據(jù),SurfaceView負(fù)責(zé)顯示,SurfaceHolder控制了Surface。通過讓Activity實(shí)現(xiàn)SurfaceHolder.Callback接口,開發(fā)者自行實(shí)現(xiàn)下面三個(gè)函數(shù),開發(fā)者就完成內(nèi)容的處理:

public void surfaceCreated(SurfaceHolder holder);
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height);
public void surfaceDestroyed(SurfaceHolder holder);

而具體到實(shí)現(xiàn),一些額外的細(xì)節(jié)也要考慮到:相機(jī)的初始化和釋放;應(yīng)用暫停時(shí)釋放相機(jī),恢復(fù)時(shí)獲取相機(jī);屏幕方向與顯示方向的一致。所以有如下代碼:

package com.example.chris.myapplication;

import android.app.Activity;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

import java.io.IOException;

/**
 * Created by chris on 2017/6/25.
 * 網(wǎng)上找了一些博客、教程和代碼,稍微有點(diǎn)頭緒了,現(xiàn)在寫自己的Activity代碼
 */

@SuppressWarnings("deprecation")
// TODO:把camera換成camera2接口??
public class ChrisActivity extends Activity implements SurfaceHolder.Callback{
    private static final String TAG = "ChrisAcvitity";
    private Camera mCamera;
    private SurfaceHolder mHolder;
    private SurfaceView mView;

    @Override
    // 創(chuàng)建Activity時(shí)執(zhí)行的動(dòng)作
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.surfaceview_main);

        mView = (SurfaceView) findViewById(R.id.surfaceView);
        mHolder = mView.getHolder();
        mHolder.addCallback(this);
    }

    @Override
    // apk暫停時(shí)執(zhí)行的動(dòng)作:把相機(jī)關(guān)閉,避免占用導(dǎo)致其他應(yīng)用無法使用相機(jī)
    protected void onPause() {
        super.onPause();

        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }

    @Override
    // 恢復(fù)apk時(shí)執(zhí)行的動(dòng)作
    protected void onResume() {
        super.onResume();
        if (null!=mCamera){
            mCamera = getCameraInstance();
            try {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
            } catch(IOException e) {
                Log.d(TAG, "Error setting camera preview: " + e.getMessage());
            }
        }
    }


    // SurfaceHolder.Callback必須實(shí)現(xiàn)的方法
    public void surfaceCreated(SurfaceHolder holder){
        mCamera = getCameraInstance();
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch(IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    // SurfaceHolder.Callback必須實(shí)現(xiàn)的方法
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
        refreshCamera(); // 這一步是否多余?在以后復(fù)雜的使用場(chǎng)景下,此步驟是必須的。
        int rotation = getDisplayOrientation(); //獲取當(dāng)前窗口方向
        mCamera.setDisplayOrientation(rotation); //設(shè)定相機(jī)顯示方向
    }

    // SurfaceHolder.Callback必須實(shí)現(xiàn)的方法
    public void surfaceDestroyed(SurfaceHolder holder){
        mHolder.removeCallback(this);
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }

    // === 以下是各種輔助函數(shù) ===

    // 獲取camera實(shí)例
    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open();
        } catch(Exception e){
            Log.d("TAG", "camera is not available");
        }
        return c;
    }

    // 獲取當(dāng)前窗口管理器顯示方向
    private int getDisplayOrientation(){
        WindowManager windowManager = getWindowManager();
        Display display = windowManager.getDefaultDisplay();
        int rotation = display.getRotation();
        int degrees = 0;
        switch (rotation){
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        android.hardware.Camera.CameraInfo camInfo =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, camInfo);

        // 這里其實(shí)還是不太懂:為什么要獲取camInfo的方向呢?相當(dāng)于相機(jī)標(biāo)定??
        int result = (camInfo.orientation - degrees + 360) % 360;

        return result;
    }

    // 刷新相機(jī)
    private void refreshCamera(){
        if (mHolder.getSurface() == null){
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch(Exception e){
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (Exception e) {

        }
    }

}

AndroidManifest.xml 記得添加相機(jī)權(quán)限

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.chris.myapplication" >

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".ChrisActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

總結(jié)

以上是生活随笔為你收集整理的Android实时取景:用SurfaceView实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。