Repository: MRYangY/YZxing Branch: master Commit: 00fe62b5ab69 Files: 65 Total size: 144.1 KB Directory structure: gitextract_56lrij_q/ ├── .gitignore ├── .idea/ │ ├── caches/ │ │ └── build_file_checksums.ser │ ├── codeStyles/ │ │ └── Project.xml │ ├── inspectionProfiles/ │ │ └── Project_Default.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── LICENSE ├── README.md ├── YZxing-lib/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── example/ │ │ └── qrcode/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── qrcode/ │ │ │ ├── BarcodeActivity.java │ │ │ ├── BeepManager.java │ │ │ ├── Constant.java │ │ │ ├── ScannerActivity.java │ │ │ ├── ShowResultActivity.java │ │ │ ├── callback/ │ │ │ │ └── PreviewCallback.java │ │ │ ├── camera/ │ │ │ │ ├── AutoFocusManager.java │ │ │ │ ├── CameraConfigurationManager.java │ │ │ │ ├── CameraFacing.java │ │ │ │ ├── CameraManager.java │ │ │ │ ├── OpenCamera.java │ │ │ │ └── OpenCameraInterface.java │ │ │ ├── decode/ │ │ │ │ ├── DecodeHandler.java │ │ │ │ ├── DecodeThread.java │ │ │ │ ├── InactivityTimer.java │ │ │ │ └── ScannerHandler.java │ │ │ ├── utils/ │ │ │ │ ├── CameraConfigurationUtils.java │ │ │ │ ├── CommonUtils.java │ │ │ │ ├── DecodeUtils.java │ │ │ │ └── UriUtils.java │ │ │ └── view/ │ │ │ └── ScannerView.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── progressbar_drawable.xml │ │ ├── layout/ │ │ │ ├── layout_activity_barcode.xml │ │ │ ├── layout_activity_scanner.xml │ │ │ └── layout_activity_show_result.xml │ │ ├── menu/ │ │ │ └── menu_scan.xml │ │ ├── raw/ │ │ │ └── beep.ogg │ │ └── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── qrcode/ │ └── ExampleUnitTest.java ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── example/ │ │ └── yzxing/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── yzxing/ │ │ │ └── MainActivity.java │ │ └── res/ │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── yzxing/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Built application files *.apk *.ap_ # Files for the ART/Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ out/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ # Intellij *.iml .idea/workspace.xml .idea/tasks.xml .idea/gradle.xml .idea/dictionaries .idea/libraries # Keystore files *.jks # External native build folder generated in Android Studio 2.2 and later .externalNativeBuild # Google Services (e.g. APIs or Firebase) google-services.json # Freeline freeline.py freeline/ freeline_project_description.json ================================================ FILE: .idea/codeStyles/Project.xml ================================================ ================================================ FILE: .idea/inspectionProfiles/Project_Default.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # YZxing 一款仿微信扫一扫界面,基于zxing实现的扫码库。 ### **实现效果** ### ![image](https://github.com/MRYangY/YZxing/blob/master/app/src/main/res/drawable-xhdpi/screenshot_2.png) ![image](https://github.com/MRYangY/YZxing/blob/master/app/src/main/res/drawable-xhdpi/screenshot_1.png) ### **使用方式** ### ~~首先通过在build.gradle文件中添加如下编译语句将YZxing-lib库添加到项目中。 compile 'com.yangy:YZxing-lib:1.1'~~ (或者在直接把本仓库里面的YZxing库下载下来,添加到项目中。) 然后在点击跳转到扫码界面的点击事件中,调用如下方法: Intent intent = new Intent(this, ScannerActivity.class); //这里可以用intent传递一些参数,比如扫码聚焦框尺寸大小,支持的扫码类型。 // //设置扫码框的宽 // intent.putExtra(Constant.EXTRA_SCANNER_FRAME_WIDTH, 400); // //设置扫码框的高 // intent.putExtra(Constant.EXTRA_SCANNER_FRAME_HEIGHT, 400); // //设置扫码框距顶部的位置 // intent.putExtra(Constant.EXTRA_SCANNER_FRAME_TOP_PADDING, 100); // Bundle bundle = new Bundle(); // //设置支持的扫码类型 // bundle.putSerializable(Constant.EXTRA_SCAN_CODE_TYPE, mHashMap); // intent.putExtras(bundle); startActivityForResult(intent, RESULT_REQUEST_CODE); 这里可以使用intent传递一些配置参数。支持有设置扫码框的大小,及位置;设置支持的扫码类型。目前支持的自定义配置不多,后续有机会再扩充。 跳转的时候要有startActivityForResult来跳转,这样在扫码成功之后,返回的结果可以在onActivityResult方法中处理代码如下: @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { switch (requestCode) { case RESULT_REQUEST_CODE: if (data == null) return; String type = data.getStringExtra(Constant.EXTRA_RESULT_CODE_TYPE); String content = data.getStringExtra(Constant.EXTRA_RESULT_CONTENT); Toast.makeText(MainActivity.this,"codeType:" + type + "-----content:" + content,Toast.LENGTH_SHORT).show(); break; default: break; } } super.onActivityResult(requestCode, resultCode, data); } ---------------------------------------- ## **YZxing版本更新说明** ## 目前最新版为v2.2!! -------------       目前YZxing已经更新到了v2.1 ,更新内容有:     1.修改空指针导致的的闪退bug。     2.添加从相册获取二维码功能(用户可以选择是否使用该功能)。      首先通过在build.gradle文件中添加如下编译语句将YZxing-lib库添加到项目中。 compile 'com.yangy:YZxing-lib:2.1' (或者在直接把本仓库里面的YZxing库下载下来,添加到项目中。)     使用方式与1.1版本一致,注意的是如需使用**从相册获取二维码功能**则需要在intent中添加是否启用scan_from_pic,默认是FALSE:       **//       //设置是否启用从相册获取二维码。 // intent.putExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC,true);**   ### **从照片获取二维码的效果如下:** ### ![image](https://github.com/MRYangY/YZxing/blob/master/app/src/main/res/effect-picture/scan_form_pic_%20effect.gif) ## **觉得还不错的就动动手指给个star吧(*^__^*)** ##   ## **关于二维码功能的分析请移步至文章** ## https://blog.csdn.net/qq_34902522/column/info/28769 ================================================ FILE: YZxing-lib/.gitignore ================================================ /build ================================================ FILE: YZxing-lib/build.gradle ================================================ apply plugin: 'com.android.library' apply plugin: 'com.jfrog.bintray' apply plugin: 'com.github.dcendents.android-maven' def siteUrl = 'https://github.com/MRYangY/YZxing' // 项目的主页 def gitUrl = 'https://github.com/MRYangY/YZxing.git' // Git仓库的url Properties properties = new Properties() properties.load(project.rootProject.file('local.properties').newDataInputStream()) version = "2.2" //发布版本号 group = "com.yangy" //最终引用形式,如compile 'com.yangy.lfilepicker:1.0.0',其中lfilepicker在后面配置 android { compileSdkVersion 26 buildToolsVersion "25.0.3" defaultConfig { minSdkVersion 19 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:26.+' compile 'com.android.support.constraint:constraint-layout:1.0.2' testCompile 'junit:junit:4.12' compile 'com.google.zxing:core:3.3.0' } bintray { user = properties.getProperty("bintray.user") key = properties.getProperty("bintray.apikey") pkg { repo = 'YZXing'//自己创建的仓库名字 name = 'yzxing'//上传到JCenter的名字,最终引用的名字 websiteUrl = siteUrl vcsUrl = gitUrl licenses = ['Apache-2.0']//不能随便写,只能是仓库创建时选择的license type userOrg = 'yangyulibrary' //自己创建的organization名称 publish = true // 是否是公开项目,公开别人可以引用 version { name = '2.2' desc = '基于zxing的扫码库,样式类似于微信扫一扫'//描述,自己定义 released = new Date() vcsTag = 'v2.2' attributes = ['gradle-plugin': 'com.use.less:com.use.less.gradle:gradle-useless-plugin'] } } configurations = ['archives'] } install { repositories.mavenInstaller { // This generates POM.xml with proper parameters pom { project { packaging 'aar' // Add your description here name 'yangy Android' description 'yangy open library.' url siteUrl // Set your license licenses { license { name 'Apache-2.0' //和之前自己定义的协议一致 url 'https://raw.githubusercontent.com/minggo620/Pluto-Android/master/LICENSE' } } developers { developer { id 'mryangy423' //填写bintray或者github的用户名 name 'mryangy423' //姓名 email 'mryangy423@gmail.com'//邮箱 } } scm { connection gitUrl developerConnection gitUrl url siteUrl } } } task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } task javadoc(type: Javadoc) { failOnError false //必须添加以免出错 source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } artifacts { archives javadocJar archives sourcesJar } javadoc { options{ //如果你的项目里面有中文注释的话,必须将格式设置为UTF-8,不然会出现乱码 encoding "UTF-8" charSet 'UTF-8' author true version true links "http://docs.oracle.com/javase/7/docs/api" } } } } ================================================ FILE: YZxing-lib/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/yangyu/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: YZxing-lib/src/androidTest/java/com/example/qrcode/ExampleInstrumentedTest.java ================================================ package com.example.qrcode; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.example.qrcode.test", appContext.getPackageName()); } } ================================================ FILE: YZxing-lib/src/main/AndroidManifest.xml ================================================ ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/BarcodeActivity.java ================================================ package com.example.qrcode; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Point; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.common.BitMatrix; import java.util.EnumMap; import java.util.Map; /** * Create by rain *

* DATE:18-10-26 *

* Describe: **/ public class BarcodeActivity extends Activity { private static final String TAG = "BarcodeActivity"; private static final int WHITE = 0xFFFFFFFF; private static final int BLACK = 0xFF000000; private static final int CODE_TYPE_QR_CODE = 0x11; private static final int CODE_TYPE_CODE_128 = 0x12; private int mType = CODE_TYPE_QR_CODE; private EditText mInputContentView; private Button mEncodeView; private ImageView mBarcodeImageView; private RadioGroup mRgCodeType; private RadioButton mRbQrCode; private RadioButton mRb128Code; private Bitmap bitmap; private Handler mHandler = new Handler(Looper.getMainLooper()); private int smallerDimension; private int barcodeImageWidth; private int barcodeImageHeight; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_activity_barcode); mInputContentView = findViewById(R.id.et_input_content); mEncodeView = findViewById(R.id.bt_encode); mEncodeView.setOnClickListener(mEncodeListener); mBarcodeImageView = findViewById(R.id.barcode_image); mRgCodeType = findViewById(R.id.rg_code_type); mRbQrCode = findViewById(R.id.rb_qr_code); mRb128Code = findViewById(R.id.rb_code_128); mRgCodeType.setOnCheckedChangeListener(onCheckedChangeListener); WindowManager manager = (WindowManager) getSystemService(WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); Point displaySize = new Point(); display.getSize(displaySize); int width = displaySize.x; int height = displaySize.y; smallerDimension = width < height ? width : height; smallerDimension = smallerDimension * 7 / 8; barcodeImageWidth = smallerDimension; barcodeImageHeight = smallerDimension; Log.e(TAG, "onCreate: smallerDimension = " + smallerDimension); } private RadioGroup.OnCheckedChangeListener onCheckedChangeListener = new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { barcodeImageWidth = smallerDimension; if (checkedId == R.id.rb_qr_code) { mType = CODE_TYPE_QR_CODE; barcodeImageHeight = smallerDimension; } else if (checkedId == R.id.rb_code_128) { mType = CODE_TYPE_CODE_128; barcodeImageHeight = smallerDimension / 2; } } }; private View.OnClickListener mEncodeListener = new View.OnClickListener() { @Override public void onClick(View v) { String text = mInputContentView.getText().toString(); if (TextUtils.isEmpty(text)) { Toast.makeText(BarcodeActivity.this, "请先输入条形码内容!!", Toast.LENGTH_SHORT).show(); return; } encode(text); } }; private void encode(String content) { Log.e(TAG, "encode: content = " + content); if (content == null) { return; } Map hints = null; String encoding = guessAppropriateEncoding(content); if (encoding != null) { hints = new EnumMap<>(EncodeHintType.class); hints.put(EncodeHintType.CHARACTER_SET, encoding); } BitMatrix result; try { result = new MultiFormatWriter().encode(content, getWantedCodeType(mType) , barcodeImageWidth, barcodeImageHeight, hints); int width = result.getWidth(); int height = result.getHeight(); int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { int offset = y * width; for (int x = 0; x < width; x++) { pixels[offset + x] = result.get(x, y) ? BLACK : WHITE; } } bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); mHandler.post(mUpdateImageRunnable); } catch (Exception iae) { // Unsupported format Log.e(TAG, "encode: " + iae.getMessage()); } } private Runnable mUpdateImageRunnable = new Runnable() { @Override public void run() { mBarcodeImageView.setImageBitmap(bitmap); } }; /** * get apposite barcode type * * @param type * @return */ private BarcodeFormat getWantedCodeType(int type) { switch (type) { case CODE_TYPE_QR_CODE: return BarcodeFormat.QR_CODE; case CODE_TYPE_CODE_128: return BarcodeFormat.CODE_128; default: return BarcodeFormat.QR_CODE; } } private static String guessAppropriateEncoding(CharSequence contents) { // Very crude at the moment for (int i = 0; i < contents.length(); i++) { if (contents.charAt(i) > 0xFF) { return "UTF-8"; } } return null; } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/BeepManager.java ================================================ package com.example.qrcode; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Vibrator; import android.preference.PreferenceManager; import android.util.Log; import java.io.Closeable; import java.io.IOException; /** * Created by yangyu on 17/10/19. */ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable { private static final String TAG = BeepManager.class.getSimpleName(); private static final float BEEP_VOLUME = 0.10f; private static final long VIBRATE_DURATION = 200L; private final Activity activity; private MediaPlayer mediaPlayer; private boolean playBeep; private boolean vibrate; BeepManager(Activity activity) { this.activity = activity; this.mediaPlayer = null; updatePrefs(); } synchronized void updatePrefs() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); playBeep = shouldBeep(prefs, activity); vibrate = prefs.getBoolean(Constant.KEY_VIBRATE, false); if (playBeep && mediaPlayer == null) { // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud, // so we now play on the music stream. activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); mediaPlayer = buildMediaPlayer(activity); } } synchronized void playBeepSoundAndVibrate() { if (playBeep && mediaPlayer != null) { mediaPlayer.start(); } if (vibrate) { Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(VIBRATE_DURATION); } } private static boolean shouldBeep(SharedPreferences prefs, Context activity) { boolean shouldPlayBeep = prefs.getBoolean(Constant.KEY_PLAY_BEEP, true); if (shouldPlayBeep) { // See if sound settings overrides this AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { shouldPlayBeep = false; } } return shouldPlayBeep; } private MediaPlayer buildMediaPlayer(Context activity) { MediaPlayer mediaPlayer = new MediaPlayer(); try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep)) { mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); mediaPlayer.setOnErrorListener(this); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setLooping(false); mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); mediaPlayer.prepare(); return mediaPlayer; } catch (IOException ioe) { Log.w(TAG, ioe); mediaPlayer.release(); return null; } } @Override public synchronized boolean onError(MediaPlayer mp, int what, int extra) { if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { // we are finished, so put up an appropriate error toast if required and finish activity.finish(); } else { // possibly media player error, so release and recreate close(); updatePrefs(); } return true; } @Override public synchronized void close() { if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/Constant.java ================================================ package com.example.qrcode; /** * Created by yangyu on 17/10/29. */ public class Constant { public static final int MESSAGE_SCANNER_DECODE = 0; public static final int MESSAGE_SCANNER_DECODE_SUCCEEDED = 1; public static final int MESSAGE_SCANNER_DECODE_FAIL = 2; public static final int MESSAGE_SCANNER_QUIT = 3; public static final String KEY_AUTO_FOCUS = "auto_focus"; public static final String KEY_DISABLE_CONTINUOUS_FOCUS = "continuous_focus"; public static final String KEY_VIBRATE = "beep_vibrate"; public static final String KEY_PLAY_BEEP = "beep_play"; public static final String EXTRA_SCANNER_FRAME_WIDTH = "scan_frame_width"; public static final String EXTRA_SCANNER_FRAME_HEIGHT = "scan_frame_height"; public static final String EXTRA_SCANNER_FRAME_TOP_PADDING = "scan_frame_top_padding"; public static final String EXTRA_SCAN_CODE_TYPE = "scan_code_type"; public static final String EXTRA_IS_ENABLE_SCAN_FROM_PIC = "is_enable_scan_from_pic"; public static final String EXTRA_RESULT_CODE_TYPE = "result_code_type"; public static final String EXTRA_RESULT_CONTENT = "result_content"; public static final String EXTRA_RESULT_TEXT_FROM_PIC = "text_from_pic"; } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/ScannerActivity.java ================================================ package com.example.qrcode; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import com.example.qrcode.camera.CameraManager; import com.example.qrcode.decode.InactivityTimer; import com.example.qrcode.decode.ScannerHandler; import com.example.qrcode.utils.CommonUtils; import com.example.qrcode.utils.DecodeUtils; import com.example.qrcode.utils.UriUtils; import com.example.qrcode.view.ScannerView; import com.google.zxing.BarcodeFormat; import com.google.zxing.Result; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.Set; /** * Created by yangyu on 17/10/18. */ public class ScannerActivity extends AppCompatActivity implements SurfaceHolder.Callback { private static final String TAG = "ScannerActivity"; public static final String BARCODE_FORMAT = "support_barcode_format"; public final int PERMISSION_REQUEST_CODE_WRITE_EXTERNAL_STORAGE = 0X11; public final int REQUEST_CODE_GET_PIC_URI = 0X12; private final int MESSAGE_DECODE_FROM_BITMAP = 0; private Toolbar mToolBar; private ScannerView mScannerView; private SurfaceView mSurfaceView; private InactivityTimer mInactivityTimer; private BeepManager beepManager; private com.example.qrcode.camera.CameraManager cameraManager; private ScannerHandler handler; private Collection decodeFormats; private int mScanFocusWidth; private int mScanFocusHeight; private int mScanFocusTopPadding; private boolean isEnableScanFromPicture; private boolean hasSurface; private MyHandler mHandler; private static class MyHandler extends Handler { private WeakReference activity; MyHandler(ScannerActivity mainActivityWeakReference) { activity = new WeakReference(mainActivityWeakReference); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); ScannerActivity activity = this.activity.get(); if (activity != null) { if (msg.what == activity.MESSAGE_DECODE_FROM_BITMAP) { Bitmap bm = (Bitmap) msg.obj; DecodeUtils.DecodeAsyncTask decodeAsyncTask = new DecodeUtils.DecodeAsyncTask(activity); decodeAsyncTask.execute(bm); } } } } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_activity_scanner); initView(); hasSurface = false; Intent intent = getIntent(); if (intent != null) { mScanFocusWidth = intent.getIntExtra(Constant.EXTRA_SCANNER_FRAME_WIDTH, -1); mScanFocusHeight = intent.getIntExtra(Constant.EXTRA_SCANNER_FRAME_HEIGHT, -1); mScanFocusTopPadding = intent.getIntExtra(Constant.EXTRA_SCANNER_FRAME_TOP_PADDING, -1); isEnableScanFromPicture = intent.getBooleanExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC, false); Bundle b = intent.getExtras(); if (b != null) { HashMap formats = (HashMap) b.getSerializable(Constant.EXTRA_SCAN_CODE_TYPE); if (formats != null) { decodeFormats = formats.get(BARCODE_FORMAT); } else { decodeFormats = EnumSet.of(BarcodeFormat.QR_CODE , BarcodeFormat.CODE_128); } } else { decodeFormats = EnumSet.of(BarcodeFormat.QR_CODE , BarcodeFormat.CODE_128); } } Log.e(TAG, "onCreate:decodeFormats :" + decodeFormats.size() + "--" + decodeFormats.toString()); mInactivityTimer = new InactivityTimer(this); beepManager = new BeepManager(this); mHandler = new MyHandler(this); } @Override protected void onResume() { super.onResume(); cameraManager = new CameraManager(this); cameraManager.setManualFramingRect(mScanFocusWidth, mScanFocusHeight, mScanFocusTopPadding); mScannerView.setCameraManager(cameraManager); SurfaceHolder holder = mSurfaceView.getHolder(); if (hasSurface) { initCamera(holder); } else { holder.addCallback(this); } mInactivityTimer.onResume(); beepManager.updatePrefs(); } @Override protected void onPause() { super.onPause(); if (handler != null) { handler.quitSynchronously(); handler = null; } cameraManager.closeDriver(); mInactivityTimer.onPause(); beepManager.close(); } @Override protected void onDestroy() { cameraManager.clearFramingRect(); mInactivityTimer.shutdown(); super.onDestroy(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: //关闭灯光 cameraManager.setTorch(false); return true; case KeyEvent.KEYCODE_VOLUME_UP: //开启闪光灯 cameraManager.setTorch(true); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onCreateOptionsMenu(Menu menu) { if (isEnableScanFromPicture) { getMenuInflater().inflate(R.menu.menu_scan, menu); return true; } else { return super.onCreateOptionsMenu(menu); } } @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.scan_from_picture) { //先申请权限 int checked = ContextCompat.checkSelfPermission(ScannerActivity.this , Manifest.permission.WRITE_EXTERNAL_STORAGE); if (checked == PackageManager.PERMISSION_GRANTED) { goPicture(); } else { ActivityCompat.requestPermissions(ScannerActivity.this , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE_WRITE_EXTERNAL_STORAGE); } } else if (itemId == R.id.encode_barcode) { startActivity(new Intent(ScannerActivity.this, BarcodeActivity.class)); } return true; } private void goPicture() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent, REQUEST_CODE_GET_PIC_URI); } private void initView() { mToolBar = (Toolbar) findViewById(R.id.tool_bar); mToolBar.setTitle("二维码/条形码"); mToolBar.setTitleTextColor(Color.WHITE); setSupportActionBar(mToolBar); mToolBar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); mSurfaceView = (SurfaceView) findViewById(R.id.surface); mScannerView = (ScannerView) findViewById(R.id.scan_view); } private void initCamera(SurfaceHolder surfaceHolder) { if (surfaceHolder == null) { throw new IllegalStateException("No SurfaceHolder provided"); } if (cameraManager.isOpen()) { Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?"); return; } try { cameraManager.openDriver(surfaceHolder); if (handler == null) { handler = new ScannerHandler(this, decodeFormats, "utf-8", cameraManager); } } catch (IOException ioe) { Log.w(TAG, ioe); } catch (RuntimeException e) { Log.w(TAG, "Unexpected error initializing camera", e); } } @Override public void surfaceCreated(SurfaceHolder holder) { if (holder == null) { Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!"); } if (!hasSurface) { hasSurface = true; initCamera(holder); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { hasSurface = false; } //在这里处理扫码结果 public void handDecode(final Result result) { mInactivityTimer.onActivity(); beepManager.playBeepSoundAndVibrate(); // AlertDialog.Builder mScannerDialogBuilder = new AlertDialog.Builder(this); // mScannerDialogBuilder.setMessage("codeType:" + result.getBarcodeFormat() + "-----content:" + result.getText()); // mScannerDialogBuilder.setCancelable(false); // mScannerDialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() { // @Override // public void onClick(DialogInterface dialog, int which) { // dialog.dismiss(); // ScannerActivity.this.finish(); // } // }); // mScannerDialogBuilder.create().show(); Intent data = new Intent(); BarcodeFormat format = result.getBarcodeFormat(); String type = format.toString(); data.putExtra(Constant.EXTRA_RESULT_CODE_TYPE, type); data.putExtra(Constant.EXTRA_RESULT_CONTENT, result.getText()); setResult(RESULT_OK, data); finish(); } public CameraManager getCameraManager() { return cameraManager; } public Handler getHandler() { return handler; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case REQUEST_CODE_GET_PIC_URI: Uri uri = data.getData(); String imagePath = UriUtils.getPicturePathFromUri(ScannerActivity.this, uri); //对获取到的二维码照片进行压缩 Bitmap bitmap = CommonUtils.compressPicture(imagePath); Message message = mHandler.obtainMessage(MESSAGE_DECODE_FROM_BITMAP, bitmap); mHandler.sendMessage(message); Log.e(TAG, "onActivityResult: uri:" + uri.toString()); break; } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_REQUEST_CODE_WRITE_EXTERNAL_STORAGE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { goPicture(); return; } } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/ShowResultActivity.java ================================================ package com.example.qrcode; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.util.Patterns; import android.view.View; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; import android.widget.TextView; /** * Created by yangyu on 2017/11/28. */ public class ShowResultActivity extends AppCompatActivity { private static final String TAG = "ShowResultActivity"; private WebView webView; private TextView tv; private ProgressBar pb; private String resultText; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_activity_show_result); webView = (WebView) findViewById(R.id.web_content); tv = (TextView) findViewById(R.id.tv); pb = (ProgressBar) findViewById(R.id.progress); WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setUseWideViewPort(true); webSettings.setLoadWithOverviewMode(true); webView.setWebChromeClient(webChromeClient); webView.setWebViewClient(webViewClient); Intent intent = getIntent(); if (intent != null) { resultText = intent.getStringExtra(Constant.EXTRA_RESULT_TEXT_FROM_PIC); if (Patterns.WEB_URL.matcher(resultText).matches()) { //是一个web url tv.setVisibility(View.GONE); webView.setVisibility(View.VISIBLE); webView.loadUrl(resultText); } else { //不是web url tv.setVisibility(View.VISIBLE); webView.setVisibility(View.GONE); pb.setVisibility(View.GONE); tv.setText(resultText); } } } private WebViewClient webViewClient = new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); } }; private WebChromeClient webChromeClient = new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); if (newProgress == 100) { pb.setVisibility(View.GONE); } else { pb.setVisibility(View.VISIBLE); pb.setProgress(newProgress); } } @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); Log.e(TAG, "onReceivedTitle: " + title); } }; } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/callback/PreviewCallback.java ================================================ package com.example.qrcode.callback; import android.graphics.Point; import android.hardware.Camera; import android.os.Handler; import android.os.Message; import android.util.Log; import com.example.qrcode.camera.CameraConfigurationManager; /** * Created by yangyu on 17/10/19. */ public class PreviewCallback implements Camera.PreviewCallback { private static final String TAG = PreviewCallback.class.getSimpleName(); private final CameraConfigurationManager configManager; private Handler previewHandler; private int previewMessage; public PreviewCallback(CameraConfigurationManager configManager) { this.configManager = configManager; } public void setHandler(Handler previewHandler, int previewMessage) { this.previewHandler = previewHandler; this.previewMessage = previewMessage; } @Override public void onPreviewFrame(byte[] data, Camera camera) { Point cameraResolution = configManager.getCameraResolution(); Handler thePreviewHandler = previewHandler; if (cameraResolution != null && thePreviewHandler != null) { Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x, cameraResolution.y, data); message.sendToTarget(); previewHandler = null; } else { Log.d(TAG, "Got preview callback, but no handler or resolution available"); } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/camera/AutoFocusManager.java ================================================ package com.example.qrcode.camera; import android.content.Context; import android.content.SharedPreferences; import android.hardware.Camera; import android.os.AsyncTask; import android.preference.PreferenceManager; import android.util.Log; import com.example.qrcode.Constant; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.RejectedExecutionException; /** * Created by yangyu on 17/10/19. */ final class AutoFocusManager implements Camera.AutoFocusCallback { private static final String TAG = AutoFocusManager.class.getSimpleName(); private static final long AUTO_FOCUS_INTERVAL_MS = 2000L; private static final Collection FOCUS_MODES_CALLING_AF; static { FOCUS_MODES_CALLING_AF = new ArrayList<>(2); FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO); FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO); } private boolean stopped; private boolean focusing; private final boolean useAutoFocus; private final Camera camera; private AsyncTask outstandingTask; AutoFocusManager(Context context, Camera camera) { this.camera = camera; SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); String currentFocusMode = camera.getParameters().getFocusMode(); useAutoFocus = sharedPrefs.getBoolean(Constant.KEY_AUTO_FOCUS, true) && FOCUS_MODES_CALLING_AF.contains(currentFocusMode); Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus); start(); } @Override public synchronized void onAutoFocus(boolean success, Camera theCamera) { focusing = false; autoFocusAgainLater(); } private synchronized void autoFocusAgainLater() { if (!stopped && outstandingTask == null) { AutoFocusTask newTask = new AutoFocusTask(); try { newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); outstandingTask = newTask; } catch (RejectedExecutionException ree) { Log.w(TAG, "Could not request auto focus", ree); } } } synchronized void start() { if (useAutoFocus) { outstandingTask = null; if (!stopped && !focusing) { try { camera.autoFocus(this); focusing = true; } catch (RuntimeException re) { // Have heard RuntimeException reported in Android 4.0.x+; continue? Log.w(TAG, "Unexpected exception while focusing", re); // Try again later to keep cycle going autoFocusAgainLater(); } } } } private synchronized void cancelOutstandingTask() { if (outstandingTask != null) { if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) { outstandingTask.cancel(true); } outstandingTask = null; } } synchronized void stop() { stopped = true; if (useAutoFocus) { cancelOutstandingTask(); // Doesn't hurt to call this even if not focusing try { camera.cancelAutoFocus(); } catch (RuntimeException re) { // Have heard RuntimeException reported in Android 4.0.x+; continue? Log.w(TAG, "Unexpected exception while cancelling focusing", re); } } } private final class AutoFocusTask extends AsyncTask { @Override protected Object doInBackground(Object... voids) { try { Thread.sleep(AUTO_FOCUS_INTERVAL_MS); } catch (InterruptedException e) { // continue } start(); return null; } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/camera/CameraConfigurationManager.java ================================================ package com.example.qrcode.camera; /** * Created by yangyu on 17/10/18. */ import android.content.Context; import android.content.SharedPreferences; import android.graphics.Point; import android.hardware.Camera; import android.preference.PreferenceManager; import android.util.Log; import android.view.Display; import android.view.WindowManager; import com.example.qrcode.Constant; import com.example.qrcode.utils.CameraConfigurationUtils; /** * A class which deals with reading, parsing, and setting the camera parameters which are used to * configure the camera hardware. */ @SuppressWarnings("deprecation") // camera APIs public final class CameraConfigurationManager { private static final String TAG = "CameraConfiguration"; private final Context context; private Point screenResolution; private Point cameraResolution; private Point bestPreviewSize; CameraConfigurationManager(Context context) { this.context = context; } /** * Reads, one time, values from the camera that are needed by the app. */ void initFromCameraParameters(OpenCamera camera) { Camera.Parameters parameters = camera.getCamera().getParameters(); WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); Point theScreenResolution = new Point(); display.getSize(theScreenResolution); screenResolution = theScreenResolution; Log.i(TAG, "Screen resolution in current orientation: " + screenResolution); cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution); Log.i(TAG, "Camera resolution: " + cameraResolution); bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution); Log.i(TAG, "Best available preview size: " + bestPreviewSize); } void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) { Camera theCamera = camera.getCamera(); Camera.Parameters parameters = theCamera.getParameters(); if (parameters == null) { Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration."); return; } Log.i(TAG, "Initial camera parameters: " + parameters.flatten()); if (safeMode) { Log.w(TAG, "In camera config safe mode -- most settings will not be honored"); } SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); CameraConfigurationUtils.setFocus( parameters, prefs.getBoolean(Constant.KEY_AUTO_FOCUS, true), prefs.getBoolean(Constant.KEY_DISABLE_CONTINUOUS_FOCUS, true), safeMode); parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y); theCamera.setParameters(parameters); //设置屏幕方向为垂直方向 theCamera.setDisplayOrientation(90); } public Point getCameraResolution() { return cameraResolution; } Point getScreenResolution() { return screenResolution; } boolean getTorchState(Camera camera) { if (camera != null) { Camera.Parameters parameters = camera.getParameters(); if (parameters != null) { String flashMode = parameters.getFlashMode(); return flashMode != null && (Camera.Parameters.FLASH_MODE_ON.equals(flashMode) || Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)); } } return false; } void setTorch(Camera camera, boolean newSetting) { Camera.Parameters parameters = camera.getParameters(); doSetTorch(parameters, newSetting, false); camera.setParameters(parameters); } private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) { CameraConfigurationUtils.setTorch(parameters, newSetting); } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/camera/CameraFacing.java ================================================ package com.example.qrcode.camera; /** * Created by yangyu on 17/10/18. */ enum CameraFacing { BACK, // must be value 0! FRONT, // must be value 1! } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/camera/CameraManager.java ================================================ package com.example.qrcode.camera; /** * Created by yangyu on 17/10/18. */ import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.hardware.Camera; import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import com.example.qrcode.callback.PreviewCallback; import com.google.zxing.PlanarYUVLuminanceSource; import java.io.IOException; /** * This object wraps the Camera service object and expects to be the only one talking to it. The * implementation encapsulates the steps needed to take preview-sized images, which are used for * both preview and decoding. * * @author dswitkin@google.com (Daniel Switkin) */ @SuppressWarnings("deprecation") // camera APIs public final class CameraManager { private static final String TAG = CameraManager.class.getSimpleName(); private static final int MIN_FRAME_WIDTH = 240; private static final int MIN_FRAME_HEIGHT = 240; private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920 private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080 private static final int DEFAULT_TOP_PADDINF = 200; private final Context context; private final CameraConfigurationManager configManager; private OpenCamera camera; private AutoFocusManager autoFocusManager; private Rect framingRect; private boolean initialized; private boolean previewing; private int requestedCameraId = OpenCameraInterface.NO_REQUESTED_CAMERA; private int requestedFramingRectWidth; private int requestedFramingRectHeight; private int requestedFramingRectTopPadding; /** * Preview frames are delivered here, which we pass on to the registered handler. Make sure to * clear the handler so it will only receive one message. */ private final PreviewCallback previewCallback; public CameraManager(Context context) { this.context = context; this.configManager = new CameraConfigurationManager(context); previewCallback = new PreviewCallback(configManager); } /** * Opens the camera driver and initializes the hardware parameters. * * @param holder The surface object which the camera will draw preview frames into. * @throws IOException Indicates the camera driver failed to open. */ public synchronized void openDriver(SurfaceHolder holder) throws IOException { OpenCamera theCamera = camera; if (theCamera == null) { theCamera = OpenCameraInterface.open(requestedCameraId); if (theCamera == null) { throw new IOException("Camera.open() failed to return object from driver"); } camera = theCamera; } if (!initialized) { initialized = true; configManager.initFromCameraParameters(theCamera); if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) { setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight , requestedFramingRectTopPadding > 0 ? requestedFramingRectTopPadding : -1); requestedFramingRectWidth = 0; requestedFramingRectHeight = 0; } } Camera cameraObject = theCamera.getCamera(); try { configManager.setDesiredCameraParameters(theCamera, false); } catch (RuntimeException re) { } cameraObject.setPreviewDisplay(holder); } public synchronized boolean isOpen() { return camera != null; } /** * Closes the camera driver if still in use. */ public synchronized void closeDriver() { if (camera != null) { camera.getCamera().release(); camera = null; } } public synchronized void clearFramingRect(){ // Make sure to clear these each time we close the camera, so that any scanning rect // requested by intent is forgotten. framingRect = null; } /** * Asks the camera hardware to begin drawing preview frames to the screen. */ public synchronized void startPreview() { OpenCamera theCamera = camera; if (theCamera != null && !previewing) { theCamera.getCamera().startPreview(); previewing = true; autoFocusManager = new AutoFocusManager(context, theCamera.getCamera()); } } /** * Tells the camera to stop drawing preview frames. */ public synchronized void stopPreview() { if (autoFocusManager != null) { autoFocusManager.stop(); autoFocusManager = null; } if (camera != null && previewing) { camera.getCamera().stopPreview(); previewCallback.setHandler(null, 0); previewing = false; } } /** * @param newSetting if {@code true}, light should be turned on if currently off. And vice versa. */ public synchronized void setTorch(boolean newSetting) { OpenCamera theCamera = camera; if (theCamera != null && newSetting != configManager.getTorchState(theCamera.getCamera())) { boolean wasAutoFocusManager = autoFocusManager != null; if (wasAutoFocusManager) { autoFocusManager.stop(); autoFocusManager = null; } configManager.setTorch(theCamera.getCamera(), newSetting); if (wasAutoFocusManager) { autoFocusManager = new AutoFocusManager(context, theCamera.getCamera()); autoFocusManager.start(); } } } /** * A single preview frame will be returned to the handler supplied. The data will arrive as byte[] * in the message.obj field, with width and height encoded as message.arg1 and message.arg2, * respectively. * * @param handler The handler to send the message to. * @param message The what field of the message to be sent. */ public synchronized void requestPreviewFrame(Handler handler, int message) { OpenCamera theCamera = camera; if (theCamera != null && previewing) { previewCallback.setHandler(handler, message); theCamera.getCamera().setOneShotPreviewCallback(previewCallback); } } /** * Calculates the framing rect which the UI should draw to show the user where to place the * barcode. This target helps with alignment as well as forces the user to hold the device * far enough away to ensure the image will be in focus. * * @return The rectangle to draw on screen in window coordinates. */ public synchronized Rect getFramingRect() { if (framingRect == null) { if (camera == null) { return null; } Point screenResolution = configManager.getScreenResolution(); if (screenResolution == null) { // Called early, before init even finished return null; } int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); int leftOffset = (screenResolution.x - width) / 2; int topOffset = DEFAULT_TOP_PADDINF; framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height); Log.d(TAG, "Calculated framing rect: " + framingRect); } return framingRect; } private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) { int dim = 5 * resolution / 8; // Target 5/8 of each dimension if (dim < hardMin) { return hardMin; } if (dim > hardMax) { return hardMax; } return dim; } // /** // * Like {@link #getFramingRect} but coordinates are in terms of the preview frame, // * not UI / screen. // * // * @return {@link Rect} expressing barcode scan area in terms of the preview size // */ // public synchronized Rect getFramingRectInPreview() { // if (framingRectInPreview == null) { // Rect framingRect = getFramingRect(); // if (framingRect == null) { // return null; // } // Rect rect = new Rect(framingRect); // Point cameraResolution = configManager.getCameraResolution(); // Point screenResolution = configManager.getScreenResolution(); // if (cameraResolution == null || screenResolution == null) { // // Called early, before init even finished // return null; // } // rect.left = rect.left * cameraResolution.x / screenResolution.x; // rect.right = rect.right * cameraResolution.x / screenResolution.x; // rect.top = rect.top * cameraResolution.y / screenResolution.y; // rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y; // framingRectInPreview = rect; // } // return framingRectInPreview; // } /** * 设置camera ID * * @param cameraId camera ID of the camera to use. A negative value means "no preference". */ public synchronized void setManualCameraId(int cameraId) { requestedCameraId = cameraId; } /** * 设置扫码框的大小 * * @param width The width in pixels to scan. * @param height The height in pixels to scan. */ public synchronized void setManualFramingRect(int width, int height, int topPadding) { if (initialized) { Point screenResolution = configManager.getScreenResolution(); if (width > screenResolution.x) { width = screenResolution.x; } if (height > screenResolution.y) { height = screenResolution.y; } int leftOffset = (screenResolution.x - width) / 2; int topOffset = topPadding < 0 ? DEFAULT_TOP_PADDINF : topPadding; framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height); } else { requestedFramingRectWidth = width; requestedFramingRectHeight = height; requestedFramingRectTopPadding = topPadding; } } /** * A factory method to build the appropriate LuminanceSource object based on the format * of the preview buffers, as described by Camera.Parameters. * * @param data A preview frame. * @param width The width of the image. * @param height The height of the image. * @return A PlanarYUVLuminanceSource instance. */ public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) { return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false); } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/camera/OpenCamera.java ================================================ package com.example.qrcode.camera; import android.hardware.Camera; /** * Created by yangyu on 17/10/18. */ public final class OpenCamera { private final int index; private final Camera camera; private final CameraFacing facing; private final int orientation; public OpenCamera(int index, Camera camera, CameraFacing facing, int orientation) { this.index = index; this.camera = camera; this.facing = facing; this.orientation = orientation; } public Camera getCamera() { return camera; } public CameraFacing getFacing() { return facing; } public int getOrientation() { return orientation; } @Override public String toString() { return "Camera #" + index + " : " + facing + ',' + orientation; } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/camera/OpenCameraInterface.java ================================================ package com.example.qrcode.camera; /** * Created by yangyu on 17/10/18. */ import android.hardware.Camera; import android.util.Log; /** * Abstraction over the {@link Camera} API that helps open them and return their metadata. */ @SuppressWarnings("deprecation") // camera APIs public final class OpenCameraInterface { private static final String TAG = OpenCameraInterface.class.getName(); /** For {@link #open(int)}, means no preference for which camera to open. */ public static final int NO_REQUESTED_CAMERA = -1; private OpenCameraInterface() { } /** * Opens the requested camera with {@link Camera#open(int)}, if one exists. * * @param cameraId camera ID of the camera to use. A negative value * or {@link #NO_REQUESTED_CAMERA} means "no preference", in which case a rear-facing * camera is returned if possible or else any camera * @return handle to {@link OpenCamera} that was opened */ public static OpenCamera open(int cameraId) { int numCameras = Camera.getNumberOfCameras(); if (numCameras == 0) { Log.w(TAG, "No cameras!"); return null; } boolean explicitRequest = cameraId >= 0; Camera.CameraInfo selectedCameraInfo = null; int index; if (explicitRequest) { index = cameraId; selectedCameraInfo = new Camera.CameraInfo(); Camera.getCameraInfo(index, selectedCameraInfo); } else { index = 0; while (index < numCameras) { Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); Camera.getCameraInfo(index, cameraInfo); CameraFacing reportedFacing = CameraFacing.values()[cameraInfo.facing]; if (reportedFacing == CameraFacing.BACK) { selectedCameraInfo = cameraInfo; break; } index++; } } Camera camera; if (index < numCameras) { Log.i(TAG, "Opening camera #" + index); camera = Camera.open(index); } else { if (explicitRequest) { Log.w(TAG, "Requested camera does not exist: " + cameraId); camera = null; } else { Log.i(TAG, "No camera facing " + CameraFacing.BACK + "; returning camera #0"); camera = Camera.open(0); selectedCameraInfo = new Camera.CameraInfo(); Camera.getCameraInfo(0, selectedCameraInfo); } } if (camera == null) { return null; } return new OpenCamera(index, camera, CameraFacing.values()[selectedCameraInfo.facing], selectedCameraInfo.orientation); } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/decode/DecodeHandler.java ================================================ package com.example.qrcode.decode; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import com.example.qrcode.Constant; import com.example.qrcode.ScannerActivity; import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.MultiFormatReader; import com.google.zxing.PlanarYUVLuminanceSource; import com.google.zxing.ReaderException; import com.google.zxing.Result; import com.google.zxing.common.HybridBinarizer; import java.util.Arrays; import java.util.Map; /** * Created by yangyu on 17/10/19. */ final class DecodeHandler extends Handler { private static final String TAG = DecodeHandler.class.getSimpleName(); private final ScannerActivity activity; private final MultiFormatReader multiFormatReader; private boolean running = true; private byte[] mRotatedData; DecodeHandler(ScannerActivity activity, Map hints) { multiFormatReader = new MultiFormatReader(); multiFormatReader.setHints(hints); this.activity = activity; } @Override public void handleMessage(Message message) { if (message == null || !running) { return; } if (message.what == Constant.MESSAGE_SCANNER_DECODE) { decode((byte[]) message.obj, message.arg1, message.arg2); } else if (message.what == Constant.MESSAGE_SCANNER_QUIT) { running = false; Looper.myLooper().quit(); } } /** * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, * reuse the same reader objects from one decode to the next. * * @param data The YUV preview frame. * @param width The width of the preview frame. * @param height The height of the preview frame. */ private void decode(byte[] data, int width, int height) { //这里需要对扫码的数据进行宽高的调换,原来扫码的是横屏数据,需要转化成竖屏。 if (null == mRotatedData) { mRotatedData = new byte[width * height]; } else { if (mRotatedData.length < width * height) { mRotatedData = new byte[width * height]; } } Arrays.fill(mRotatedData, (byte) 0); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (x + y * width >= data.length) { break; } mRotatedData[x * height + height - y - 1] = data[x + y * width]; } } int tmp = width; width = height; height = tmp; long start = System.currentTimeMillis(); Result rawResult = null; PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(mRotatedData, width, height); if (source != null) { BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap); } catch (ReaderException re) { // continue Log.e(TAG, "decode: re:" + re.getMessage()); } finally { multiFormatReader.reset(); } } Handler handler = activity.getHandler(); if (rawResult != null) { // Don't log the barcode contents for security. long end = System.currentTimeMillis(); Log.d(TAG, "Found barcode in " + (end - start) + " ms"); if (handler != null) { Message message = Message.obtain(handler, Constant.MESSAGE_SCANNER_DECODE_SUCCEEDED, rawResult); Bundle bundle = new Bundle(); message.setData(bundle); message.sendToTarget(); } } else { if (handler != null) { Message message = Message.obtain(handler, Constant.MESSAGE_SCANNER_DECODE_FAIL); message.sendToTarget(); } } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/decode/DecodeThread.java ================================================ package com.example.qrcode.decode; /** * Created by yangyu on 17/10/18. */ import android.os.Handler; import android.os.Looper; import com.example.qrcode.ScannerActivity; import com.google.zxing.BarcodeFormat; import com.google.zxing.DecodeHintType; import java.util.Collection; import java.util.EnumMap; import java.util.Map; import java.util.concurrent.CountDownLatch; /** * This thread does all the heavy lifting of decoding the images. * * @author dswitkin@google.com (Daniel Switkin) */ final class DecodeThread extends Thread { private final ScannerActivity activity; private final Map hints; private Handler handler; private final CountDownLatch handlerInitLatch; DecodeThread(ScannerActivity activity, Collection decodeFormats, String characterSet ) { this.activity = activity; handlerInitLatch = new CountDownLatch(1); hints = new EnumMap<>(DecodeHintType.class); hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); if (characterSet != null) { hints.put(DecodeHintType.CHARACTER_SET, characterSet); } hints.put(DecodeHintType.TRY_HARDER, true); } Handler getHandler() { try { handlerInitLatch.await(); } catch (InterruptedException ie) { } return handler; } @Override public void run() { Looper.prepare(); handler = new DecodeHandler(activity, hints); handlerInitLatch.countDown(); Looper.loop(); } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/decode/InactivityTimer.java ================================================ package com.example.qrcode.decode; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.AsyncTask; import android.os.BatteryManager; import android.util.Log; import java.util.concurrent.RejectedExecutionException; /** * Created by yangyu on 17/10/30. */ public final class InactivityTimer { private static final String TAG = InactivityTimer.class.getSimpleName(); private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L; private final Activity activity; private final BroadcastReceiver powerStatusReceiver; private boolean registered; private AsyncTask inactivityTask; public InactivityTimer(Activity activity) { this.activity = activity; powerStatusReceiver = new PowerStatusReceiver(); registered = false; onActivity(); } public synchronized void onActivity() { cancel(); inactivityTask = new InactivityAsyncTask(); try { inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } catch (RejectedExecutionException ree) { Log.w(TAG, "Couldn't schedule inactivity task; ignoring"); } } public synchronized void onPause() { cancel(); if (registered) { activity.unregisterReceiver(powerStatusReceiver); registered = false; } else { Log.w(TAG, "PowerStatusReceiver was never registered?"); } } public synchronized void onResume() { if (registered) { Log.w(TAG, "PowerStatusReceiver was already registered?"); } else { activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); registered = true; } onActivity(); } private synchronized void cancel() { AsyncTask task = inactivityTask; if (task != null) { task.cancel(true); inactivityTask = null; } } public void shutdown() { cancel(); } private final class PowerStatusReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { // 0 indicates that we're on battery boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0; if (onBatteryNow) { InactivityTimer.this.onActivity(); } else { InactivityTimer.this.cancel(); } } } } private final class InactivityAsyncTask extends AsyncTask { @Override protected Object doInBackground(Object... objects) { try { Thread.sleep(INACTIVITY_DELAY_MS); Log.i(TAG, "Finishing activity due to inactivity"); activity.finish(); } catch (InterruptedException e) { // continue without killing } return null; } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/decode/ScannerHandler.java ================================================ package com.example.qrcode.decode; import android.os.Handler; import android.os.Message; import com.example.qrcode.Constant; import com.example.qrcode.ScannerActivity; import com.example.qrcode.camera.CameraManager; import com.google.zxing.BarcodeFormat; import com.google.zxing.Result; import java.util.Collection; /** * Created by yangyu on 17/10/18. */ /** * This class handles all the messaging which comprises the state machine for capture. * * @author dswitkin@google.com (Daniel Switkin) */ public final class ScannerHandler extends Handler { private static final String TAG = ScannerHandler.class.getSimpleName(); private final ScannerActivity activity; private final DecodeThread decodeThread; private State state; private final CameraManager cameraManager; private enum State { PREVIEW, SUCCESS, DONE } public ScannerHandler(ScannerActivity activity, Collection decodeFormats, String characterSet, CameraManager cameraManager) { this.activity = activity; decodeThread = new DecodeThread(activity, decodeFormats, characterSet); decodeThread.start(); state = State.SUCCESS; // Start ourselves capturing previews and decoding. this.cameraManager = cameraManager; cameraManager.startPreview(); restartPreviewAndDecode(); } @Override public void handleMessage(Message message) { if (message == null) return; switch (message.what) { case Constant.MESSAGE_SCANNER_DECODE_SUCCEEDED: Result r = (Result) message.obj; activity.handDecode(r); break; case Constant.MESSAGE_SCANNER_DECODE_FAIL: state = State.PREVIEW; cameraManager.requestPreviewFrame(decodeThread.getHandler(), Constant.MESSAGE_SCANNER_DECODE); break; } } public void quitSynchronously() { state = State.DONE; cameraManager.stopPreview(); Message quit = Message.obtain(decodeThread.getHandler(), Constant.MESSAGE_SCANNER_QUIT); quit.sendToTarget(); try { // Wait at most half a second; should be enough time, and onPause() will timeout quickly decodeThread.join(500L); } catch (InterruptedException e) { // continue } // Be absolutely sure we don't send any queued up messages removeMessages(Constant.MESSAGE_SCANNER_DECODE_SUCCEEDED); removeMessages(Constant.MESSAGE_SCANNER_DECODE_FAIL); } private void restartPreviewAndDecode() { if (state == State.SUCCESS) { state = State.PREVIEW; cameraManager.requestPreviewFrame(decodeThread.getHandler(), Constant.MESSAGE_SCANNER_DECODE); } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/utils/CameraConfigurationUtils.java ================================================ package com.example.qrcode.utils; /** * Created by yangyu on 17/10/18. */ import android.annotation.TargetApi; import android.graphics.Point; import android.graphics.Rect; import android.hardware.Camera; import android.os.Build; import android.util.Log; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; /** * Utility methods for configuring the Android camera. * * @author Sean Owen */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) public final class CameraConfigurationUtils { private static final String TAG = "CameraConfiguration"; private static final Pattern SEMICOLON = Pattern.compile(";"); private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen private static final float MAX_EXPOSURE_COMPENSATION = 1.5f; private static final float MIN_EXPOSURE_COMPENSATION = 0.0f; private static final double MAX_ASPECT_DISTORTION = 0.15; private static final int MIN_FPS = 10; private static final int MAX_FPS = 20; private static final int AREA_PER_1000 = 400; private CameraConfigurationUtils() { } public static void setFocus(Camera.Parameters parameters, boolean autoFocus, boolean disableContinuous, boolean safeMode) { List supportedFocusModes = parameters.getSupportedFocusModes(); String focusMode = null; if (autoFocus) { if (safeMode || disableContinuous) { focusMode = findSettableValue("focus mode", supportedFocusModes, Camera.Parameters.FOCUS_MODE_AUTO); } else { focusMode = findSettableValue("focus mode", supportedFocusModes, Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE, Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, Camera.Parameters.FOCUS_MODE_AUTO); } } // Maybe selected auto-focus but not available, so fall through here: if (!safeMode && focusMode == null) { focusMode = findSettableValue("focus mode", supportedFocusModes, Camera.Parameters.FOCUS_MODE_MACRO, Camera.Parameters.FOCUS_MODE_EDOF); } if (focusMode != null) { if (focusMode.equals(parameters.getFocusMode())) { Log.i(TAG, "Focus mode already set to " + focusMode); } else { parameters.setFocusMode(focusMode); } } } public static void setTorch(Camera.Parameters parameters, boolean on) { List supportedFlashModes = parameters.getSupportedFlashModes(); String flashMode; if (on) { flashMode = findSettableValue("flash mode", supportedFlashModes, Camera.Parameters.FLASH_MODE_TORCH, Camera.Parameters.FLASH_MODE_ON); } else { flashMode = findSettableValue("flash mode", supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF); } if (flashMode != null) { if (flashMode.equals(parameters.getFlashMode())) { Log.i(TAG, "Flash mode already set to " + flashMode); } else { Log.i(TAG, "Setting flash mode to " + flashMode); parameters.setFlashMode(flashMode); } } } public static void setFocusArea(Camera.Parameters parameters) { if (parameters.getMaxNumFocusAreas() > 0) { Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas())); List middleArea = buildMiddleArea(AREA_PER_1000); Log.i(TAG, "Setting focus area to : " + toString(middleArea)); parameters.setFocusAreas(middleArea); } else { Log.i(TAG, "Device does not support focus areas"); } } public static void setMetering(Camera.Parameters parameters) { if (parameters.getMaxNumMeteringAreas() > 0) { Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas()); List middleArea = buildMiddleArea(AREA_PER_1000); Log.i(TAG, "Setting metering area to : " + toString(middleArea)); parameters.setMeteringAreas(middleArea); } else { Log.i(TAG, "Device does not support metering areas"); } } private static List buildMiddleArea(int areaPer1000) { return Collections.singletonList( new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1)); } public static void setVideoStabilization(Camera.Parameters parameters) { if (parameters.isVideoStabilizationSupported()) { if (parameters.getVideoStabilization()) { Log.i(TAG, "Video stabilization already enabled"); } else { Log.i(TAG, "Enabling video stabilization..."); parameters.setVideoStabilization(true); } } else { Log.i(TAG, "This device does not support video stabilization"); } } public static void setBarcodeSceneMode(Camera.Parameters parameters) { if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) { Log.i(TAG, "Barcode scene mode already set"); return; } String sceneMode = findSettableValue("scene mode", parameters.getSupportedSceneModes(), Camera.Parameters.SCENE_MODE_BARCODE); if (sceneMode != null) { parameters.setSceneMode(sceneMode); } } public static void setZoom(Camera.Parameters parameters, double targetZoomRatio) { if (parameters.isZoomSupported()) { Integer zoom = indexOfClosestZoom(parameters, targetZoomRatio); if (zoom == null) { return; } if (parameters.getZoom() == zoom) { Log.i(TAG, "Zoom is already set to " + zoom); } else { Log.i(TAG, "Setting zoom to " + zoom); parameters.setZoom(zoom); } } else { Log.i(TAG, "Zoom is not supported"); } } private static Integer indexOfClosestZoom(Camera.Parameters parameters, double targetZoomRatio) { List ratios = parameters.getZoomRatios(); Log.i(TAG, "Zoom ratios: " + ratios); int maxZoom = parameters.getMaxZoom(); if (ratios == null || ratios.isEmpty() || ratios.size() != maxZoom + 1) { Log.w(TAG, "Invalid zoom ratios!"); return null; } double target100 = 100.0 * targetZoomRatio; double smallestDiff = Double.POSITIVE_INFINITY; int closestIndex = 0; for (int i = 0; i < ratios.size(); i++) { double diff = Math.abs(ratios.get(i) - target100); if (diff < smallestDiff) { smallestDiff = diff; closestIndex = i; } } Log.i(TAG, "Chose zoom ratio of " + (ratios.get(closestIndex) / 100.0)); return closestIndex; } public static void setInvertColor(Camera.Parameters parameters) { if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) { Log.i(TAG, "Negative effect already set"); return; } String colorMode = findSettableValue("color effect", parameters.getSupportedColorEffects(), Camera.Parameters.EFFECT_NEGATIVE); if (colorMode != null) { parameters.setColorEffect(colorMode); } } public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) { List rawSupportedSizes = parameters.getSupportedPreviewSizes(); if (rawSupportedSizes == null) { Log.w(TAG, "Device returned no supported preview sizes; using default"); Camera.Size defaultSize = parameters.getPreviewSize(); if (defaultSize == null) { throw new IllegalStateException("Parameters contained no preview size!"); } return new Point(defaultSize.width, defaultSize.height); } // Sort by size, descending List supportedPreviewSizes = new ArrayList<>(rawSupportedSizes); Collections.sort(supportedPreviewSizes, new Comparator() { @Override public int compare(Camera.Size a, Camera.Size b) { int aPixels = a.height * a.width; int bPixels = b.height * b.width; if (bPixels < aPixels) { return -1; } if (bPixels > aPixels) { return 1; } return 0; } }); if (Log.isLoggable(TAG, Log.INFO)) { StringBuilder previewSizesString = new StringBuilder(); for (Camera.Size supportedPreviewSize : supportedPreviewSizes) { previewSizesString.append(supportedPreviewSize.width).append('x') .append(supportedPreviewSize.height).append(' '); } Log.i(TAG, "Supported preview sizes: " + previewSizesString); } double screenAspectRatio = screenResolution.x / (double) screenResolution.y; // Remove sizes that are unsuitable Iterator it = supportedPreviewSizes.iterator(); while (it.hasNext()) { Camera.Size supportedPreviewSize = it.next(); int realWidth = supportedPreviewSize.width; int realHeight = supportedPreviewSize.height; if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { it.remove(); continue; } boolean isCandidatePortrait = realWidth < realHeight; int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; double aspectRatio = maybeFlippedWidth / (double) maybeFlippedHeight; double distortion = Math.abs(aspectRatio - screenAspectRatio); if (distortion > MAX_ASPECT_DISTORTION) { it.remove(); continue; } if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) { Point exactPoint = new Point(realWidth, realHeight); Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint); return exactPoint; } } // If no exact match, use largest preview size. This was not a great idea on older devices because // of the additional computation needed. We're likely to get here on newer Android 4+ devices, where // the CPU is much more powerful. if (!supportedPreviewSizes.isEmpty()) { Camera.Size largestPreview = supportedPreviewSizes.get(0); Point largestSize = new Point(largestPreview.width, largestPreview.height); Log.i(TAG, "Using largest suitable preview size: " + largestSize); return largestSize; } // If there is nothing at all suitable, return current preview size Camera.Size defaultPreview = parameters.getPreviewSize(); if (defaultPreview == null) { throw new IllegalStateException("Parameters contained no preview size!"); } Point defaultSize = new Point(defaultPreview.width, defaultPreview.height); Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize); return defaultSize; } private static String findSettableValue(String name, Collection supportedValues, String... desiredValues) { Log.i(TAG, "Requesting " + name + " value from among: " + Arrays.toString(desiredValues)); Log.i(TAG, "Supported " + name + " values: " + supportedValues); if (supportedValues != null) { for (String desiredValue : desiredValues) { if (supportedValues.contains(desiredValue)) { Log.i(TAG, "Can set " + name + " to: " + desiredValue); return desiredValue; } } } Log.i(TAG, "No supported values match"); return null; } private static String toString(Collection arrays) { if (arrays == null || arrays.isEmpty()) { return "[]"; } StringBuilder buffer = new StringBuilder(); buffer.append('['); Iterator it = arrays.iterator(); while (it.hasNext()) { buffer.append(Arrays.toString(it.next())); if (it.hasNext()) { buffer.append(", "); } } buffer.append(']'); return buffer.toString(); } private static String toString(Iterable areas) { if (areas == null) { return null; } StringBuilder result = new StringBuilder(); for (Camera.Area area : areas) { result.append(area.rect).append(':').append(area.weight).append(' '); } return result.toString(); } public static String collectStats(Camera.Parameters parameters) { return collectStats(parameters.flatten()); } public static String collectStats(CharSequence flattenedParams) { StringBuilder result = new StringBuilder(1000); result.append("BOARD=").append(Build.BOARD).append('\n'); result.append("BRAND=").append(Build.BRAND).append('\n'); result.append("CPU_ABI=").append(Build.CPU_ABI).append('\n'); result.append("DEVICE=").append(Build.DEVICE).append('\n'); result.append("DISPLAY=").append(Build.DISPLAY).append('\n'); result.append("FINGERPRINT=").append(Build.FINGERPRINT).append('\n'); result.append("HOST=").append(Build.HOST).append('\n'); result.append("ID=").append(Build.ID).append('\n'); result.append("MANUFACTURER=").append(Build.MANUFACTURER).append('\n'); result.append("MODEL=").append(Build.MODEL).append('\n'); result.append("PRODUCT=").append(Build.PRODUCT).append('\n'); result.append("TAGS=").append(Build.TAGS).append('\n'); result.append("TIME=").append(Build.TIME).append('\n'); result.append("TYPE=").append(Build.TYPE).append('\n'); result.append("USER=").append(Build.USER).append('\n'); result.append("VERSION.CODENAME=").append(Build.VERSION.CODENAME).append('\n'); result.append("VERSION.INCREMENTAL=").append(Build.VERSION.INCREMENTAL).append('\n'); result.append("VERSION.RELEASE=").append(Build.VERSION.RELEASE).append('\n'); result.append("VERSION.SDK_INT=").append(Build.VERSION.SDK_INT).append('\n'); if (flattenedParams != null) { String[] params = SEMICOLON.split(flattenedParams); Arrays.sort(params); for (String param : params) { result.append(param).append('\n'); } } return result.toString(); } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/utils/CommonUtils.java ================================================ package com.example.qrcode.utils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; /** * Created by yangyu on 2017/11/28. */ public class CommonUtils { private static final String TAG = "CommonUtils"; public static Bitmap compressPicture(String imgPath) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imgPath, options); Log.e(TAG, "onActivityResult: 未压缩之前图片的宽:" + options.outWidth + "--未压缩之前图片的高:" + options.outHeight + "--未压缩之前图片大小:" + options.outWidth * options.outHeight * 4 / 1024 / 1024 + "M"); options.inSampleSize = calculateInSampleSize(options, 100, 100); Log.e(TAG, "onActivityResult: inSampleSize:" + options.inSampleSize); options.inJustDecodeBounds = false; Bitmap afterCompressBm = BitmapFactory.decodeFile(imgPath, options); // //默认的图片格式是Bitmap.Config.ARGB_8888 Log.e(TAG, "onActivityResult: 图片的宽:" + afterCompressBm.getWidth() + "--图片的高:" + afterCompressBm.getHeight() + "--图片大小:" + afterCompressBm.getWidth() * afterCompressBm.getHeight() * 4 / 1024 / 1024 + "M"); return afterCompressBm; } private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/utils/DecodeUtils.java ================================================ package com.example.qrcode.utils; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.os.AsyncTask; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.example.qrcode.Constant; import com.example.qrcode.ShowResultActivity; import com.google.zxing.BinaryBitmap; import com.google.zxing.ChecksumException; import com.google.zxing.DecodeHintType; import com.google.zxing.FormatException; import com.google.zxing.NotFoundException; import com.google.zxing.RGBLuminanceSource; import com.google.zxing.Result; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.QRCodeReader; import java.lang.ref.WeakReference; import java.util.EnumMap; import java.util.Map; /** * Created by yangyu on 2017/11/27. */ public class DecodeUtils { private static final String TAG = "DecodeUtils"; public static class DecodeAsyncTask extends AsyncTask { private WeakReference mContext; private Result result; public DecodeAsyncTask(Context mContext) { this.mContext = new WeakReference(mContext); } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Result doInBackground(Bitmap... bitmaps) { result = decodeFromPicture(bitmaps[0]); return result; } @Override protected void onPostExecute(Result result) { super.onPostExecute(result); if (result != null) { String text = result.getText(); if (!TextUtils.isEmpty(text)) { Intent intent = new Intent(mContext.get(), ShowResultActivity.class); intent.putExtra(Constant.EXTRA_RESULT_TEXT_FROM_PIC, text); mContext.get().startActivity(intent); if (mContext.get() instanceof Activity) ((Activity) mContext.get()).finish(); } } else { Toast.makeText(mContext.get(), "解码失败", Toast.LENGTH_SHORT).show(); } } } private static Result decodeFromPicture(Bitmap bitmap) { if (bitmap == null) return null; int picWidth = bitmap.getWidth(); int picHeight = bitmap.getHeight(); int[] pix = new int[picWidth * picHeight]; Log.e(TAG, "decodeFromPicture:图片大小: " + bitmap.getByteCount() / 1024 / 1024 + "M"); bitmap.getPixels(pix, 0, picWidth, 0, 0, picWidth, picHeight); //构造LuminanceSource对象 RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(picWidth , picHeight, pix); BinaryBitmap bb = new BinaryBitmap(new HybridBinarizer(rgbLuminanceSource)); //因为解析的条码类型是二维码,所以这边用QRCodeReader最合适。 QRCodeReader qrCodeReader = new QRCodeReader(); Map hints = new EnumMap<>(DecodeHintType.class); hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); hints.put(DecodeHintType.TRY_HARDER, true); Result result = null; try { result = qrCodeReader.decode(bb, hints); return result; } catch (NotFoundException | ChecksumException | FormatException e) { e.printStackTrace(); return null; } } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/utils/UriUtils.java ================================================ package com.example.qrcode.utils; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; import android.provider.MediaStore; /** * Created by yangyu on 2017/11/27. */ public class UriUtils { public static String getPicturePathFromUri(Context context, Uri uri) { int sdkVersion = Build.VERSION.SDK_INT; if (sdkVersion >= 19) { return getPicturePathFromUriAboveApi19(context, uri); } else { return getPicturePathFromUriBelowAPI19(context, uri); } } private static String getPicturePathFromUriBelowAPI19(Context context, Uri uri) { return getDataColumn(context, uri, null, null); } private static String getPicturePathFromUriAboveApi19(Context context, Uri uri) { String filePath = null; if (DocumentsContract.isDocumentUri(context, uri)) { // 如果是document类型的 uri, 则通过document id来进行处理 String documentId = DocumentsContract.getDocumentId(uri); if (isMediaDocument(uri)) { // MediaProvider // 使用':'分割 String id = documentId.split(":")[1]; String selection = MediaStore.Images.Media._ID + "=?"; String[] selectionArgs = {id}; filePath = getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs); } else if (isDownloadsDocument(uri)) { // DownloadsProvider Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId)); filePath = getDataColumn(context, contentUri, null, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是 content 类型的 Uri filePath = getDataColumn(context, uri, null, null); } else if ("file".equals(uri.getScheme())) { // 如果是 file 类型的 Uri,直接获取图片对应的路径 filePath = uri.getPath(); } return filePath; } private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { String path = null; String[] projection = new String[]{MediaStore.Images.Media.DATA}; Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndexOrThrow(projection[0]); path = cursor.getString(columnIndex); } } catch (Exception e) { if (cursor != null) { cursor.close(); } } return path; } private static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } private static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } } ================================================ FILE: YZxing-lib/src/main/java/com/example/qrcode/view/ScannerView.java ================================================ package com.example.qrcode.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import com.example.qrcode.R; import com.example.qrcode.camera.CameraManager; /** * Created by yangyu on 17/10/30. */ public class ScannerView extends View { private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64}; private static final long ANIMATION_DELAY = 90L; private Paint mBgPaint; private Paint mCornerPaint; private Paint mFocusFramePaint; private Paint mTipPaint; private Paint mLaserPaint; private CameraManager cameraManager; private int mCornerLength; private int mCornerThick; private int mTipPaddingTop; private int mFocusLineThick; private int scannerAlpha; public ScannerView(Context context) { this(context, null); } public ScannerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } /** * 一些初始化操作 */ private void init() { mBgPaint = new Paint(); mCornerPaint = new Paint(); mFocusFramePaint = new Paint(); mTipPaint = new Paint(); mLaserPaint = new Paint(); mBgPaint.setColor(getResources().getColor(R.color.scan_view_bg)); mBgPaint.setAntiAlias(true); mCornerPaint.setAntiAlias(true); mCornerPaint.setColor(getResources().getColor(R.color.scan_frame_green_color)); mFocusFramePaint.setAntiAlias(true); mFocusFramePaint.setColor(Color.WHITE); mTipPaint.setAntiAlias(true); mTipPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.scanner_view_tip_size)); mTipPaint.setColor(getResources().getColor(R.color.scan_view_tip_color)); mLaserPaint.setAntiAlias(true); mLaserPaint.setColor(getResources().getColor(R.color.scan_line_color)); mCornerLength = getResources().getDimensionPixelSize(R.dimen.scanner_view_corner_width); mCornerThick = getResources().getDimensionPixelSize(R.dimen.scanner_view_corner_thick); mTipPaddingTop = getResources().getDimensionPixelSize(R.dimen.scanner_view_tip_top); mFocusLineThick = getResources().getDimensionPixelSize(R.dimen.scanner_view_focus_line_thick); scannerAlpha = 0; } public void setCameraManager(CameraManager cameraManager) { this.cameraManager = cameraManager; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (cameraManager == null) return; Rect mScannerRect = cameraManager.getFramingRect(); if (mScannerRect == null) return; int width = canvas.getWidth(); int height = canvas.getHeight(); canvas.drawRect(0, 0, width, mScannerRect.top, mBgPaint); canvas.drawRect(0, mScannerRect.top, mScannerRect.left, mScannerRect.bottom + 1, mBgPaint); canvas.drawRect(mScannerRect.right + 1, mScannerRect.top, width, mScannerRect.bottom + 1, mBgPaint); canvas.drawRect(0, mScannerRect.bottom + 1, width, height, mBgPaint); drawCorner(canvas, mScannerRect); drawFocusRect(canvas, mScannerRect); drawTipText(canvas, getResources().getDisplayMetrics().widthPixels, mScannerRect.bottom + mTipPaddingTop); drawLaser(canvas, mScannerRect); //实现动画效果 postInvalidateDelayed(ANIMATION_DELAY, mScannerRect.left, mScannerRect.top, mScannerRect.right, mScannerRect.bottom); } /** * 绘制矩形框的四个角 * * @param canvas * @param rect */ private void drawCorner(Canvas canvas, Rect rect) { if (rect == null) return; //绘制左上角 canvas.drawRect(rect.left, rect.top, rect.left + mCornerLength, rect.top + mCornerThick, mCornerPaint); canvas.drawRect(rect.left, rect.top, rect.left + mCornerThick, rect.top + mCornerLength, mCornerPaint); //绘制左下角 canvas.drawRect(rect.left, rect.bottom - mCornerThick, rect.left + mCornerLength, rect.bottom, mCornerPaint); canvas.drawRect(rect.left, rect.bottom - mCornerLength, rect.left + mCornerThick, rect.bottom, mCornerPaint); //绘制右上角 canvas.drawRect(rect.right - mCornerLength, rect.top, rect.right, rect.top + mCornerThick, mCornerPaint); canvas.drawRect(rect.right - mCornerThick, rect.top, rect.right, rect.top + mCornerLength, mCornerPaint); //绘制右下角 canvas.drawRect(rect.right - mCornerLength, rect.bottom - mCornerThick, rect.right, rect.bottom, mCornerPaint); canvas.drawRect(rect.right - mCornerThick, rect.bottom - mCornerLength, rect.right, rect.bottom, mCornerPaint); } /** * 绘制聚焦框 */ private void drawFocusRect(Canvas canvas, Rect rect) { canvas.drawRect(rect.left + mCornerLength, rect.top, rect.right - mCornerLength, rect.top + mFocusLineThick, mFocusFramePaint); canvas.drawRect(rect.right - mFocusLineThick, rect.top + mCornerLength, rect.right, rect.bottom - mCornerLength, mFocusFramePaint); canvas.drawRect(rect.left + mCornerLength, rect.bottom - mFocusLineThick, rect.right - mCornerLength, rect.bottom, mFocusFramePaint); canvas.drawRect(rect.left, rect.top + mCornerLength, rect.left + mFocusLineThick, rect.bottom - mCornerLength, mFocusFramePaint); } //绘制提示语 private void drawTipText(Canvas canvas, int w, int h) { String tip = getResources().getString(R.string.scanner_view_tip_text); float l = (w - tip.length() * mTipPaint.getTextSize()) / 2; canvas.drawText(tip, l, h, mTipPaint); } /** * 绘制激光线 */ private void drawLaser(Canvas canvas, Rect rect) { mLaserPaint.setAlpha(SCANNER_ALPHA[scannerAlpha]); scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; int middle = rect.height() / 2 + rect.top; canvas.drawRect(rect.left + 2, middle - 1, rect.right - 1, middle + 2, mLaserPaint); } } ================================================ FILE: YZxing-lib/src/main/res/drawable/progressbar_drawable.xml ================================================ ================================================ FILE: YZxing-lib/src/main/res/layout/layout_activity_barcode.xml ================================================