Android二維碼(仿微信谬运,輕量Zxing)

前言#

要做一個(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)圖#

模仿微信的效果,真機(jī)上效果更好棚点,錄屏的將就著看

默認(rèn)效果

集成方式#

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)呢胎署,歡迎討論~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吆录,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子硝拧,更是在濱河造成了極大的恐慌,老刑警劉巖葛假,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件障陶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡聊训,警方通過查閱死者的電腦和手機(jī)抱究,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來带斑,“玉大人鼓寺,你說我怎么就攤上這事⊙模” “怎么了妈候?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挂滓。 經(jīng)常有香客問我苦银,道長,這世上最難降的妖魔是什么赶站? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任幔虏,我火速辦了婚禮,結(jié)果婚禮上贝椿,老公的妹妹穿的比我還像新娘想括。我一直安慰自己,他們只是感情好烙博,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布瑟蜈。 她就那樣靜靜地躺著烟逊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪踪栋。 梳的紋絲不亂的頭發(fā)上焙格,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音夷都,去河邊找鬼眷唉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛囤官,可吹牛的內(nèi)容都是我干的冬阳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼党饮,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼肝陪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起刑顺,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤氯窍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蹲堂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狼讨,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年柒竞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了政供。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡朽基,死狀恐怖布隔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情稼虎,我是刑警寧澤衅檀,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站霎俩,受9級(jí)特大地震影響术吝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茸苇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一排苍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧学密,春花似錦淘衙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毯侦。三九已至,卻和暖如春具垫,著一層夾襖步出監(jiān)牢的瞬間侈离,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工筝蚕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卦碾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓起宽,卻偏偏與公主長得像洲胖,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坯沪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評(píng)論 25 707
  • Swift版本點(diǎn)擊這里歡迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh閱讀 25,289評(píng)論 7 249
  • 1.\d躯嫉,\w,\s,a-zA-Z0-9,\b,.,*,+,?,x{3},^$分別是什么? \d:查找數(shù)字 \w:...
    饑人谷_徐小坤閱讀 367評(píng)論 0 0
  • 進(jìn)入深秋妹蔽,鼻炎患病率明顯增多浇借,其中以季節(jié)過敏性鼻炎為多歹嘹。主要原因是冷空氣刺激加上秋冬季節(jié)空氣濕度相對(duì)小,浮塵多扎阶,各...
    ded5c8b65735閱讀 292評(píng)論 0 0
  • 老婆沒有你的日子真的好難過好傷心,一次又一次的為你流下眼淚,老婆你知道嗎外面在下雨可以撐傘可是心如果在下雨又應(yīng)該怎...
    登者閱讀 224評(píng)論 0 0