第三方Zxing的GitHub地址:

tip:前面几种是为了更好的理解,也可直接划到第三种看仿微信扫码的那种。

用法:

国际惯例,先上图:
Android扫码 有仿微信版_第1张图片

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...即可)                                * */                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 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扫码 有仿微信版_第2张图片

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扫码 有仿微信版_第3张图片

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扫码 有仿微信版_第4张图片
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);        //设置扫描条的动画        注意这个 60 是扫描条的宽度 单位是px        //参数 3:运动开始的地方:frame.top是扫描框离屏幕顶部的距离,减60是因为 这个扫描条 的高是 60 px,        // 参数3的单位也是px,所以运动开始的地方就是 frame.top-60;参数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 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;        }    }}

OK!打完收工

更多相关文章

  1. 后台动态添加布局文件、控件与动态设置属性2
  2. androidUI布局仿猫扑界面
  3. Android如何获得系统(system)权限 !!!
  4. 一个关于android中ListView的子控件中按钮事件的简单方法
  5. 【摘录】 Android中如何修改系统时间(应用程序获得系统权限)
  6. Android Layout 布局 && Android自带样式(theme)&& CheckBox样例 &
  7. 使用Android常用控件与布局实现美观的登录页面
  8. Android Studio3.1.2在项目的build.gradle中添加百分比布局库的
  9. andorid 将布局文件(layout)转换为图片(Bitmap)简单使用详解

随机推荐

  1. Android准备工作
  2. J2ME VS Android
  3. android回调函数总结
  4. 使用Android(安卓)Studio+Spring Boot+My
  5. Android开源手机平台揭秘和未来发展
  6. Android(安卓)神兵利器Dagger2使用详解(一
  7. Android源代码获取(Windows)
  8. Android(安卓)OOM-Heap,MAT工具检测内存
  9. Android生成二维码--拍照或从相册选取图
  10. Android(安卓)App基本要素(二)