前言#
要做一個(gè)功能,二維碼識(shí)別垦藏。網(wǎng)上找一堆相關(guān)的Demo梆暖,但是總不是想要的效果,或者都是多年前的版本掂骏,權(quán)衡考慮之后轰驳,決定親自操刀。不糾結(jié)直接選中Zxing框架弟灼,https://github.com/zxing/zxing 在網(wǎng)站上直接clone下來级解,運(yùn)行,然后就發(fā)現(xiàn)問題了...
選Zxing存在的問題#
- 為什么是橫屏蠕趁,調(diào)成豎屏,居然有問題
- 這個(gè)包居然有好多用不著的代碼
- 默認(rèn)識(shí)別的界面不是想要的效果
- 加個(gè)Title在頂部之后識(shí)別框居然不居中
發(fā)現(xiàn)問題辛馆,那么本文的優(yōu)點(diǎn)就來了俺陋,且聽一一道來#
- 集成速度快豁延,相關(guān)核心功能都已經(jīng)再次封裝好
- 最新的V3.30的工程,識(shí)別速度快腊状,基本見圖秒識(shí)別
- 解決橫豎屏的問題诱咏,通過設(shè)置Activity的android:screenOrientation="portrait" 方式設(shè)置,就可以自適應(yīng)橫屏豎屏
- 去掉工程中無用的代碼缴挖,留下最核心的代碼袋狞,實(shí)現(xiàn)最最最輕量級(jí)
- 自定義AutoScannerView控件,實(shí)現(xiàn)微信識(shí)別區(qū)域的效果
- 解決工程之間只以屏幕為居中的問題映屋,目前可以根據(jù)設(shè)置寬度高度自適應(yīng)居中
- 具體示例參考:https://github.com/yangxixi88/ZxingLite
光說不練假把式苟鸯,上動(dòng)圖#
集成方式#
1早处、導(dǎo)入zxinglite工程,Andorid Studio通過Import Module方式導(dǎo)入
2瘫析、導(dǎo)入目標(biāo)工程之后砌梆,如果有存在R等資源文件未找到,可以在菜單欄Build->Make Module zxinglite 即可
3贬循、添加兩個(gè)權(quán)限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
4咸包、集成BaseCaptureActivity,實(shí)現(xiàn)getSurfaceView()和dealDecode()等方法
5杖虾、布局樣式烂瘫,仿微信效果則用AutoScannerView,默認(rèn)效果使用com.google.zxing.client.android.ViewfinderView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_wechat_capture"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="yangxixi.zxinglib.WeChatCaptureActivity">
<SurfaceView
android:id="@+id/preview_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<com.google.zxing.client.android.AutoScannerView
android:id="@+id/autoscanner_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
6奇适、仿微信效果的Activity
/**
* 模仿微信的掃描界面
*/
public class WeChatCaptureActivity extends BaseCaptureActivity {
private static final String TAG = WeChatCaptureActivity.class.getSimpleName();
private SurfaceView surfaceView;
private AutoScannerView autoScannerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wechat_capture);
surfaceView = (SurfaceView) findViewById(R.id.preview_view);
autoScannerView = (AutoScannerView) findViewById(R.id.autoscanner_view);
}
@Override
protected void onResume() {
super.onResume();
autoScannerView.setCameraManager(cameraManager);
}
@Override
public SurfaceView getSurfaceView() {
return (surfaceView == null) ? (SurfaceView) findViewById(R.id.preview_view) : surfaceView;
}
@Override
public void dealDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
Log.i(TAG, "dealDecode ~~~~~ " + rawResult.getText() + " " + barcode + " " + scaleFactor);
playBeepSoundAndVibrate(true, false);
Toast.makeText(this, rawResult.getText(), Toast.LENGTH_LONG).show();
// 對(duì)此次掃描結(jié)果不滿意可以調(diào)用
// reScan();
}
}
默認(rèn)效果Activity
/**
* 默認(rèn)的掃描界面
*/
public class DefaultCaptureActivity extends BaseCaptureActivity {
private static final String TAG = DefaultCaptureActivity.class.getSimpleName();
private SurfaceView surfaceView;
private ViewfinderView viewfinderView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_capture);
surfaceView = (SurfaceView) findViewById(R.id.preview_view);
viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
}
@Override
public SurfaceView getSurfaceView() {
return (surfaceView == null) ? (SurfaceView) findViewById(R.id.preview_view) : surfaceView;
}
@Override
public ViewfinderView getViewfinderHolder() {
return (viewfinderView == null) ? (ViewfinderView) findViewById(R.id.viewfinder_view) : viewfinderView;
}
@Override
public void dealDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
Log.i(TAG, "dealDecode ~~~~~ " + rawResult.getText() + " " + barcode + " " + scaleFactor);
playBeepSoundAndVibrate();
Toast.makeText(this, rawResult.getText(), Toast.LENGTH_LONG).show();
// 對(duì)此次掃描結(jié)果不滿意可以調(diào)用
// reScan();
}
}
自定義AutoScannerView的實(shí)現(xiàn)
/**
* Created by yangxixi on 16/11/22.
*
* 自動(dòng)上下掃描
*/
public class AutoScannerView extends View {
private static final String TAG = AutoScannerView.class.getSimpleName();
private Paint maskPaint;
private Paint linePaint;
private Paint traAnglePaint;
private Paint textPaint;
private CameraManager cameraManager;
private final int maskColor = Color.parseColor("#60000000"); //蒙在攝像頭上面區(qū)域的半透明顏色
private final int triAngleColor = Color.parseColor("#76EE00"); //邊角的顏色
private final int lineColor = Color.parseColor("#FF0000"); //中間線的顏色
private final int textColor = Color.parseColor("#CCCCCC"); //文字的顏色
private final int triAngleLength = dp2px(20); //每個(gè)角的點(diǎn)距離
private final int triAngleWidth = dp2px(4); //每個(gè)角的點(diǎn)寬度
private final int textMarinTop = dp2px(30); //文字距離識(shí)別框的距離
private int lineOffsetCount = 0;
public AutoScannerView(Context context, AttributeSet attrs) {
super(context, attrs);
maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
maskPaint.setColor(maskColor);
traAnglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
traAnglePaint.setColor(triAngleColor);
traAnglePaint.setStrokeWidth(triAngleWidth);
traAnglePaint.setStyle(Paint.Style.STROKE);
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(lineColor);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(textColor);
textPaint.setTextSize(dp2px(14));
}
public void setCameraManager(CameraManager cameraManager) {
this.cameraManager = cameraManager;
invalidate();//重新進(jìn)入可能不刷新忱反,所以調(diào)用一次。
}
@Override
protected void onDraw(Canvas canvas) {
if (cameraManager == null)
return;
Rect frame = cameraManager.getFramingRect();
Rect previewFrame = cameraManager.getFramingRectInPreview();
if (frame == null || previewFrame == null) {
return;
}
int width = canvas.getWidth();
int height = canvas.getHeight();
// 除了中間的識(shí)別區(qū)域滤愕,其他區(qū)域都將蒙上一層半透明的圖層
canvas.drawRect(0, 0, width, frame.top, maskPaint);
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, maskPaint);
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, maskPaint);
canvas.drawRect(0, frame.bottom + 1, width, height, maskPaint);
String text = "將二維碼放入框內(nèi)温算,即可自動(dòng)掃描";
canvas.drawText(text, (width - textPaint.measureText(text)) / 2, frame.bottom + textMarinTop, textPaint);
// 四個(gè)角落的三角
Path leftTopPath = new Path();
leftTopPath.moveTo(frame.left + triAngleLength, frame.top + triAngleWidth / 2);
leftTopPath.lineTo(frame.left + triAngleWidth / 2, frame.top + triAngleWidth / 2);
leftTopPath.lineTo(frame.left + triAngleWidth / 2, frame.top + triAngleLength);
canvas.drawPath(leftTopPath, traAnglePaint);
Path rightTopPath = new Path();
rightTopPath.moveTo(frame.right - triAngleLength, frame.top + triAngleWidth / 2);
rightTopPath.lineTo(frame.right - triAngleWidth / 2, frame.top + triAngleWidth / 2);
rightTopPath.lineTo(frame.right - triAngleWidth / 2, frame.top + triAngleLength);
canvas.drawPath(rightTopPath, traAnglePaint);
Path leftBottomPath = new Path();
leftBottomPath.moveTo(frame.left + triAngleWidth / 2, frame.bottom - triAngleLength);
leftBottomPath.lineTo(frame.left + triAngleWidth / 2, frame.bottom - triAngleWidth / 2);
leftBottomPath.lineTo(frame.left + triAngleLength, frame.bottom - triAngleWidth / 2);
canvas.drawPath(leftBottomPath, traAnglePaint);
Path rightBottomPath = new Path();
rightBottomPath.moveTo(frame.right - triAngleLength, frame.bottom - triAngleWidth / 2);
rightBottomPath.lineTo(frame.right - triAngleWidth / 2, frame.bottom - triAngleWidth / 2);
rightBottomPath.lineTo(frame.right - triAngleWidth / 2, frame.bottom - triAngleLength);
canvas.drawPath(rightBottomPath, traAnglePaint);
//循環(huán)劃線,從上到下
if (lineOffsetCount > frame.bottom - frame.top - dp2px(10)) {
lineOffsetCount = 0;
} else {
lineOffsetCount = lineOffsetCount + 6;
// canvas.drawLine(frame.left, frame.top + lineOffsetCount, frame.right, frame.top + lineOffsetCount, linePaint); //畫一條紅色的線
Rect lineRect = new Rect();
lineRect.left = frame.left;
lineRect.top = frame.top + lineOffsetCount;
lineRect.right = frame.right;
lineRect.bottom = frame.top + dp2px(10) + lineOffsetCount;
canvas.drawBitmap(((BitmapDrawable)(getResources().getDrawable(R.drawable.scanline))).getBitmap(), null, lineRect, linePaint);
}
postInvalidateDelayed(10L, frame.left, frame.top, frame.right, frame.bottom);
}
private int dp2px(int dp) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5f);
}
}
集成之后间影,基本就可以注竿,如果需要設(shè)置橫屏豎屏直接設(shè)置screenOrientation就好了,里面的效果適配效果都已經(jīng)實(shí)現(xiàn)魂贬;直接在dealDecode中處理掃描之后的結(jié)果巩割,playBeepSoundAndVibrate可以設(shè)置響鈴,振動(dòng)或者同時(shí)都可以付燥。想設(shè)置微信識(shí)別框的參數(shù)可以再AutoScannerView中修改宣谈,這里就不精細(xì)去實(shí)現(xiàn)噠
結(jié)語#
到這里,大致流程就介紹完了键科。有什么不足的闻丑,歡迎指出漩怎。
歡迎在下方評(píng)論和喜歡,謝謝嗦嗡,轉(zhuǎn)載請(qǐng)說明出處勋锤。
具體示例,請(qǐng)?zhí)D(zhuǎn)Github查看侥祭,地址:https://github.com/yangxixi88/ZxingLite 歡迎StarStarStar啊~~~
讀完思考#
自定義AutoScannerView是勻速從上到下叁执,而細(xì)細(xì)觀察微信的效果,是兩頭慢矮冬,中間快谈宛,如果所以實(shí)現(xiàn)微信那種效果,需要從哪些方面改進(jìn)呢胎署,歡迎討論~~