Open the camera preview with CameraX and display it on the screen. Combine the functions of suspension Windows. Implementation of a draggable suspension window, real-time preview of the camera example.

This example is in a separate module. Note the subtle differences in Gradle when using it.

Operate the camera and open preview. This section of code is the same as opening the camera preview for Android CameraX. The suspension window related code is the same as the draggable suspension window. On this basis, the limitation of drag range is added.

Introduction of depend on

Some configurations of the gradle module, using Android SDK version 31, with Databinding enabled

plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    id 'kotlin-kapt'
}

android {
    compileSdk 31

    defaultConfig {
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
    }
    dataBinding {
        enabled = true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'androidx. Appcompat: appcompat: 1.4.0'
    implementation 'com. Google. Android. Material: material: 1.4.0'
    implementation project(path: ':baselib')
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx. Test. Ext: junit: 1.1.3'
    androidTestImplementation 'androidx. Test. Espresso: espresso - core: 3.4.0'

    implementation "Androidx. Camera: the camera - the core: 1.1.0 - alpha11"
    implementation "Androidx. Camera: the camera - camera2:1.1.0 - alpha11"
    implementation "Androidx. Camera: the camera - lifecycle: 1.1.0 - alpha11"
    implementation "Androidx. Camera: the camera - view: 1.0.0 - alpha31"
    implementation "Androidx. Camera: the camera - extensions: 1.0.0 - alpha31"
}
Copy the code

Introduction of CameraX dependencies (CameraX core library is implemented with CamerA2), currently mainly using 1.0-Alpha11 version

permissions

Need a dynamic application for android. Permission. CAMERA permissions

<uses-permission android:name="android.permission.CAMERA" />
Copy the code

This article skips the dynamic permission application

layout

CameraX provides androidx. Camera. The PreviewView

Put it in a FrameLayout like me_act_simple_preivew_X_scale.xml


      
<layout>

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent">

        <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.camera.view.PreviewView
                android:id="@+id/previewView"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </FrameLayout>

        <LinearLayout
            android:id="@+id/func_field"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:gravity="center"
            android:orientation="vertical"
            android:padding="4dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <Button
                    android:id="@+id/start"
                    style="@style/NormalBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Turn on the camera." />

                <Button
                    android:id="@+id/end"
                    style="@style/NormalBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="4dp"
                    android:text="Stop the camera." />

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:orientation="horizontal">

                <Button
                    android:id="@+id/enable_ana"
                    style="@style/NormalBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="setAnalyzer" />

                <Button
                    android:id="@+id/clr_ana"
                    style="@style/NormalBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="4dp"
                    android:text="clearAnalyzer" />

                <Button
                    android:id="@+id/take_one_analyse"
                    style="@style/NormalBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="4dp"
                    android:text="Capture" />

            </LinearLayout>
        </LinearLayout>

        <View
            android:id="@+id/touch_move"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone" />

        <ImageView
            android:id="@+id/zoom_iv"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_margin="12dp"
            android:src="@drawable/me_ic_to_small" />

        <TextView
            android:id="@+id/tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:text="rustfisher.com" />

    </RelativeLayout>
</layout>
Copy the code

Func_field holds some buttons. Zoom out and restore the interface using zoom_IV

style

Prepare a style

<style name="MeTranslucentAct" parent="AppTheme">
    <item name="android:windowBackground"># 80000000</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
</style>
Copy the code

Registered in the manifest

<activity
    android:name=".camera.MeSimplePreviewXFloatingAct"
    android:exported="true"
    android:theme="@style/MeTranslucentAct" />
Copy the code

activity

Enabling the Camera

New MeSimplePreviewXFloatingAct, inherit androidx appcompat. App. AppCompatActivity

// Get the mCameraProvider from onCreate
mCameraProviderFuture = ProcessCameraProvider.getInstance(this);
mCameraProviderFuture.addListener(() -> {
    try {
        mCameraProvider = mCameraProviderFuture.get();
        Log.d(TAG, "Got the cameraProvider");
        } catch (ExecutionException | InterruptedException e) {
        // Do not handle it here
    }
}, ContextCompat.getMainExecutor(this));
Copy the code

In order to obtain ProcessCameraProvider, use ProcessCameraProvider. Get a cameraProviderFuture getInstance method. Fetch ProcessCameraProvider (cameraProvider) after cameraProviderFuture completes.

Open the camera method bindPreview

private void bindPreview(ProcessCameraProvider cameraProvider) {
    if (cameraProvider == null) {
        Toast.makeText(getApplicationContext(), "No camera was obtained.", Toast.LENGTH_SHORT).show();
        return;
    }
    Toast.makeText(getApplicationContext(), "Camera on", Toast.LENGTH_SHORT).show();
    Preview preview = new Preview.Builder().build();

    CameraSelector cameraSelector = new CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build();

    preview.setSurfaceProvider(mBinding.previewView.getSurfaceProvider());

    cameraProvider.bindToLifecycle(this, cameraSelector, preview, mImageAnalysis);
    mRunning = true;
}
Copy the code

To enable Preview, build a Preview with Preview.Builder. Use Camera Elector to select the rear camera. Preview SurfaceProvider by the layout of the androidx. Camera. The PreviewView provided.

After cameraProvider. BindToLifecycle binding on, start the camera preview

Floating window

Set the window flag before setContentView

WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
Copy the code

Zoom in and out of Windows

In the window need to use the android. View. WindowManager. LayoutParams

private void toSmallWindow(a) {
    mBinding.funcField.setVisibility(View.GONE);
    mIsSmallWindow = true;
    mBinding.zoomIv.setImageResource(R.drawable.me_to_big);

    android.view.WindowManager.LayoutParams p = getWindow().getAttributes();
    p.height = 480; // The size of the suspension window can be determined by yourself
    p.width = 360;
    p.dimAmount = 0.0 f;
    getWindow().setAttributes(p);
}

private void toBigWindow(a) {
    WindowManager.LayoutParams lp = getWindow().getAttributes();
    lp.x = 0;
    lp.y = 0;
    getWindow().setAttributes(lp);

    getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    mBinding.funcField.setVisibility(View.VISIBLE);
    mIsSmallWindow = false;
    mBinding.zoomIv.setImageResource(R.drawable.me_ic_to_small);
}
Copy the code

Please prepare the image resources of the button

Limit drag range

Get a reference range first

mBinding.container.post(() -> {
    mBigWid = mBinding.container.getWidth();
    mBigHeight = mBinding.container.getHeight();
    Log.d(TAG, "container size: " + mBigWid + "," + mBigHeight);
});
Copy the code

Related reading: Get the width and height of the view

Complete code for activity

// package com.rustfisher.mediasamples.camera;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;

import com.google.common.util.concurrent.ListenableFuture;
import com.rustfisher.mediasamples.R;
import com.rustfisher.mediasamples.databinding.MeActSimplePreivewXScaleBinding;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/** * preview camera hover window **@author an.rustfisher.com
 * @dateThe 2021-12-31 for * /
public class MeSimplePreviewXFloatingAct extends AppCompatActivity {
    private static final String TAG = "rfDevX";
    private MeActSimplePreivewXScaleBinding mBinding;
    private ListenableFuture<ProcessCameraProvider> mCameraProviderFuture;
    private ProcessCameraProvider mCameraProvider;
    private boolean mRunning = false;

    private boolean mIsSmallWindow = false;
    private boolean mLimitArea = true;

    private boolean mTakeOneYuv = false; // Get a frame. Do not do this in real projects

    private final ImageAnalysis mImageAnalysis =
            new ImageAnalysis.Builder()
                    //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
                    .setTargetResolution(new Size(720.1280)) // Suggested size of image
                    .setOutputImageRotationEnabled(true) // Whether to rotate the image obtained in the parser
                    .setTargetRotation(Surface.ROTATION_0) // Allow rotation to get image rotation Settings
                    .setBackpressureStrategy(ImageAnalysis.STRATEGY_BLOCK_PRODUCER)
                    .build();

    private float mLastTx = 0; // The upper position of the finger
    private float mLastTy = 0;

    private int mBigHeight = 0;
    private int mBigWid = 0;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        mBinding = DataBindingUtil.setContentView(this, R.layout.me_act_simple_preivew_x_scale);
        mCameraProviderFuture = ProcessCameraProvider.getInstance(this);
        mCameraProviderFuture.addListener(() -> {
            try {
                mCameraProvider = mCameraProviderFuture.get();
                Log.d(TAG, "Got the cameraProvider");
// bindPreview(mCameraProvider);
            } catch (ExecutionException | InterruptedException e) {
                // Do not handle it here
            }
        }, ContextCompat.getMainExecutor(this));
        mBinding.start.setOnClickListener(v -> {
            if(mCameraProvider ! =null&&! mRunning) { bindPreview(mCameraProvider); }}); mBinding.end.setOnClickListener(v -> { mCameraProvider.unbindAll(); mRunning =false;
        });

        mBinding.takeOneAnalyse.setOnClickListener(v -> {
            mTakeOneYuv = true;
            Log.d(TAG, "Get a frame, output image rotation:" + mImageAnalysis.isOutputImageRotationEnabled());
        });

        final ExecutorService executorService = Executors.newFixedThreadPool(2);
        mBinding.enableAna.setOnClickListener(v -> {
            Toast.makeText(getApplicationContext(), "Enable profiler", Toast.LENGTH_SHORT).show();
            mImageAnalysis.setAnalyzer(executorService, imageProxy -> {
                // Let's process the data
                if (mTakeOneYuv) {
                    mTakeOneYuv = false;
                    Log.d(TAG, "Rotation Angle:" + imageProxy.getImageInfo().getRotationDegrees());
                    ImgHelper.useYuvImgSaveFile(imageProxy, true); // Store this frame as a file
                    runOnUiThread(() -> Toast.makeText(getApplicationContext(), "Capture a frame", Toast.LENGTH_SHORT).show());
                }
                imageProxy.close(); // Finally close this
            });
        });
        mBinding.clrAna.setOnClickListener(v -> {
            mImageAnalysis.clearAnalyzer();
            Toast.makeText(getApplicationContext(), "clearAnalyzer", Toast.LENGTH_SHORT).show();
        });
        mBinding.zoomIv.setOnClickListener(v -> {
            if (mIsSmallWindow) {
                toBigWindow();
            } else{ toSmallWindow(); }}); mBinding.root.setOnTouchListener((v, event) -> {switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Log.d(TAG, "down " + event);
                    mLastTx = event.getRawX();
                    mLastTy = event.getRawY();
                    return true;
                case MotionEvent.ACTION_MOVE:
                    Log.d(TAG, "move " + event);
                    float dx = event.getRawX() - mLastTx;
                    float dy = event.getRawY() - mLastTy;
                    mLastTx = event.getRawX();
                    mLastTy = event.getRawY();
                    Log.d(TAG, " dx: " + dx + ", dy: " + dy);
                    if (mIsSmallWindow) {
                        WindowManager.LayoutParams lp = getWindow().getAttributes();
                        int tx = (int) (lp.x + dx);
                        int ty = (int) (lp.y + dy);
                        Log.d(TAG, "move to " + tx + "," + ty);
                        if (mLimitArea) {
                            tx = Math.max(lp.width / 2 - mBigWid / 2, tx);
                            tx = Math.min(mBigWid / 2 - lp.width / 2, tx);
                            ty = Math.max(lp.height / 2 - mBigHeight / 2, ty);
                            ty = Math.min(mBigHeight / 2 - lp.height / 2, ty);
                        }
                        lp.x = tx;
                        lp.y = ty;
                        getWindow().setAttributes(lp);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    Log.d(TAG, "up " + event);
                    return true;
                case MotionEvent.ACTION_CANCEL:
                    Log.d(TAG, "cancel " + event);
                    return true;
            }
            return false;
        });
        mBinding.container.post(() -> {
            mBigWid = mBinding.container.getWidth();
            mBigHeight = mBinding.container.getHeight();
            Log.d(TAG, "container size: " + mBigWid + "," + mBigHeight);
        });
    }

    private void bindPreview(ProcessCameraProvider cameraProvider) {
        if (cameraProvider == null) {
            Toast.makeText(getApplicationContext(), "No camera was obtained.", Toast.LENGTH_SHORT).show();
            return;
        }
        Toast.makeText(getApplicationContext(), "Camera on", Toast.LENGTH_SHORT).show();
        Preview preview = new Preview.Builder().build();

        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();

        preview.setSurfaceProvider(mBinding.previewView.getSurfaceProvider());

        cameraProvider.bindToLifecycle(this, cameraSelector, preview, mImageAnalysis);
        mRunning = true;
    }

    private void toSmallWindow(a) {
        mBinding.funcField.setVisibility(View.GONE);
        mIsSmallWindow = true;
        mBinding.zoomIv.setImageResource(R.drawable.me_to_big);

        android.view.WindowManager.LayoutParams p = getWindow().getAttributes();
        p.height = 480;
        p.width = 360;
        p.dimAmount = 0.0 f;
        getWindow().setAttributes(p);
    }

    private void toBigWindow(a) {
        WindowManager.LayoutParams lp = getWindow().getAttributes();
        lp.x = 0;
        lp.y = 0;
        getWindow().setAttributes(lp);

        getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mBinding.funcField.setVisibility(View.VISIBLE);
        mIsSmallWindow = false; mBinding.zoomIv.setImageResource(R.drawable.me_ic_to_small); }}Copy the code

Run the test

Run the Activity on your phone to see a preview of the camera. Image aspect ratio normal, no stretching phenomenon. After shrinking to a suspension window, you can drag it.

  • Glory EMUI 3.1 Lite, Android 5.1 running fine
  • Redmi 9A, MIUI 12.5.1 stable, Android 10 running fine

summary

CameraX simplifies developers’ lives by simply opening the camera to preview. PreviewView is provided, and developers do not need to customize SurfaceView or TextureView. In live preview, the camera can focus automatically. Try pressing the home button to return to your desktop, or lock the screen and come back.

reference

  • Android floating window
  • Android CameraX opens camera preview