欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android扫一扫 有仿微信版

程序员文章站 2022-03-28 16:15:06
第三方zxing的GitHub地址用法:国际惯例,先上图:Step 1 :添加依赖 //第三方zxing implementation 'com.journeyapps:zxing-android-embedded:3.6.0'Step 2 :添加权限 Step 3 :activity_main.xml布局 添加 测试用的两个控件

第三方Zxing的GitHub地址:

tip:前面几种扫码是为了更好的理解,若想看微信版本的扫码,请直接下划到第三种扫码。

用法:

国际惯例,先上图:
Android扫一扫  有仿微信版

Step 1 :添加依赖

    //第三方zxing
    implementation 'com.journeyapps:zxing-android-embedded:3.6.0'

Step 2 :添加权限

  <uses-permission android:name="android.permission.CAMERA"/>

Step 3 :activity_main.xml布局 添加 测试用的两个控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始"
        android:id="@+id/button"/>

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

Step 4 :MainActivity 代码:

public class MainActivity extends AppCompatActivity {

    private Button button;
    private ImageView ivImage;

    //  Step 1 : 初始化 获取控件 设置监听
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //获取测试的控件
        button = findViewById(R.id.button);//点击跳转到扫码活动
        ivImage = findViewById(R.id.iv_image);//输出二维码图片

        //控件监听
        listenerView();
    }


    private void listenerView() {

        //  Step 2 :跳转到扫描活动
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //=======设置扫描活动  可根据需求设置以下内容
                IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this);

                //  1.扫描成功后的提示音,默认关闭
                intentIntegrator.setBeepEnabled(true);

                //  2.启动后置摄像头扫描,若为 1 为前置摄像头,默认后置
                intentIntegrator.setCameraId(0);

                /*  3.设置扫描的条码的格式:默认为所有类型
                 *   IntentIntegrator.PRODUCT_CODE_TYPES:商品码类型
                 *   IntentIntegrator.ONE_D_CODE_TYPES:一维码类型
                 *   IntentIntegrator.QR_CODE:二维码
                 *   IntentIntegrator.DATA_MATRIX:数据矩阵类型
                 *   IntentIntegrator.ALL_CODE_TYPES:所类有型
                 * */
                intentIntegrator.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES);

                /*  4.方向锁:true为锁定,false反之,默认锁定.
                ps:在AndroidManifest.xml里设置以下属性,则扫码界面完全依赖传感器(tools红色提示,指向它会提示,点击左边蓝色Create...即可)
                <activity
                    android:name="com.journeyapps.barcodescanner.CaptureActivity"
                    android:screenOrientation="fullSensor"
                    tools:replace="screenOrientation" />
                * */
                intentIntegrator.setOrientationLocked(true);

                //  5.设置扫描界面的提示信息:默认为:请将条码置于取景框内扫描。(ps:设置没提示文字:setPrompt(""))
                intentIntegrator.setPrompt("请选择二维码");

                //  6.设置关闭扫描的时间(单位:毫秒),不设置不关闭
                intentIntegrator.setTimeout(60000);

                //  7.保存二维码图片:在onActivityResult方法里可获取保存的路径,根据需要来是否需要保存
                intentIntegrator.setBarcodeImageEnabled(true);

                //启动扫描
                intentIntegrator.initiateScan();

            }
        });

    }


    //  Step 3 :处理扫码后返回的结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);

        if(result!=null){

            //==是否扫到内容
            if (result.getContents()!=null){
                Toast.makeText(this,"扫描结果:"+result.getContents(),Toast.LENGTH_LONG).show();
            }else{
                Toast.makeText(this,"取消扫码",Toast.LENGTH_LONG).show();
            }

            //==是否有保存照片的路径  在intentIntegrator已设置保存照片
            if(result.getBarcodeImagePath()!=null){
                
                FileInputStream file=null;
                try {
                    file=new FileInputStream(new File(result.getBarcodeImagePath()));
                    ivImage.setImageBitmap(BitmapFactory.decodeStream(file));//显示获取的照片
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        file.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
            
            /*  获取条码种类:在intentIntegrator.setDesiredBarcodeFormats那设置扫码格式后(点击格式可进入查看该格式有多少个类型)

                例如:PRODUCT_CODE_TYPES:商品码类型,它就有 UPC_A, UPC_E, EAN_8, EAN_13, RSS_14 种类
                public static final Collection<String> PRODUCT_CODE_TYPES = list(UPC_A, UPC_E, EAN_8, EAN_13, RSS_14);

                根据getFormatName获取到的种类,就知道是哪个扫码格式,进而根据需求进行相关操作
             */
            if (result.getFormatName()!=null){
                Toast.makeText(this,"图片格式:"+result.getFormatName(),Toast.LENGTH_LONG).show();
            }


        }else{
            super.onActivityResult(requestCode, resultCode, data);
        }

    }
    
    //=========PS:Android6.0以后的版本还需动态申请权限,若需动态申请,请在MainActivity中添加申请权限(调用此方法)再做打开相机扫描
    //暂时未发现需要动态申请(本人手机安卓10),可能不同手机厂商做法
    private void requsetPermission(){
        if (Build.VERSION.SDK_INT>22){
            if (ContextCompat.checkSelfPermission(MainActivity.this,
                    android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
                //先判断有没有权限 ,没有就在这里进行权限的申请
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{android.Manifest.permission.CAMERA},1);

            }else {

            }
        }else {

        }
    }

    //============PS:申请权限后的方法
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    //已获取权限,写需要做的代码

                }else {
                    //拒绝摄像头权限,可以提示用户给权限
                    Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }

    }
}

拓展:自定义扫描界面

第一种:带闪光灯

国际惯例,先上图:图中白点为闪光灯按钮
Android扫一扫  有仿微信版

Step 1 :引入依赖:

    //第三方zxing
    implementation 'com.journeyapps:zxing-android-embedded:3.6.0'

Step 2 :申请权限:

    <!--相机-->
    <uses-permission android:name="android.permission.CAMERA"/>
    <!--若需要闪光灯权限 ,请加入此权限(自测不需要)-->
<!--
    <uses-permission android:name="android.permission.FLASHLIGHT" />
-->

Step 3 :准备3个布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始"
        android:id="@+id/button"/>

</LinearLayout>

content_scan.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--
    layout_width、layout_height:启动扫描界面的布局参数

    zxing_framing_rect_width、zxing_framing_rect_height:
    在扫描界面中,只能扫描二维码的宽高,去掉后会有默认的宽高
    -->
    <com.journeyapps.barcodescanner.BarcodeView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/zxing_barcode_surface"
        app:zxing_framing_rect_width="250dp"
        app:zxing_framing_rect_height="250dp"/>

    <com.journeyapps.barcodescanner.ViewfinderView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/zxing_viewfinder_view"
        app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
        app:zxing_result_view="@color/zxing_custom_result_view"
        app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
        app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>

</merge>

activity_scan.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--装扫描界面的控件
    @layout/content_scan:为嵌入content_scan.xml的布局
    -->
    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/dbv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        app:zxing_scanner_layout="@layout/content_scan">
    </com.journeyapps.barcodescanner.DecoratedBarcodeView>

    <!--闪光灯图片 自行找图片样式
        @drawable/ic_flashlight_close 关闭时的图片
    -->
    <ImageButton
        android:id="@+id/ib_flashlight_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="60dp"
        android:background="@drawable/ic_flashlight_close"/>
</RelativeLayout>

Step 4 :ScanActivity.java

public class ScanActivity extends AppCompatActivity {
    private CaptureManager capture;
    private ImageButton ibFlashlight;
    private DecoratedBarcodeView barcodeScannerView;
    private boolean bTorch = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //==设置布局、获取控件
        setContentView(R.layout.activity_scan);
        barcodeScannerView = findViewById(R.id.dbv);
        ibFlashlight= findViewById(R.id.ib_flashlight_close);

        //==监听: 根据barcodeScannerView设置闪光灯ibFlashlight状态
        barcodeScannerView.setTorchListener(new DecoratedBarcodeView.TorchListener() {
            @Override
            public void onTorchOn() {//开灯

                //R.drawable.ic_flashlight_open)  开灯显示的图片 自行找图片样式
                ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_open));
                bTorch = true;
            }

            @Override
            public void onTorchOff() {//关灯

                //R.drawable.ic_flashlight_close)  关灯显示的图片 自行找图片样式
                ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_close));
                bTorch = false;
            }
        });

        //==开或关灯
        ibFlashlight.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bTorch){
                    barcodeScannerView.setTorchOff();
                } else {
                    barcodeScannerView.setTorchOn();
                }

            }
        });

        //==初始化活动
        capture = new CaptureManager(this, barcodeScannerView);

        capture.initializeFromIntent(getIntent(), savedInstanceState);

        capture.decode();
    }


    @Override
    protected void onResume() {
        super.onResume();
        capture.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        capture.onPause();
        barcodeScannerView.setTorchOff();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        capture.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        capture.onSaveInstanceState(outState);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
        capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
    }
}

Step 5 :MainActivity 代码:

public class MainActivity extends AppCompatActivity {

    private Button button;

    //  Step 1 : 初始化 获取控件 设置监听
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //获取测试的控件
        button = findViewById(R.id.button);//点击跳转到扫码活动

        //控件监听
        listenerView();
    }


    private void listenerView() {

        //  Step 2 :跳转到扫描活动
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //=======设置扫描活动  可根据需求设置以下内容
                IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this);

                //启动自定义的扫描活动,不设置则启动默认的活动
                intentIntegrator.setCaptureActivity(ScanActivity.class);

                //启动扫描
                intentIntegrator.initiateScan();

            }
        });

    }


    //  Step 3 :处理扫码后返回的结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);

        if(result!=null){

            //==是否扫到内容
            if (result.getContents()!=null){
                Toast.makeText(this,"扫描结果:"+result.getContents(),Toast.LENGTH_LONG).show();
            }else{
                Toast.makeText(this,"取消扫码",Toast.LENGTH_LONG).show();
            }


        }else{
            super.onActivityResult(requestCode, resultCode, data);
        }

    }
    
    //=========PS:Android6.0以后的版本还需动态申请权限,若需动态申请,请在MainActivity中添加申请权限(调用此方法)再做打开相机扫描
    //暂时未发现需要动态申请(本人手机安卓10),可能不同手机厂商做法
    private void requsetPermission(){
        if (Build.VERSION.SDK_INT>22){
            if (ContextCompat.checkSelfPermission(MainActivity.this,
                    android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
                //先判断有没有权限 ,没有就在这里进行权限的申请
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{android.Manifest.permission.CAMERA},1);

            }else {

            }
        }else {

        }
    }

    //============PS:申请权限后的方法
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    //已获取权限,写需要做的代码

                }else {
                    //拒绝摄像头权限,可以提示用户给权限
                    Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }

    }
    
}

第二种:带闪光灯 并修改其扫描界面
国际惯例,先上图(ScanWidget代码里有介绍去掉四个角样式)本来是动图的
Android扫一扫  有仿微信版

tip:上面已经有介绍添加依赖和权限了,这里不多说,

并且,所用到的xml和activity和第一种的相同。

不同之处:
1.新建一个自定义的扫描活动:ScanWidget,代码如下

public class ScanWidget extends ViewfinderView {

    /* ******************************************    边角线相关属性    ************************************************/

    /**
     * "边角线长度/扫描边框长度"的占比 (比例越大,线越长)
     */
    public float mLineRate = 0.1F;

    /**
     * 边角线厚度 (建议使用dp)
     */
    public float mLineDepth =  TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());

    /**
     * 边角线颜色
     */
    public int mLineColor = Color.WHITE;

    /* *******************************************    扫描线相关属性    ************************************************/

    /**
     * 扫描线起始位置
     */
    public int mScanLinePosition = 0;

    /**
     * 扫描线厚度
     */
    public float mScanLineDepth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());

    /**
     * 扫描线每次重绘的移动距离
     */
    public float mScanLineDy = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());

    /**
     * 线性梯度
     */
    public LinearGradient mLinearGradient;

    /**
     * 线性梯度位置
     */
    public float[] mPositions = new float[]{0f, 0.5f, 1f};

    /**
     * 线性梯度各个位置对应的颜色值
     */
    public int[] mScanLineColor = new int[]{0x00FFFFFF, Color.WHITE, 0x00FFFFFF};



    // This constructor is used when the class is built from an XML resource.
    public ScanWidget(Context context, AttributeSet attrs) {
        super(context, attrs);

    }


    @Override
    public void onDraw(Canvas canvas) {
        refreshSizes();
        if (framingRect == null || previewFramingRect == null) {
            return;
        }

        final Rect frame = framingRect;
        final Rect previewFrame = previewFramingRect;

        //=====绘制4个角  可以注释此段代码,就像微信那样只要扫描线在动的样式了
        paint.setColor(mLineColor); // 定义四个角画笔的颜色(本身整个扫描界面都为此颜色,通过设置四个角距离而被覆盖,进而形成四个角)
        //左上角
        canvas.drawRect(frame.left, frame.top, frame.left + frame.width() * mLineRate, frame.top + mLineDepth, paint);
        canvas.drawRect(frame.left, frame.top, frame.left + mLineDepth, frame.top + frame.height() * mLineRate, paint);

        //右上角
        canvas.drawRect(frame.right - frame.width() * mLineRate, frame.top, frame.right, frame.top + mLineDepth, paint);
        canvas.drawRect(frame.right - mLineDepth, frame.top, frame.right, frame.top + frame.height() * mLineRate, paint);

        //左下角
        canvas.drawRect(frame.left, frame.bottom - mLineDepth, frame.left + frame.width() * mLineRate, frame.bottom, paint);
        canvas.drawRect(frame.left, frame.bottom - frame.height() * mLineRate, frame.left + mLineDepth, frame.bottom, paint);

        //右下角
        canvas.drawRect(frame.right - frame.width() * mLineRate, frame.bottom - mLineDepth, frame.right, frame.bottom, paint);
        canvas.drawRect(frame.right - mLineDepth, frame.bottom - frame.height() * mLineRate, frame.right, frame.bottom, paint);


        //=======扫描框为的颜色,灰色遮罩层,删除则无灰色遮罩层
        /*
        int width = canvas.getWidth();
        int height = canvas.getHeight();
        paint.setColor(resultBitmap != null ? resultColor : maskColor);//遮罩层的颜色
        canvas.drawRect(0, 0, width, frame.top, paint);
        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
        canvas.drawRect(0, frame.bottom + 1, width, height, paint);
         */

        if (resultBitmap != null) {
            // Draw the opaque result bitmap over the scanning rectangle
            paint.setAlpha(CURRENT_POINT_OPACITY);
            canvas.drawBitmap(resultBitmap, null, frame, paint);
        } else {

            // ===绘制扫描线
            mScanLinePosition += mScanLineDy;
            if(mScanLinePosition > frame.height()){
                mScanLinePosition = 0;
            }
            mLinearGradient = new LinearGradient(frame.left, frame.top + mScanLinePosition, frame.right, frame.top + mScanLinePosition, mScanLineColor, mPositions, Shader.TileMode.CLAMP);
            paint.setShader(mLinearGradient);
            canvas.drawRect(frame.left, frame.top + mScanLinePosition, frame.right, frame.top + mScanLinePosition + mScanLineDepth, paint);
            paint.setShader(null);



            final float scaleX = frame.width() / (float) previewFrame.width();
            final float scaleY = frame.height() / (float) previewFrame.height();

            final int frameLeft = frame.left;
            final int frameTop = frame.top;

            /*去掉扫描区域的闪光点
            if (!lastPossibleResultPoints.isEmpty()) {
                paint.setAlpha(CURRENT_POINT_OPACITY / 2);
                paint.setColor(resultPointColor);
                float radius = POINT_SIZE / 2.0f;
                for (final ResultPoint point : lastPossibleResultPoints) {
                    canvas.drawCircle(
                            frameLeft + (int) (point.getX() * scaleX),
                            frameTop + (int) (point.getY() * scaleY),
                            radius, paint
                    );
                }
                lastPossibleResultPoints.clear();
            }
            */

            // draw current possible result points
            if (!possibleResultPoints.isEmpty()) {
                paint.setAlpha(CURRENT_POINT_OPACITY);
                paint.setColor(resultPointColor);
                for (final ResultPoint point : possibleResultPoints) {
                    canvas.drawCircle(
                            frameLeft + (int) (point.getX() * scaleX),
                            frameTop + (int) (point.getY() * scaleY),
                            POINT_SIZE, paint
                    );
                }

                // swap and clear buffers
                final List<ResultPoint> temp = possibleResultPoints;
                possibleResultPoints = lastPossibleResultPoints;
                lastPossibleResultPoints = temp;
                possibleResultPoints.clear();
            }

            // Request another update at the animation interval, but only repaint the laser line,
            // not the entire viewfinder mask.
            postInvalidateDelayed(ANIMATION_DELAY,
                    frame.left - POINT_SIZE,
                    frame.top - POINT_SIZE,
                    frame.right + POINT_SIZE,
                    frame.bottom + POINT_SIZE);
        }
    }

}

2.content_scan.xml的代码为:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--
    layout_width、layout_height:启动扫描界面的布局参数

    zxing_framing_rect_width、zxing_framing_rect_height:
    在扫描界面中,只能扫描二维码的宽高,去掉后会有默认的宽高
    -->
    <com.journeyapps.barcodescanner.BarcodeView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/zxing_barcode_surface" />

    <!--使用的是自定义的扫描活动-->
    <com.gx.test.widget.ScanWidget
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/zxing_viewfinder_view" />

</merge>

第三种:改进版,仿微信扫描条
扫描条是图片,利用动画实现扫描条活动,第二种的扫描条是绘制的线,调快移动距离的话会感觉一卡一卡的,效果不好。
国际惯例,先上图:因虚拟机录制,看着扫描条一卡一卡的,实际用手机调试,是像微信的扫码条那样流畅的
Android扫一扫  有仿微信版

Step 1 :引入依赖:

    //第三方zxing
    implementation 'com.journeyapps:zxing-android-embedded:3.6.0'

Step 2 :申请权限:

    <!--相机-->
    <uses-permission android:name="android.permission.CAMERA"/>
    <!--若需要闪光灯权限 ,请加入此权限(自测不需要)-->
<!--
    <uses-permission android:name="android.permission.FLASHLIGHT" />
-->

Step 3 :自定义 MyApplication

public class MyApplication extends Application {
    
    private View view;

    public View getView() {
        return view;
    }

    public void setView(View view) {
        this.view = view;
    }
}

并在AndroidManifest.xml配置好

    <application
        android:name=".MyApplication"

Step 4 :ScanWidget 自定义扫描活动界面:

//  Step 1 :继承  ViewfinderView 并 加控制器
public class ScanWidget extends ViewfinderView {


    //边角线厚度
    public float mLineDepth =  TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());

    //边角线长度/扫描边框长度"的占比 (比例越大,线越长)
    public float mLineRate = 0.1F;


    public ScanWidget(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //  Step 2 : 重写此方法:在此方法删除原来的扫描条等等样式,并加入自己的扫描样式
    @Override
    public void onDraw(Canvas canvas) {

        refreshSizes();
        if (framingRect == null || previewFramingRect == null) {
            return;
        }

        final Rect frame = framingRect;
        final Rect previewFrame = previewFramingRect;

        final int width = canvas.getWidth();
        final int height = canvas.getHeight();


        //====================自己加入的扫描条动画在此处(扫描条其实是View控件放了个背景,view加入动画就实现了扫描条运动)↓
        //ps:若是启用下面代码(带有 PS 的注释那段,请看其作用),请在全局定义boolean b=false,然后例:
        //if(!b){这里写这段自己加入的动画代码; b=true;}  否则出现扫描条不运动,也不会因下面那PS提示的代码让这段代码反复执行。

        //=====加入扫描条
        MyApplication myApplication= (MyApplication) this.getContext().getApplicationContext();

        //设置扫描条的参数
        View view=myApplication.getView();
        FrameLayout.LayoutParams params= (FrameLayout.LayoutParams) view.getLayoutParams();
        params.width=frame.right-frame.left;//这是计算扫描框的宽度,进而设置扫描条的宽度
        params.setMargins(frame.left,0,0,0);//设置左边距,让扫描条在横方向在扫描框里
        view.setLayoutParams(params);

        //设置扫描条的动画        注意这个 70 是扫描条的高度 单位是px
        //参数 3:运动开始的地方:frame.top是扫描框离屏幕顶部的距离,减70是因为 这个扫描条 的高是 70 px,
        // 参数3的单位也是px,所以运动开始的地方就是 frame.top-70;参数4作用同3
        Animation animation = new TranslateAnimation(0, 0, frame.top-70, frame.bottom-70);
        animation.setRepeatMode(Animation.RESTART);
        animation.setRepeatCount(Animation.INFINITE);
        animation.setDuration(2000);
        view.startAnimation(animation);
        
        //清除内存
        myApplication.setView(null);

        //=====为矩形扫描区域四个角加上边框  根据情况可以去掉该段代码,像微信扫码了
        paint.setColor(Color.GREEN); // 定义四个角画笔的颜色(本身整个扫描界面都为此颜色,通过设置四个角距离而被覆盖,进而形成四个角)
        //左上角
        canvas.drawRect(frame.left, frame.top, frame.left + frame.width() * mLineRate, frame.top + mLineDepth, paint);
        canvas.drawRect(frame.left, frame.top, frame.left + mLineDepth, frame.top + frame.height() * mLineRate, paint);

        //右上角
        canvas.drawRect(frame.right - frame.width() * mLineRate, frame.top, frame.right, frame.top + mLineDepth, paint);
        canvas.drawRect(frame.right - mLineDepth, frame.top, frame.right, frame.top + frame.height() * mLineRate, paint);

        //左下角
        canvas.drawRect(frame.left, frame.bottom - mLineDepth, frame.left + frame.width() * mLineRate, frame.bottom, paint);
        canvas.drawRect(frame.left, frame.bottom - frame.height() * mLineRate, frame.left + mLineDepth, frame.bottom, paint);

        //右下角
        canvas.drawRect(frame.right - frame.width() * mLineRate, frame.bottom - mLineDepth, frame.right, frame.bottom, paint);
        canvas.drawRect(frame.right - mLineDepth, frame.bottom - frame.height() * mLineRate, frame.right, frame.bottom, paint);

        //============================自己加入动画↑=========================


        // 灰色遮罩层  可以去掉
        paint.setColor(resultBitmap != null ? resultColor : maskColor);
        canvas.drawRect(0, 0, width, frame.top, paint);
        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
        canvas.drawRect(0, frame.bottom + 1, width, height, paint);

/*

        //===========PS:以下方法,不断执行onDraw方法绘制扫描线等样式进而产生自带的扫描线和闪光点,
        // 若是扫描到了,就会把结果图绘制在矩形框上,根据情况选择是否注释以下代码或部分动画代码

        if (resultBitmap != null) {
            //扫描到后在矩形上绘制不透明的图
            // Draw the opaque result bitmap over the scanning rectangle
            paint.setAlpha(CURRENT_POINT_OPACITY);
            canvas.drawBitmap(resultBitmap, null, frame, paint);
        } else {

            //自带的红色扫描线
            // Draw a red "laser scanner" line through the middle to show decoding is active
            paint.setColor(laserColor);
            paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
            scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
            final int middle = frame.height() / 2 + frame.top;
            canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);

            final float scaleX = frame.width() / (float) previewFrame.width();
            final float scaleY = frame.height() / (float) previewFrame.height();

            final int frameLeft = frame.left;
            final int frameTop = frame.top;

            // draw the last possible result points
            if (!lastPossibleResultPoints.isEmpty()) {
                paint.setAlpha(CURRENT_POINT_OPACITY / 2);
                paint.setColor(resultPointColor);
                float radius = POINT_SIZE / 2.0f;
                for (final ResultPoint point : lastPossibleResultPoints) {
                    canvas.drawCircle(
                            frameLeft + (int) (point.getX() * scaleX),
                            frameTop + (int) (point.getY() * scaleY),
                            radius, paint
                    );
                }
                lastPossibleResultPoints.clear();
            }

            // draw current possible result points
            if (!possibleResultPoints.isEmpty()) {
                paint.setAlpha(CURRENT_POINT_OPACITY);
                paint.setColor(resultPointColor);
                for (final ResultPoint point : possibleResultPoints) {
                    canvas.drawCircle(
                            frameLeft + (int) (point.getX() * scaleX),
                            frameTop + (int) (point.getY() * scaleY),
                            POINT_SIZE, paint
                    );
                }

                // swap and clear buffers
                final List<ResultPoint> temp = possibleResultPoints;
                possibleResultPoints = lastPossibleResultPoints;
                lastPossibleResultPoints = temp;
                possibleResultPoints.clear();
            }

            //不断调用执行绘制该活动界面进出现自动的动画
            // Request another update at the animation interval, but only repaint the laser line,
            // not the entire viewfinder mask.
            postInvalidateDelayed(ANIMATION_DELAY,
                    frame.left - POINT_SIZE,
                    frame.top - POINT_SIZE,
                    frame.right + POINT_SIZE,
                    frame.bottom + POINT_SIZE);
        }

*/



    }


}

Step 5 :准备3个布局:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始"
        android:id="@+id/button"/>

</LinearLayout>

content_scan.xml 其实这个布局,BarcodeView和ScanWidget相当于不在布局里,所以该布局像只有View控件,进而达到扫描条运动

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--
    layout_width、layout_height:启动扫描界面的布局参数

    zxing_framing_rect_width、zxing_framing_rect_height:
    在扫描界面中,只能扫描二维码的宽高,去掉后会有默认的宽高
    -->
    <com.journeyapps.barcodescanner.BarcodeView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/zxing_barcode_surface" />

    <!--注意自定义的扫描活动路径-->
    <com.gx.qr.ScanWidget
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/zxing_viewfinder_view"
        app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
        app:zxing_result_view="@color/zxing_custom_result_view"
        app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
        app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask"/>

    <!--@drawable/bmt 扫描条图标  请自行找素材-->
    <View
        android:id="@+id/scan_the"
        android:background="@drawable/bmt"
        android:layout_width="wrap_content"
        android:layout_height="70px" />

</merge>

activity_scan.xml 其实这个布局,DecoratedBarcodeView相当于不在布局里,所以该布局像只有ImageButton控件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--装扫描界面的控件
    @layout/content_scan:为嵌入content_scan.xml的布局
    -->
    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/dbv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        app:zxing_scanner_layout="@layout/content_scan">
    </com.journeyapps.barcodescanner.DecoratedBarcodeView>

    <!--闪光灯图片 自行找图片样式
        @drawable/ic_flashlight_close 关闭时的图片
    -->
    <ImageButton
        android:id="@+id/ib_flashlight_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="60dp"
        android:background="@drawable/ic_flashlight_close"/>
</RelativeLayout>

Step 6 :ScanActivity

public class ScanActivity extends AppCompatActivity {
    private CaptureManager capture;
    private ImageButton ibFlashlight;
    private DecoratedBarcodeView barcodeScannerView;
    private boolean bTorch = false;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //==设置布局、获取控件
        setContentView(R.layout.activity_scan);
        barcodeScannerView = findViewById(R.id.dbv);
        ibFlashlight= findViewById(R.id.ib_flashlight_close);

        //==保存扫描条到Application里
        View view = findViewById(R.id.scan_the);
        MyApplication myApplication= (MyApplication) getApplication();
        myApplication.setView(view);

        //==监听: 根据barcodeScannerView设置闪光灯ibFlashlight状态
        barcodeScannerView.setTorchListener(new DecoratedBarcodeView.TorchListener() {
            @Override
            public void onTorchOn() {//开灯

                //R.drawable.ic_flashlight_open)  开灯显示的图片 自行找图片样式
                ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_open));
                bTorch = true;
            }

            @Override
            public void onTorchOff() {//关灯

                //R.drawable.ic_flashlight_close)  关灯显示的图片 自行找图片样式
                ibFlashlight.setBackground(getResources().getDrawable(R.drawable.ic_flashlight_close));
                bTorch = false;
            }
        });

        //==开或关灯
        ibFlashlight.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bTorch){
                    barcodeScannerView.setTorchOff();
                } else {
                    barcodeScannerView.setTorchOn();
                }

            }
        });

        //==初始化活动
        capture = new CaptureManager(this, barcodeScannerView);

        capture.initializeFromIntent(getIntent(), savedInstanceState);

        capture.decode();
    }


    @Override
    protected void onResume() {
        super.onResume();
        capture.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        capture.onPause();
        barcodeScannerView.setTorchOff();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        capture.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        capture.onSaveInstanceState(outState);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
        capture.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
    }
}

Step 7 :MainActivity

public class MainActivity extends AppCompatActivity {

    private Button button;

    //  Step 1 : 初始化 获取控件 设置监听
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = findViewById(R.id.button);//点击跳转到扫码活动

        //控件监听
        listenerView();
    }


    private void listenerView() {

        //  Step 2 :跳转到扫描活动
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //=======设置扫描活动  可根据需求设置以下内容
                IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this);

                //启动自定义的扫描活动,不设置则启动默认的活动
                intentIntegrator.setCaptureActivity(ScanActivity.class);

                //启动扫描
                intentIntegrator.initiateScan();

            }
        });

    }


    //  Step 3 :处理扫码后返回的结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        IntentResult result = IntentIntegrator.parseActivityResult(requestCode,resultCode,data);

        if(result!=null){

            //==是否扫到内容
            if (result.getContents()!=null){
                Toast.makeText(this,"扫描结果:"+result.getContents(),Toast.LENGTH_LONG).show();
            }else{
                Toast.makeText(this,"取消扫码",Toast.LENGTH_LONG).show();
            }

        }else{
            super.onActivityResult(requestCode, resultCode, data);
        }

    }

    //=========PS:Android6.0以后的版本还需动态申请权限,若需动态申请,请在MainActivity中添加申请权限(调用此方法)再做打开相机扫描
    //暂时未发现需要动态申请(本人手机安卓10),可能不同手机厂商做法
    private void requsetPermission(){
        if (Build.VERSION.SDK_INT>22){
            if (ContextCompat.checkSelfPermission(MainActivity.this,
                    android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
                //先判断有没有权限 ,没有就在这里进行权限的申请
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{android.Manifest.permission.CAMERA},1);

            }else {

            }
        }else {

        }
    }

    //============PS:申请权限后的方法
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    //已获取权限,写需要做的代码

                }else {
                    //拒绝摄像头权限,可以提示用户给权限
                    Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }

    }

}

所用到的图片:

ic_flashlight_close.png
Android扫一扫  有仿微信版

ic_flashlight_open.png

Android扫一扫  有仿微信版

bmt.png
Android扫一扫  有仿微信版

OK!打完收工

本文地址:https://blog.csdn.net/weixin_44619313/article/details/107575365

相关标签: Android 扫码