This topic describes how to customize a UI and add the scan capability in the custom UI to a project. This process involves the following four steps:
Procedure
Create a dependency project
Choose File > New > New Module.

Select Android Library and click Next.

Enter custom in Module name and click Finish.

Create and customize a UI in the dependency project
Create a
widgetpackage in thecom.example.custompackage of thecustommodule. In thewidgetpackage, add theAPSurfaceTextureclass that inherits from theSurfaceTextureclass to capture image streams.public class APSurfaceTexture extends SurfaceTexture { private static final String TAG = "APSurfaceTexture"; public SurfaceTexture mSurface; public APSurfaceTexture() { super(0); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public void attachToGLContext(int texName) { mSurface.attachToGLContext(texName); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public void detachFromGLContext() { try { mSurface.detachFromGLContext(); } catch (Exception ex) { try { Method nativeMethod = SurfaceTexture.class.getDeclaredMethod("nativeDetachFromGLContext"); nativeMethod.setAccessible(true); int retCode = (Integer) nativeMethod.invoke(mSurface); LoggerFactory.getTraceLogger().debug(TAG, "nativeDetachFromGLContext invoke retCode:" + retCode); } catch (Exception e) { LoggerFactory.getTraceLogger().error(TAG, "nativeDetachFromGLContext invoke exception:" + e.getMessage()); } LoggerFactory.getTraceLogger().error(TAG, "mSurface.detachFromGLContext() exception:" + ex.getMessage()); } } @Override public boolean equals(Object o) { return mSurface.equals(o); } @Override public long getTimestamp() { return mSurface.getTimestamp(); } @Override public void getTransformMatrix(float[] mtx) { mSurface.getTransformMatrix(mtx); } @Override public void release() { super.release(); mSurface.release(); } @Override public int hashCode() { return mSurface.hashCode(); } @TargetApi(Build.VERSION_CODES.KITKAT) @Override public void releaseTexImage() { mSurface.releaseTexImage(); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) @Override public void setDefaultBufferSize(int width, int height) { mSurface.setDefaultBufferSize(width, height); } @Override public void setOnFrameAvailableListener(OnFrameAvailableListener listener) { mSurface.setOnFrameAvailableListener(listener); } @Override public String toString() { return mSurface.toString(); } @Override public void updateTexImage() { mSurface.updateTexImage(); } }In the
widgetpackage of thecustommodule, add theAPTextureViewclass that inherits from theTextureViewclass for the display of image streams.public class APTextureView extends TextureView { private static final String TAG = "APTextureView"; private Field mSurfaceField; public APTextureView(Context context) { super(context); } public APTextureView(Context context, AttributeSet attrs) { super(context, attrs); } public APTextureView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDetachedFromWindow() { try { super.onDetachedFromWindow(); } catch (Exception ex) { LoggerFactory.getTraceLogger().error(TAG, "onDetachedFromWindow exception:" + ex.getMessage()); } } @Override public void setSurfaceTexture(SurfaceTexture surfaceTexture) { super.setSurfaceTexture(surfaceTexture); afterSetSurfaceTexture(); } private void afterSetSurfaceTexture() { LoggerFactory.getTraceLogger().debug(TAG, "afterSetSurfaceTexture Build.VERSION.SDK_INT:" + Build.VERSION.SDK_INT); if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 20) { return; } try { if (mSurfaceField == null) { mSurfaceField = TextureView.class.getDeclaredField("mSurface"); mSurfaceField.setAccessible(true); } SurfaceTexture innerSurface = (SurfaceTexture) mSurfaceField.get(this); if (innerSurface != null) { if (!(innerSurface instanceof APSurfaceTexture)) { APSurfaceTexture wrapSurface = new APSurfaceTexture(); wrapSurface.mSurface = innerSurface; mSurfaceField.set(this, wrapSurface); LoggerFactory.getTraceLogger().debug(TAG, "afterSetSurfaceTexture wrap mSurface"); } } } catch (Exception ex) { LoggerFactory.getTraceLogger().error(TAG, "afterSetSurfaceTexture exception:" + ex.getMessage()); } } }In the
com.example.custompackage, create aUtilsclass for the conversion of images.public class Utils { private static String TAG = "Utils"; public static void toast(Context context, String msg) { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } public static Bitmap changeBitmapColor(Bitmap bitmap, int color) { int bitmap_w = bitmap.getWidth(); int bitmap_h = bitmap.getHeight(); int[] arrayColor = new int[bitmap_w * bitmap_h]; int count = 0; for (int i = 0; i < bitmap_h; i++) { for (int j = 0; j < bitmap_w; j++) { int originColor = bitmap.getPixel(j, i); // Non-transparent area if (originColor != 0) { originColor = color; } arrayColor[count] = originColor; count++; } } return Bitmap.createBitmap(arrayColor, bitmap_w, bitmap_h, Bitmap.Config.ARGB_8888); } public static Bitmap uri2Bitmap(Context context, Uri uri) { Bitmap bitmap = null; InputStream in; try { in = context.getContentResolver().openInputStream(uri); if (in != null) { bitmap = BitmapFactory.decodeStream(in); in.close(); } } catch (Exception e) { LoggerFactory.getTraceLogger().error(TAG, "uri2Bitmap: Exception " + e.getMessage()); } return bitmap; } }In the
custommodule, create anattrs.xmlfile under theres > valuesdirectory, and add the following code to the file.<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="scan"> <attr name="shadowColor" format="color" /> </declare-styleable> </resources>Create a
drawablefolder under theresdirectory of thecustommodule, and copy the resource files to thedrawablefolder, as shown in the figure below.
In the
widgetpackage of thecustommodule, add theFinderViewclass that inherits from theViewclass. Add the following code to implement the drawing of the scan window, corners, and border shadows.public class FinderView extends View { private static final int DEFAULT_SHADOW_COLOR = 0x96000000; private int scanWindowLeft, scanWindowTop, scanWindowRight, scanWindowBottom; private Bitmap leftTopCorner, rightTopCorner, leftBottomCorner, rightBottomCorner; private Paint paint; private int shadowColor; public FinderView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } public FinderView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private void init(Context context, AttributeSet attrs) { applyConfig(context, attrs); setVisibility(INVISIBLE); initCornerBitmap(context); paint = new Paint(); paint.setAntiAlias(true); } private void applyConfig(Context context, AttributeSet attrs) { if (attrs != null) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.scan); shadowColor = typedArray.getColor(R.styleable.scan_shadowColor, DEFAULT_SHADOW_COLOR); typedArray.recycle(); } } //Initialize the corner style of the scan window. private void initCornerBitmap(Context context) { Resources res = context.getResources(); leftTopCorner = BitmapFactory.decodeResource(res, R.drawable.scan_window_corner_left_top); rightTopCorner = BitmapFactory.decodeResource(res, R.drawable.scan_window_corner_right_top); leftBottomCorner = BitmapFactory.decodeResource(res, R.drawable.scan_window_corner_left_bottom); rightBottomCorner = BitmapFactory.decodeResource(res, R.drawable.scan_window_corner_right_bottom); } @Override public void draw(Canvas canvas) { super.draw(canvas); drawShadow(canvas); drawCorner(canvas); } //Draw the corner styles of the scan window. private void drawCorner(Canvas canvas) { paint.setAlpha(255); canvas.drawBitmap(leftTopCorner, scanWindowLeft, scanWindowTop, paint); canvas.drawBitmap(rightTopCorner, scanWindowRight - rightTopCorner.getWidth(), scanWindowTop, paint); canvas.drawBitmap(leftBottomCorner, scanWindowLeft, scanWindowBottom - leftBottomCorner.getHeight(), paint); canvas.drawBitmap(rightBottomCorner, scanWindowRight - rightBottomCorner.getWidth(), scanWindowBottom - rightBottomCorner.getHeight(), paint); } //Draw the border shadows of the scan window. private void drawShadow(Canvas canvas) { paint.setColor(shadowColor); canvas.drawRect(0, 0, getWidth(), scanWindowTop, paint); canvas.drawRect(0, scanWindowTop, scanWindowLeft, scanWindowBottom, paint); canvas.drawRect(scanWindowRight, scanWindowTop, getWidth(), scanWindowBottom, paint); canvas.drawRect(0, scanWindowBottom, getWidth(), getHeight(), paint); } /** * Determine the location of the scan window according to the position of RayView. */ public void setScanWindowLocation(int left, int top, int right, int bottom) { scanWindowLeft = left; scanWindowTop = top; scanWindowRight = right; scanWindowBottom = bottom; invalidate(); setVisibility(VISIBLE); } public void setShadowColor(int shadowColor) { this.shadowColor = shadowColor; } //Set the corner colors of the scan window. public void setCornerColor(int angleColor) { leftTopCorner = Utils.changeBitmapColor(leftTopCorner, angleColor); rightTopCorner = Utils.changeBitmapColor(rightTopCorner, angleColor); leftBottomCorner = Utils.changeBitmapColor(leftBottomCorner, angleColor); rightBottomCorner = Utils.changeBitmapColor(rightBottomCorner, angleColor); } }In the
widgetpackage, add theRayViewclass that inherits from theImageViewclass. Add the following code to implement the drawing of the scan lines.public class RayView extends ImageView { private FinderView mFinderView; private ScaleAnimation scanAnimation; private int[] location = new int[2]; public RayView(Context context, AttributeSet attrs) { super(context, attrs); } public RayView(Context context) { super(context); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //Set the position of the scan window in FinderView. getLocationOnScreen(location); if (mFinderView != null) { mFinderView.setScanWindowLocation(location[0], location[1], location[0] + getWidth(), location[1] + getHeight()); } } public void startScanAnimation() { setVisibility(VISIBLE); if (scanAnimation == null) { scanAnimation = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f); scanAnimation.setDuration(3000L); scanAnimation.setFillAfter(true); scanAnimation.setRepeatCount(Animation.INFINITE); scanAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); } startAnimation(scanAnimation); } public void stopScanAnimation() { setVisibility(INVISIBLE); if (scanAnimation != null) { this.clearAnimation(); scanAnimation = null; } } public void setFinderView(FinderView FinderView) { mFinderView = FinderView; } }Create a
res > layout > Filefolder, then create aview_scan.xmlfile under the folder, and add the following code to draw the layout of the scan page.<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <com.example.custom.widget.FinderView android:id="@+id/finder_view" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:id="@+id/back" android:layout_width="48dp" android:layout_height="48dp" android:scaleType="center" android:src="@drawable/icon_back" /> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="@string/custom_title" android:textColor="#ffffff" android:textSize="16sp" /> <ImageView android:id="@+id/gallery" android:layout_width="34dp" android:layout_height="34dp" android:layout_marginEnd="10dp" android:layout_marginRight="10dp" android:scaleType="fitXY" android:src="@drawable/selector_scan_from_gallery" /> <ImageView android:id="@+id/torch" android:layout_width="34dp" android:layout_height="34dp" android:layout_marginEnd="10dp" android:layout_marginRight="10dp" android:scaleType="fitXY" android:src="@drawable/selector_torch" /> </LinearLayout> <com.example.custom.widget.RayView android:id="@+id/ray_view" android:layout_width="270dp" android:layout_height="280dp" android:layout_centerInParent="true" android:background="@drawable/custom_scan_ray" /> <TextView android:id="@+id/tip_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/ray_view" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:includeFontPadding="false" android:text="@string/scan_tip" android:textColor="#7fffffff" android:textSize="14sp" /> </merge>In the
widgetpackage, add theScanViewclass that inherits from theRelativeLayoutclass. Add the following code. This code implements the interaction between the scan-related view and the scan engine.public class ScanView extends RelativeLayout { private RayView mRayView; public ScanView(Context context) { super(context); init(context); } public ScanView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public ScanView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context ctx) { LayoutInflater.from(ctx).inflate(R.layout.view_scan, this, true); FinderView finderView = (FinderView) findViewById(R.id.finder_view); mRayView = (RayView) findViewById(R.id.ray_view); mRayView.setFinderView(finderView); } public void onStartScan() { mRayView.startScanAnimation(); } public void onStopScan() { mRayView.stopScanAnimation(); } public float getCropWidth() { return mRayView.getWidth() * 1.1f; } public Rect getScanRect(Camera camera, int previewWidth, int previewHeight) { if (camera == null) { return null; } int[] location = new int[2]; mRayView.getLocationOnScreen(location); Rect r = new Rect(location[0], location[1], location[0] + mRayView.getWidth(), location[1] + mRayView.getHeight()); Camera.Size size; try { size = camera.getParameters().getPreviewSize(); } catch (Exception e) { return null; } if (size == null) { return null; } double rateX = (double) size.height / (double) previewWidth; double rateY = (double) size.width / (double) previewHeight; // The size of the crop box = The size of the grid animation box × 1.1 int expandX = (int) (mRayView.getWidth() * 0.05); int expandY = (int) (mRayView.getHeight() * 0.05); Rect resRect = new Rect( (int) ((r.top - expandY) * rateY), (int) ((r.left - expandX) * rateX), (int) ((r.bottom + expandY) * rateY), (int) ((r.right + expandX) * rateX)); Rect finalRect = new Rect( resRect.left < 0 ? 0 : resRect.left, resRect.top < 0 ? 0 : resRect.top, resRect.width() > size.width ? size.width : resRect.width(), resRect.height() > size.height ? size.height : resRect.height()); Rect rect1 = new Rect( finalRect.left / 4 * 4, finalRect.top / 4 * 4, finalRect.right / 4 * 4, finalRect.bottom / 4 * 4); int max = Math.max(rect1.right, rect1.bottom); int diff = Math.abs(rect1.right - rect1.bottom) / 8 * 4; Rect rect2; if (rect1.right > rect1.bottom) { rect2 = new Rect(rect1.left, rect1.top - diff, max, max); } else { rect2 = new Rect(rect1.left - diff, rect1.top, max, max); } return rect2; } }Create an
activity_custom_scan.xmlfile under theres > layoutfolder, and add the following code to the file. This code draws the main page of the custom scan feature.<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.mpaas.aar.demo.custom.widget.APTextureView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.mpaas.aar.demo.custom.widget.ScanView android:id="@+id/scan_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
Use the scan feature in the dependency project
In the
com.example.custompackage of thecustommodule, add aScanHelperclass and add the following code. This code calls the scan feature and obtains the callback result of a scan result.public class ScanHelper { private static class Holder { private static ScanHelper instance = new ScanHelper(); } private ScanCallback scanCallback; private ScanHelper() { } public static ScanHelper getInstance() { return Holder.instance; } public void scan(Context context, ScanCallback scanCallback) { if (context == null) { return; } this.scanCallback = scanCallback; context.startActivity(new Intent(context, CustomScanActivity.class)); } void notifyScanResult(boolean isProcessed, Intent resultData) { if (scanCallback != null) { scanCallback.onScanResult(isProcessed, resultData); scanCallback = null; } } public interface ScanCallback { void onScanResult(boolean isProcessed, Intent result); } }In the
com.example.custompackage of thecustommodule, add theCustomScanActivityclass that inherits from theActivityclass. Set the UI immersive mode and create theViewandButtoncorresponding to the resource file.public class CustomScanActivity extends Activity { private final String TAG = CustomScanActivity.class.getSimpleName(); private static final int REQUEST_CODE_PERMISSION = 1; private static final int REQUEST_CODE_PHOTO = 2; private ImageView mTorchBtn; private APTextureView mTextureView; private ScanView mScanView; private boolean isFirstStart = true; private boolean isPermissionGranted; private boolean isScanning; private boolean isPaused; private Rect scanRect; private MPScanner mpScanner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_scan); // Sets the immersive mode. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getWindow().setFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } mTextureView = findViewById(R.id.surface_view); mScanView = findViewById(R.id.scan_view); mTorchBtn = findViewById(R.id.torch); } @Override public void onPause() { super.onPause(); } @Override public void onResume() { super.onResume(); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } @Override public void onBackPressed() { super.onBackPressed(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); } }Follow the operations below to realize the function of opening mobile phone gallery.
Create a
pickImageFromGallerymethod inCustomScanActivity.private void pickImageFromGallery() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent, REQUEST_CODE_PHOTO); }In the
onCreatemethod inCustomScanActivity, add the click event ofgallery, and call thepickImageFromGallerymethod.findViewById(R.id.gallery).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { pickImageFromGallery(); } });
Follow the operations below to realize the function of turning on or off torch.
Create a
switchTorchmethod inCustomScanActivity.private void switchTorch() { boolean torchOn = mpScanner.switchTorch(); mTorchBtn.setSelected(torchOn); }In the
onCreatemethod inCustomScanActivity, add the click event ofmTorchBtnand call theswitchTorchmethod.mTorchBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { switchTorch(); } });
In CustomScanActivity, create a
notifyScanResultmethod, and call thenotifyScanResultmethod in theonBackPressedmethod.private void notifyScanResult(boolean isProcessed, Intent resultData) { ScanHelper.getInstance().notifyScanResult(isProcessed, resultData); } @Override public void onBackPressed() { super.onBackPressed(); notifyScanResult(false, null); }In the
onCreatemethod inCustomScanActivity, add the click event ofbackand call theonBackPressedmethod.findViewById(R.id.back).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } });Create an
initMPScannermethod inCustomScanActivity, and use thesetRecognizeTypemethod in thempScannerobject to set the type of the identification code.private void initMPScanner() { mpScanner = new MPScanner(this); mpScanner.setRecognizeType( MPRecognizeType.QR_CODE, MPRecognizeType.BAR_CODE, MPRecognizeType.DM_CODE, MPRecognizeType.PDF417_CODE ); }Create an
onScanSuccessmethod inCustomScanActivityand implement the following code:private void onScanSuccess(final MPScanResult result) { runOnUiThread(new Runnable() { @Override public void run() { if (result == null) { notifyScanResult(true, null); } else { Intent intent = new Intent(); intent.setData(Uri.parse(result.getText())); notifyScanResult(true, intent); } CustomScanActivity.this.finish(); } }); }Create an
initScanRectmethod inCustomScanActivityto initialize the scan feature.Call the
getCameramethod in thempScannerobject to get theCameraobject. Call thesetScanRegionmethod in thempScannerobject to set scan area.private void initScanRect() { if (scanRect == null) { scanRect = mScanView.getScanRect( mpScanner.getCamera(), mTextureView.getWidth(), mTextureView.getHeight()); float cropWidth = mScanView.getCropWidth(); LoggerFactory.getTraceLogger().debug(TAG, "cropWidth: " + cropWidth); if (cropWidth > 0) { // Maximum preview window width = Screen width/Crop box width WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); float screenWith = wm.getDefaultDisplay().getWidth(); float screenHeight = wm.getDefaultDisplay().getHeight(); float previewScale = screenWith / cropWidth; if (previewScale < 1.0f) { previewScale = 1.0f; } if (previewScale > 1.5f) { previewScale = 1.5f; } LoggerFactory.getTraceLogger().debug(TAG, "previewScale: " + previewScale); Matrix transform = new Matrix(); transform.setScale(previewScale, previewScale, screenWith / 2, screenHeight / 2); mTextureView.setTransform(transform); } } mpScanner.setScanRegion(scanRect); }In the
initMPScannermethod, call thesetMPScanListenermethod in thempScannerobject to scan the listener.mpScanner.setMPScanListener(new MPScanListener() { @Override public void onConfiguration() { mpScanner.setDisplayView(mTextureView); } @Override public void onStart() { if (!isPaused) { runOnUiThread(new Runnable() { @Override public void run() { if (!isFinishing()) { initScanRect(); mScanView.onStartScan(); } } }); } } @Override public void onSuccess(MPScanResult mpScanResult) { mpScanner.beep(); onScanSuccess(mpScanResult); } @Override public void onError(MPScanError mpScanError) { if (!isPaused) { runOnUiThread(new Runnable() { @Override public void run() { Utils.toast(CustomScanActivity.this, getString(R.string.camera_open_error)); } }); } } });In the
initMPScannermethod, call thesetMPImageGrayListenermethod in thempScannerobject to listen to the gray value of a recognized image.mpScanner.setMPImageGrayListener(new MPImageGrayListener() { @Override public void onGetImageGray(int gray) { // Note: This callback may be executed multiple consecutive times in dark environments. if (gray < MPImageGrayListener.LOW_IMAGE_GRAY) { runOnUiThread(new Runnable() { @Override public void run() { Utils.toast(CustomScanActivity.this, "The light is too dark, please turn on the flashlight"); } }); } } }); }
In
CustomScanActivity, create astartScanmethod to enable the scan feature of the camera, and create astopScanmethod to disable the scan feature of the camera.private void startScan() { try { mpScanner.openCameraAndStartScan(); isScanning = true; } catch (Exception e) { isScanning = false; LoggerFactory.getTraceLogger().error(TAG, "startScan: Exception " + e.getMessage()); } } private void stopScan() { mpScanner.closeCameraAndStopScan(); mScanView.onStopScan(); isScanning = false; if (isFirstStart) { isFirstStart = false; } }In
CustomScanActivity, createonPermissionGranted,checkCameraPermissionandscanFromUrimethods.private void onPermissionGranted() { isPermissionGranted = true; startScan(); } private void checkCameraPermission() { if (PermissionChecker.checkSelfPermission( this, Manifest.permission.CAMERA) != PermissionChecker.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_PERMISSION); } else { onPermissionGranted(); } } private void scanFromUri(Uri uri) { final Bitmap bitmap = Utils.uri2Bitmap(this, uri); if (bitmap == null) { notifyScanResult(true, null); finish(); } else { new Thread(new Runnable() { @Override public void run() { MPScanResult mpScanResult = mpScanner.scanFromBitmap(bitmap); mpScanner.beep(); onScanSuccess(mpScanResult); } }, "scanFromUri").start(); } }Call the
checkCameraPermissionmethod in theonCreatemethod inCustomScanActivityto check camera permissions.checkCameraPermission();Separately add the following content to the
onPause,onResume,onDestroy,onRequestPermissionsResult, andonActivityResultmethods inCustomScanActivity:@Override public void onPause() { super.onPause(); isPaused = true; if (isScanning) { stopScan(); } } @Override public void onResume() { super.onResume(); isPaused = false; if (!isFirstStart && isPermissionGranted) { startScan(); } } @Override public void onDestroy() { super.onDestroy(); mpScanner.release(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE_PERMISSION) { int length = Math.min(permissions.length, grantResults.length); for (int i = 0; i < length; i++) { if (TextUtils.equals(permissions[i], Manifest.permission.CAMERA)) { if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { Utils.toast(this, getString(R.string.camera_no_permission)); } else { onPermissionGranted(); } break; } } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data == null) { return; } if (requestCode == REQUEST_CODE_PHOTO) { scanFromUri(data.getData()); } } }In the
AndroidManifest.xmlfile in thecustommodule, setCustomScanActivityas the main entry ofcustom.<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mpaas.aar.demo.custom"> <activity android:name=".CustomScanActivity" android:configChanges="orientation|keyboardHidden|navigation" android:exported="false" android:launchMode="singleTask" android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar" android:windowSoftInputMode="adjustResize|stateHidden" /> </application> </manifest>
Call the scan feature in your custom UI in the main project
In the
activity_main.xmlfile, add a button and set the ID of the button tocustom_ui_btn.<Button android:id="@+id/custom_ui_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="208dp" android:background="#108EE9" android:gravity="center" android:text="Use Scan in the Custom UI" android:textColor="#ffffff" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />Edit code in the
MainActivityclass. Add a click event to thecustom_ui_btnbutton. Obtain the custom UI and then use the scan feature in the custom UI. The code is as follows:findViewById(R.id.custom_ui_btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ScanHelper.getInstance().scan(MainActivity.this, new ScanHelper.ScanCallback() { @Override public void onScanResult(boolean isProcessed, Intent result) { if (!isProcessed) { // In the scan page, click the physical back button or the back button in the upper left corner. return; } if (result == null || result.getData() == null) { Toast.makeText(MainActivity.this, "Scan failed, try again.", Toast.LENGTH_SHORT).show(); return; } new AlertDialog.Builder(MainActivity.this) .setMessage(result.getData().toString()) .setPositiveButton(R.string.confirm, null) .create() .show(); } }); } });After you compile and run the project, click Use Scan in the Custom UI to use the scan feature in the custom UI.

Scan the QR code below, then the information about the QR code will be displayed.