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