本文将引导您绘制自定义 UI 界面并将自定义 UI 扫码的能力添加到工程中。
该过程主要分为以下四个步骤:
操作步骤
创建依赖工程
- 点击 File > New > New Module。
- 选择 Android Library,点击 Next。
- 输入 Moudle name,点击 Finish。
在依赖工程中创建定义 UI 界面
-
在 custom 的
com.example.custom
包中创建 widget 包。在 widget 包中添加APSurfaceTexture
类,让其继承SurfaceTexture
类,以获取图像流。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();
}
}
-
在 custom 的 widget 包中添加
APTextureView
类,让其继承TextureView
类,实现图像流的显示。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());
}
}
}
-
在
com.example.custom
包中创建 Utils 类,实现图片的转换。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);
// 非透明区域
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;
}
}
-
右键点击 custom > New > Directory。
- 在弹出框中输入 res,创建 res 文件夹。
- 右键点击 custom 的 res > New > Directory。
- 在弹出框中输入 values,创建 values 文件夹。
- 右键点击 custom 的 values > New > File。
- 在弹出框中输入 attrs.xml,创建 attrs.xml 文件。
- 在 attrs.xml 文件中添加如下代码。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="scan">
<attr name="shadowColor" format="color" />
</declare-styleable>
</resources>
- 右键点击 custom 的 res > New > Directory。
- 在弹出框中输入 drawable,创建 drawable 文件夹。
- 向 drawable 文件夹中粘贴如下 资源文件。
-
在 custom 的 widget 包中添加
FinderView
类,让其继承 View 类,并添加如下代码。实现扫码窗口、边角及周边阴影的绘制功能。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();
}
}
//初始化扫码窗口边角样式
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);
}
//绘制扫码窗口边角样式
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);
}
//绘制扫码周边阴影
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);
}
/**
* 根据 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;
}
//设置扫码窗口边角颜色
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);
}
}
-
在 custom 的 widget 包中添加 RayView 类,让其继承 ImageView 类,并添加如下代码。实现扫描射线的绘制功能。
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);
// 设置 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;
}
}
-
右键点击 custom 的 res > New > Directory。
- 在弹出框中输入 layout,创建 layout 文件夹。
- 右键点击 custom 的 layout > New > File。
- 在弹出框中输入
view_scan.xml
,创建view_scan.xml
文件。 -
在
view_scan.xml
文件中添加如下代码,绘制扫描页面的布局界面。<?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>
-
在 widget 包中添加
ScanView
类,让其继承RelativeLayout
类,并添加如下代码。实现扫码相关的 View 与扫码引擎的交互功能。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;
// 裁剪框大小 = 网格动画框大小*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;
}
}
- 右键点击 custom 的 layout > New > File。
- 在弹出框中输入
activity_custom_scan.xml
,创建activity_custom_scan.xml
文件。 -
在
activity_custom_scan.xml
文件中添加如下代码。绘制自定义扫码功能的主界面。<?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>
在依赖工程中使用扫码功能
-
在 custom 的
com.example.custom
包中添加ScanHelper
类,并添加如下代码。调用扫码功能以及获取扫码结果的回调结果。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);
}
}
-
在 custom 的
com.example.custom
包中添加CustomScanActivity
类,让其继承 Activity 类。设置界面沉浸模式并创建资源文件对应的 View 和 Button。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);
// 设置沉浸模式
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);
}
}
- 在 CustomScanActivity 中创建 pickImageFromGallery 方法,实现打开手机相册的功能。
private void pickImageFromGallery() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_CODE_PHOTO);
}
- 在 CustomScanActivity 的 onCreate 方法中添加 _ _gallery 的点击事件,并调用 pickImageFromGallery 方法。
findViewById(R.id.gallery).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pickImageFromGallery();
}
});
- 在 switchTorch 方法中实现切换手电开关的功能。
private void switchTorch() {
boolean torchOn = mpScanner.switchTorch();
mTorchBtn.setSelected(torchOn);
}
- 在 CustomScanActivity 的 onCreate 方法中添加 mTorchBtn 的点击事件,并调用 switchTorch 方法。
mTorchBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchTorch();
}
});
- 在 CustomScanActivity 中创建 notifyScanResult 方法。
private void notifyScanResult(boolean isProcessed, Intent resultData) {
ScanHelper.getInstance().notifyScanResult(isProcessed, resultData);
}
- 在 onBackPressed 中调用 notifyScanResult 方法。
@Override
public void onBackPressed() {
super.onBackPressed();
notifyScanResult(false, null);
}
- 在 CustomScanActivity 的 onCreate 方法中添加 _ _back 的点击事件,并调用 onBackPressed 方法。“`java
findViewById(R.id.back).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
“
- 在 CustomScanActivity 中创建 initMPScanner 方法,并使用 mpScanner 对象的 setRecognizeType 方法设置识别码的类型。
private void initMPScanner() {
mpScanner = new MPScanner(this);
mpScanner.setRecognizeType(
MPRecognizeType.QR_CODE,
MPRecognizeType.BAR_CODE,
MPRecognizeType.DM_CODE,
MPRecognizeType.PDF417_CODE
);
}
- 在 CustomScanActivity 中创建 onScanSuccess 方法,并实现如下代码。
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();
}
});
}
-
在 CustomScanActivity 中创建 initScanRect 方法,初始化扫描功能。调用 mpScanner 对象的 getCamera 方法获取 Camera 对象 并调用 mpScanner 对象的 setScanRegion 方法设置扫描区域。
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) {
// 预览放大 = 屏幕宽 / 裁剪框宽
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);
}
-
在 initMPScanner 方法中使用 mpScanner 对象的 setMPScanListener 方法实现扫描监听器的功能。
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));
}
});
}
}
});
- 在 initMPScanner 方法中使用 mpScanner 对象的 setMPImageGrayListener 方法实现识别图像灰度值的监听功能。
mpScanner.setMPImageGrayListener(new MPImageGrayListener() {
@Override
public void onGetImageGray(int gray) {
// 注意:该回调在昏暗环境下可能会连续多次执行
if (gray < MPImageGrayListener.LOW_IMAGE_GRAY) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Utils.toast(CustomScanActivity.this, "光线太暗,请打开手电筒");
}
});
}
}
});
}
-
在 CustomScanActivity 中分别创建 startScan 和 stopScan 方法,实现开启和关闭相机扫码权限。
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;
}
}
- 在 CustomScanActivity 中创建 onPermissionGranted 方法。
private void onPermissionGranted() {
isPermissionGranted = true;
startScan();
}
- 在 CustomScanActivity 中创建 checkCameraPermission 方法。
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();
}
}
- 在 CustomScanActivity 中创建 scanFromUri 方法。
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();
}
}
- 在 CustomScanActivity 的 onCreate 方法中调用 checkCameraPermission 方法检查相机权限。
checkCameraPermission();
-
在 CustomScanActivity 的 onPause、onResume、onDestroy、onRequestPermissionsResult 和 onActivityResult 方法中分别添加如下内容。
@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());
}
}
}
-
在 custom 的 AndroidManifest.xml 文件中设置 CustomScanActivity 为 custom 的主入口。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mpaas.aar.demo.custom">
<application>
<meta-data
android:name="com.google.android.actions"
android:resource="@xml/actions" />
<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>
在主工程中调用自定义 UI 下的扫码功能
- 在
activity_main.xml
文件中,添加 Button,并设置 Button 的 id 为custom_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="自定义 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" />
-
在
MainActivity
类中编写代码。添加custom_ui_btn
按钮的点击事件。获取自定义 UI 界面,并使用自定义 UI 的扫码功能。代码如下所示: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) {
// 扫码界面点击物理返回键或左上角返回键
return;
}
if (result == null || result.getData() == null) {
Toast.makeText(MainActivity.this, "扫码失败,请重试!", Toast.LENGTH_SHORT).show();
return;
}
new AlertDialog.Builder(MainActivity.this)
.setMessage(result.getData().toString())
.setPositiveButton(R.string.confirm, null)
.create()
.show();
}
});
}
});
-
编译运行工程后,界面如下:
- 点击 自定义 UI 下使用扫一扫 后即可使用自定义 UI 下的扫码功能。
- 扫描如下二维码。
- 会弹出该二维码的信息。