Android开发框架之自定义ZXing二维码扫描界面并解决取景框拉伸问题
先给大家展示下效果图:
扫描内容是下面这张,二维码是用zxing库生成的
由于改了好几个类,还是去年的事都忘得差不多了,所以只能上这个类的代码了,主要就是改了这个CaptureActivity.java
package com.zxing.activity; import java.io.IOException; import java.util.Vector; import android.app.Activity; import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.os.Handler; import android.os.Vibrator; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.widget.Toast; import com.ericssonlabs.R; import com.google.zxing.BarcodeFormat; import com.google.zxing.Result; import com.zxing.camera.CameraManager; import com.zxing.decoding.CaptureActivityHandler; import com.zxing.decoding.CaptureActivityHandler.DecodeCallback; import com.zxing.decoding.InactivityTimer; import com.zxing.view.ViewfinderView; /** * Initial the camera * @author Ryan.Tang * @modifier Lemon * @use extends CaptureActivity并且在setContentView方法后调用init方法 */ public abstract class CaptureActivity extends Activity implements Callback, DecodeCallback { // private static final String TAG = "CaptureActivity"; protected Activity context; protected SurfaceView surfaceView; protected ViewfinderView viewfinderView; /**初始化,必须在setContentView之后 * @param context * @param viewfinderView */ protected void init(Activity context, SurfaceView surfaceView, ViewfinderView viewfinderView) { this.context = context; this.surfaceView = surfaceView; this.viewfinderView = viewfinderView; CameraManager.init(getApplication()); hasSurface = false; inactivityTimer = new InactivityTimer(this); } private CaptureActivityHandler handler; private boolean hasSurface; private Vector<BarcodeFormat> decodeFormats; private String characterSet; private InactivityTimer inactivityTimer; private MediaPlayer mediaPlayer; private boolean playBeep; private static final float BEEP_VOLUME = .f; private boolean vibrate; @Override protected void onResume() { super.onResume(); SurfaceHolder surfaceHolder = surfaceView.getHolder(); if (hasSurface) { initCamera(surfaceHolder); } else { surfaceHolder.addCallback(this); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } decodeFormats = null; characterSet = null; playBeep = true; AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE); if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { playBeep = false; } initBeepSound(); vibrate = true; } @Override protected void onPause() { super.onPause(); if (handler != null) { handler.quitSynchronously(); handler = null; } CameraManager.get().closeDriver(); } @Override protected void onDestroy() { inactivityTimer.shutdown(); super.onDestroy(); } public static final String RESULT_QRCODE_STRING = "RESULT_QRCODE_STRING"; /** * Handler scan result * @param result * @param barcode */ public void handleDecode(Result result, Bitmap barcode) { inactivityTimer.onActivity(); playBeepSoundAndVibrate(); String resultString = result.getText(); //FIXME if (resultString.equals("")) { Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show(); } setResult(RESULT_OK, new Intent().putExtra(RESULT_QRCODE_STRING, resultString)); finish(); } private void initCamera(SurfaceHolder surfaceHolder) { try { CameraManager.get().openDriver(surfaceHolder); } catch (IOException ioe) { return; } catch (RuntimeException e) { return; } if (handler == null) { handler = new CaptureActivityHandler(this, decodeFormats, characterSet, viewfinderView, this); } } @Override public void drawViewfinder() { viewfinderView.drawViewfinder(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { if (!hasSurface) { hasSurface = true; initCamera(holder); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { hasSurface = false; } public Handler getHandler() { return handler; } private void initBeepSound() { 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. setVolumeControlStream(AudioManager.STREAM_MUSIC); mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setOnCompletionListener(beepListener); AssetFileDescriptor file = getResources().openRawResourceFd( R.raw.beep); try { mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); file.close(); mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); mediaPlayer.prepare(); } catch (IOException e) { mediaPlayer = null; } } } private static final long VIBRATE_DURATION = L; private void playBeepSoundAndVibrate() { if (playBeep && mediaPlayer != null) { mediaPlayer.start(); } if (vibrate) { Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); vibrator.vibrate(VIBRATE_DURATION); } } /** * When the beep has finished playing, rewind to queue up another one. */ private final OnCompletionListener beepListener = new OnCompletionListener() { public void onCompletion(MediaPlayer mediaPlayer) { mediaPlayer.seekTo(); } }; }
使用方法: 新建一个Activity继承CaptureActivity并且在setContentView方法后调用init方法即可。
示例:
CameraScanActivity.java
package zuo.biao.activity; import zuo.biao.R; import zuo.biao.library.interfaces.OnBottomDragListener; import zuo.biao.util.ActivityUtil; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import com.zxing.activity.CaptureActivity; import com.zxing.camera.CameraManager; import com.zxing.view.ViewfinderView; /**扫描二维码Activity * @author Lemon * @use 参考zuo.biao.library.ModelActivity */ public class CameraScanActivity extends CaptureActivity implements Callback, OnClickListener, OnBottomDragListener { public static final String TAG = "CameraScanActivity"; //启动方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< /**启动这个Activity的Intent * @param context * @param title * @return */ public static Intent createIntent(Context context) { return new Intent(context, CameraScanActivity.class); } //启动方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.camera_scan_activity); init(this, (SurfaceView) findViewById(R.id.svCameraScan), (ViewfinderView) findViewById(R.id.vfvCameraScan)); //功能归类分区方法,必须调用<<<<<<<<<< initView(); initData(); initListener(); //功能归类分区方法,必须调用>>>>>>>>>> } //UI显示区(操作UI,但不存在数据获取或处理代码,也不存在事件监听代码)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< public void initView() {//必须调用 } //UI显示区(操作UI,但不存在数据获取或处理代码,也不存在事件监听代码)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //data数据区(存在数据获取或处理代码,但不存在事件监听代码)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< public void initData() {//必须调用 } //data数据区(存在数据获取或处理代码,但不存在事件监听代码)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //listener事件监听区(只要存在事件监听代码就是)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< public void initListener() {//必须调用 findViewById(R.id.tvCameraScanReturn).setOnClickListener(this); findViewById(R.id.ivCameraScanReturn).setOnClickListener(this); findViewById(R.id.ivCameraScanLight).setOnClickListener(this); findViewById(R.id.ivCameraScanMyQRCode).setOnClickListener(this); } //系统自带监听方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @Override public void onDragBottom(boolean rightToLeft) { if (rightToLeft) { return; } finish(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.tvCameraScanReturn: case R.id.ivCameraScanReturn: onDragBottom(false); break; case R.id.ivCameraScanLight: switchLight(! isOpen); break; case R.id.ivCameraScanMyQRCode: // break; default: break; } } private boolean isOpen = false; /**打开或关闭闪关灯 * @param open */ private void switchLight(boolean open) { if (open == isOpen) { return; } isOpen = CameraManager.get().switchLight(open); } //类相关监听<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //类相关监听>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //系统自带监听方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //listener事件监听区(只要存在事件监听代码就是)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //内部类,尽量少用<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //内部类,尽量少用>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> }
CameraScanActivity布局文件camera_scan_activity.xml
<?xml version="." encoding="utf-"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" style="@style/activity_page" > <SurfaceView android:id="@+id/svCameraScan" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <!-- 必须在最底层,且不能指定宽高,否则扫描读取很难实现 --> <com.zxing.view.ViewfinderView android:id="@+id/vfvCameraScan" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout style="@style/ll_vertical_match_match" android:baselineAligned="false" > <RelativeLayout style="@style/topbar_bg" android:background="@color/white_alpha" > <TextView android:id="@+id/tvCameraScanReturn" style="@style/topbar_left_btn" android:text=" " /> <TextView style="@style/topbar_title" android:layout_centerHorizontal="true" android:text="扫一扫" /> </RelativeLayout> <LinearLayout style="@style/ll_vertical_match_match" android:layout_gravity="center_horizontal" android:layout_weight="" > <TextView style="@style/text_middle_white" android:layout_margin="dp" android:text="@string/camera_scan_remind" /> </LinearLayout> <LinearLayout style="@style/ll_horizontal_match_wrap" android:layout_gravity="bottom" android:background="@color/white_alpha" android:gravity="center" > <LinearLayout style="@style/ll_vertical_wrap_wrap" android:layout_margin="dp" android:paddingBottom="dp" android:paddingLeft="dp" android:paddingRight="dp" android:paddingTop="dp" > <ImageView android:id="@+id/ivCameraScanReturn" style="@style/wrap_wrap" android:background="@drawable/cilcle_gray_to_white" android:padding="dp" android:src="@drawable/back_black_light" /> <TextView style="@style/text_small" android:layout_marginTop="dp" android:text="返回" /> </LinearLayout> <LinearLayout style="@style/ll_vertical_wrap_wrap" android:layout_margin="dp" android:paddingBottom="dp" android:paddingLeft="dp" android:paddingRight="dp" android:paddingTop="dp" > <ImageView android:id="@+id/ivCameraScanLight" style="@style/wrap_wrap" android:background="@drawable/cilcle_gray_to_white" android:padding="dp" android:src="@drawable/flash_light" /> <TextView style="@style/text_small" android:layout_marginTop="dp" android:text="开灯/关灯" /> </LinearLayout> <LinearLayout style="@style/ll_vertical_wrap_wrap" android:layout_margin="dp" android:paddingBottom="dp" android:paddingLeft="dp" android:paddingRight="dp" android:paddingTop="dp" > <ImageView android:id="@+id/ivCameraScanMyQRCode" style="@style/wrap_wrap" android:background="@drawable/cilcle_gray_to_white" android:padding="dp" android:src="@drawable/qrcode" /> <TextView style="@style/text_small" android:layout_marginTop="dp" android:text="我的名片" /> </LinearLayout> </LinearLayout> </LinearLayout> </FrameLayout>
布局文件因为使用了ZBLibrary中的一些style,color等,只有这个layout的话会出现一些错误。自己新建一个layout文件并把ViewfinderView放到最外层布局文件内就行了。当然下载好下面附上的源码就没这问题了。
对了,记得在AndroidManifest.xml中加上这些权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.FLASHLIGHT" />
附源码(含使用方法)
GitHub源码 https://github.com/TommyLemon/Android-ZBLibrary
开源中国源码 http://git.oschina.net/Lemon19950301/Android-ZBLibrary
关于Android开发框架之自定义ZXing二维码扫描界面并解决取景框拉伸问题就给大家介绍到这里,以上内容有点长,希望大家能耐心看完,有任何问题欢迎给我留言,小编会及时回复大家的,在此也非常感谢大家对脚本之家网站的支持!
相关文章
详解Android 通过Socket 和服务器通讯(附demo)
Android 通过Socket 和服务器通讯,是一种比较常用的通讯方式,这篇文章主要介绍了详解Android 通过Socket 和服务器通讯,有兴趣的可以了解一下。2016-12-12flutter showModalBottomSheet常用属性及说明
这篇文章主要介绍了flutter showModalBottomSheet常用属性及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-09-09Android Recyclerview实现上拉加载更多功能
在项目中使用列表的下拉刷新和上拉加载更多是很常见的功能。下文给大家带来了Android Recyclerview上拉加载更多功能,需要的朋友参考下吧2017-05-05Android动画之渐变动画(Tween Animation)详解 (渐变、缩放、位移、旋转)
这篇文章主要介绍了Android动画之渐变动画(Tween Animation)用法,结合实例形式详细分析了Android渐变动画Tween Animation实现渐变,缩放,位移,旋转等技巧,需要的朋友可以参考下2016-01-01Android开发之利用Activity实现Dialog对话框
这篇文章主要给大家介绍了Android开发之如何利用Activity实现Dialog对话框效果,文中给出了详细的示例代码,相信对大家的理解及学习具有一定的参考借鉴价值,有需要的朋友们下面来一起看看吧。2016-12-12
最新评论