【转】Android平台下利用zxing实现二维码开发

http://www.cnblogs.com/dolphin0520/p/3355728.html

  现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing进行二维码开发。

1.如何将zxing的Android源码导入工程。

  在导入zxing的android源码之前,先去官方下载zxing的源码:http://code.google.com/p/zxing/downloads/list

  我这里下载的是1.6版本的,我试验了几个版本,发现2.0以后的版本实际用起来没有1.6和1.7版本的好用,最后选择了1.6版本。

  zxing 1.6源码结构如下:

  其中android文件夹就是android平台下的官方例子。

  在导入之前先要对core文件下的源码进行编译,得到核心包core.jar。

  编译方法请参照:http://blog.163.com/[email protected]/blog/static/671560502011611111116747/

  然后就可以导入android平台下的例子了,导入方法如下:

  1)打开Eclipse,新建android项目:(注意不要直接把android文件夹拷到workspace下导入,那样会无法导入)

  

  2)导入核心包core.jar。

  3)修改strings.xml文件。在导入core.jar之后工程还是会有错误:

  

  出现这种错误可能是由于字符错误导致的,只需要把所有的%s 和%f改成 %1s和f   即可。

  修改完之后重新清理项目,此时已经没有错误了:

  

  4)运行效果:

  

2.代码简化

  上面代码中,很多功能我们在自己的项目中都用不到,因此需要对其进行简化,至于如何简化这里就不赘述了,网上有很多教程。下面只给出简化后的结果:

  

  如果只进行二维码识别和二维码生成的话,只需要上面包中的文件。其中CaptureActivity.java是拍照取景的类,camera包下面的类主要与照相机相关的类,decoding和encoding是解码和编码相关的类,view是取景框相关的类。

3.将简化的zxing代码嵌入自己的工程。

  在自己的工程中嵌入简化的zxing代码即可实现二维码生成和识别功能。

  嵌入方法:

  1)将上述简化的代码拖到自己工程目录下;

  2)将values文件夹和raw文件夹复制自己工程目录下;

  3)建立CaptureActivity.java的布局文件capture.xml。

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

    <SurfaceView
        android:id="@+id/preview_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <com.zxing.view.ViewfinderView
        android:id="@+id/viewfinder_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center"
        android:orientation="vertical" >

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:paddingBottom="10dp"
            android:paddingTop="10dp"
            android:text="Scan Barcode"
            android:textColor="@android:color/white"
            android:textSize="18sp"
            android:textStyle="bold" />

        <Button
            android:id="@+id/btn_cancel_scan"
            android:layout_width="230dp"
            android:layout_height="40dp"
            android:layout_alignParentBottom="true"
            android:layout_centerInParent="true"
            android:layout_marginBottom="75dp"
            android:text="Cancel"
            android:textSize="15sp"
            android:textStyle="bold" />

    </RelativeLayout>

</FrameLayout>

  3)导入core.jar包

  4)修改AndrodMainfest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.qrcode"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7" />

    <uses-permission android:name="android.permission.VIBRATE" />  <!-- 震动权限 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />        <!-- 使用照相机权限 -->
    <uses-feature android:name="android.hardware.camera.autofocus" />   <!-- 自动聚焦权限 -->

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 隐藏键盘 --><!-- 全屏 -->
        <activity
            android:configChanges="orientation|keyboardHidden"
            android:name="com.zxing.activity.CaptureActivity"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
            android:windowSoftInputMode="stateAlwaysHidden" >
        </activity>

    </application>
</manifest>

  5)运行效果:

   

  在这里识别二维码之后是把结果返回到上一个activity显示,所以在MainActivity中调用方法是:

Intent openCameraIntent = new Intent(MainActivity.this,CaptureActivity.class);
startActivityForResult(openCameraIntent, 0);

  然后获取结果在onActivityResult中进行:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            Bundle bundle = data.getExtras();
            String scanResult = bundle.getString("result");
            resultTextView.setText(scanResult);
        }
    }

  如果要直接在CaptureActivity中弹出识别结果,可以更改CaptureActivity.java的handleDecode方法:

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();
        }else {
//            System.out.println("Result:"+resultString);
            Intent resultIntent = new Intent();
            Bundle bundle = new Bundle();
            bundle.putString("result", resultString);
            resultIntent.putExtras(bundle);
            this.setResult(RESULT_OK, resultIntent);
        }
        CaptureActivity.this.finish();
    }

  上述代码是得到结果之后传给之前调用CaptureActivity的activity,所以如果要改的话只需要把else语句块下面的语句和最后一句finish注释掉,补上自己的代码就可以了。比如可以改成:

public void handleDecode(Result result, Bitmap barcode) {
        inactivityTimer.onActivity();
        playBeepSoundAndVibrate();
        final String resultString = result.getText();
        //FIXME
        if (resultString.equals("")) {
            Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
        }else {
//            System.out.println("Result:"+resultString);
            /*Intent resultIntent = new Intent();
            Bundle bundle = new Bundle();
            bundle.putString("result", resultString);
            resultIntent.putExtras(bundle);
            this.setResult(RESULT_OK, resultIntent);*/

            AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create();
            resutlDialog.setTitle("扫描结果");
            resutlDialog.setMessage(resultString);
            resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    dialog.dismiss();
                }
            });
            resutlDialog.show();
        }
        //CaptureActivity.this.finish();
    }

  我的工程源码下载:http://files.cnblogs.com/dolphin0520/QRCode.rar

3.关于zxing源码的若干问题。

  在实际使用过程中发现zxing源码存在一些问题,下面逐一来说一下:

  1)竖屏问题。

  zxing给出的官方例子是横屏的,但是对于手机实际上竖屏操作更加方便一点,如果要改成竖屏,需要修改以下几个文件:

  首先将AndrodMainfest.xml下CaptureActivity的配置改为竖屏:

android:screenOrientation="portrait"

  然后需要修改CameraConfigurationManager.java下的setDesiredCameraParameters方法,该方法直接使用

camera.setDisplayOrientation(90);

  来让屏幕旋转90度,这个办法在android 2.2版本以后是可行的,但是2.2之前的版本并没有提供这个接口,因此需要对sdk版本进行判断:

if (Integer.parseInt(Build.VERSION.SDK) >=8)
        setDisplayOrientation(camera, 90);
    else
    {
        if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        {
           parameters.set("orientation", "portrait");
           parameters.set("rotation", 90);
        }
        if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
        {
           parameters.set("orientation", "landscape");
           parameters.set("rotation", 90);
        }
    }   

  在修改为竖屏之后发现取景框会发生拉伸,需要进行以下几点修改:

  在CameraManager.java中将:

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;

  修改为:

rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

  然后将DecodeHandler类中的方法decode改为:

private void decode(byte[] data, int width, int height) {
    long start = System.currentTimeMillis();
    Result rawResult = null;

    //modify here
    byte[] rotatedData = new byte[data.length];
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++)
            rotatedData[x * height + height - y - 1] = data[x + y * width];
    }
    int tmp = width; // Here we are swapping, that‘s the difference to #11
    width = height;
    height = tmp;

    PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
    try {
      rawResult = multiFormatReader.decodeWithState(bitmap);
    } catch (ReaderException re) {
      // continue
    } finally {
      multiFormatReader.reset();
    }

    if (rawResult != null) {
      long end = System.currentTimeMillis();
      Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
      Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
      Bundle bundle = new Bundle();
      bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
      message.setData(bundle);
      //Log.d(TAG, "Sending decode succeeded message...");
      message.sendToTarget();
    } else {
      Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
      message.sendToTarget();
    }
  }

  再将CameraConfigurationManager类中的方法initFromCameraParameters修改为:

void initFromCameraParameters(Camera camera) {
    Camera.Parameters parameters = camera.getParameters();
    previewFormat = parameters.getPreviewFormat();
    previewFormatString = parameters.get("preview-format");
    Log.d(TAG, "Default preview format: " + previewFormat + ‘/‘ + previewFormatString);
    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = manager.getDefaultDisplay();
    screenResolution = new Point(display.getWidth(), display.getHeight());
    Log.d(TAG, "Screen resolution: " + screenResolution);

    Point screenResolutionForCamera = new Point();
    screenResolutionForCamera.x = screenResolution.x;
    screenResolutionForCamera.y = screenResolution.y;
    // preview size is always something like 480*320, other 320*480
    if (screenResolution.x < screenResolution.y) {
    screenResolutionForCamera.x = screenResolution.y;
    screenResolutionForCamera.y = screenResolution.x;
    }

    cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);

    //cameraResolution = getCameraResolution(parameters, screenResolution);
    Log.d(TAG, "Camera resolution: " + screenResolution);
  }

  最后将PlanarYUVLuminanceSource.java中的renderCroppedGreyscaleBitmap方法改为:

public Bitmap renderCroppedGreyscaleBitmap() {
        int width = getWidth();
        int height = getHeight();
        int[] pixels = new int[width * height];
        byte[] yuv = yuvData;
        int inputOffset = top * dataHeight + left;

        for (int y = 0; y < height; y++) {
          int outputOffset = y * width;
          for (int x = 0; x < width; x++) {
            int grey = yuv[inputOffset + x] & 0xff;
            pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
          }
          inputOffset += dataHeight;
        }

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
        return bitmap;
      }

  3)取景框的绘制

  取景框的绘制可以参照这篇博文:http://www.cnblogs.com/forrestsun/archive/2012/11/06/2757005.html

  4)闪光灯的开启和关闭

  如果需要开启和关闭闪光灯,在CameraManager.java中添加2个方法:

  public void openLight()   //打开闪光灯
    {
        if(camera!=null)
        {
             Parameters parameter=camera.getParameters();
             parameter.setFlashMode(Parameters.FLASH_MODE_TORCH);
             camera.setParameters(parameter);
        }
    }

    public void closeLight()  //关闭闪光灯
    {
        if(camera!=null)
        {
             Parameters parameter=camera.getParameters();
             parameter.setFlashMode(Parameters.FLASH_MODE_OFF);
             camera.setParameters(parameter);
        }
    }

  然后在CaptureActivity中的initCamera方法中打开闪光灯:

CameraManager.get().openDriver(surfaceHolder);
CameraManager.get().openLight();   //开闪光灯

  在需要的地方关闭闪光灯即可。

  5)连续扫描问题

  如果在识别二维码成功之后,需要连续多次扫描二维码,只需在扫描完成之后添加代码(在CaptureActivity的dandleDecode方法中添加):

if(handler!=null)     //实现连续扫描
   handler.restartPreviewAndDecode();

  比如:

public void handleDecode(Result result, Bitmap barcode) {
        inactivityTimer.onActivity();
        playBeepSoundAndVibrate();
        final String resultString = result.getText();
        //FIXME
        if (resultString.equals("")) {
            Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
        }else {
//            System.out.println("Result:"+resultString);
            /*Intent resultIntent = new Intent();
            Bundle bundle = new Bundle();
            bundle.putString("result", resultString);
            resultIntent.putExtras(bundle);
            this.setResult(RESULT_OK, resultIntent);*/

            AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create();
            resutlDialog.setTitle("扫描结果");
            resutlDialog.setMessage(resultString);
            resutlDialog.setButton(AlertDialog.BUTTON_POSITIVE, "打开链接", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    dialog.dismiss();
                    if(!isLegalUrl(resultString))   //如果url不合法
                    {
                        Toast.makeText(getApplicationContext(), "该链接不是合法的URL", Toast.LENGTH_SHORT).show();
                        if(handler!=null)     //实现连续扫描
                            handler.restartPreviewAndDecode();
                        return;
                    }
                    Intent intent = new Intent();      //打开链接
                    intent.setAction("android.intent.action.VIEW");
                    Uri content_url = Uri.parse(resultString);
                    intent.setData(content_url);
                    startActivity(intent);
                }
            });

            resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    dialog.dismiss();
                    if(handler!=null)     //实现连续扫描
                        handler.restartPreviewAndDecode();
                }
            });
            resutlDialog.show();
        }
        //CaptureActivity.this.finish();
    }

  6)识别完成之后的震动。

  如果需要取消二维码识别之后的震动,只需要在CaptureActivity类的onResume方法中将vibrate 设置为false即可。

  7)修改取景框距屏幕顶部位置

  如果需要修改取景框距屏幕顶部位置,只需要修改CameraManager.java的getFramingRect方法,在getFramingRect方法中,

int topOffset = (screenResolution.y - height) / 2

  这句是控制取景框到屏幕顶部的距离,若需要减小距屏幕顶部的距离,只需要将分母变大即可。

  8)取景框下方提示文字的绘制。

  在ViewfinderView.java的 Collection<ResultPoint> currentPossible = possibleResultPoints前面加入以下代码:

  

TextPaint textPaint = new TextPaint();           textPaint.setARGB(0xFF, 0xFF, 0xFF,0xFF);   //字体颜色
textPaint.setTextSize(TEXT_SIZE * density);
textPaint.setAntiAlias(true);  //设置抗锯齿,否则字迹会很模糊StaticLayout layout = new StaticLayout(getResources().getString(R.string.scan_text),textPaint,
                    frame.right-frame.left,Alignment.ALIGN_NORMAL,1.0F,0.0F,true);
canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density)); //绘制起始位置
layout.draw(canvas);

  这里解释一下,textPaint.setAntiAlias(true); 是设置为抗锯齿,否则字体会很模糊。StaticLayout的第一个参数就是要绘制的字符串,第二个是画笔,第三个参数是设置每一行的宽度,即超过该宽度就换行,第四个是对齐方式。

  canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density))的参数是绘制字符串的起始位置。

  9)乱码问题

  在扫描一部分二维码时会出现乱码的情况,是由于编码的问题,可以参照这篇文章:

  http://www.2cto.com/kf/201209/152900.html

  如果不想编译core包,我的工程源码中的core包是已经改好了的,可以识别GBK二维码,需要的朋友可以直接拿去用。

  关于zxing 二维码识别的问题暂时就讨论这么多,有兴趣的朋友可以读源码深入研究一下。

时间: 2024-12-19 12:58:31

【转】Android平台下利用zxing实现二维码开发的相关文章

Android平台下利用zxing实现二维码开发

http://www.cnblogs.com/dolphin0520/p/3355728.html 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing进行二维码开发. 1.如何将zxing的Android源码导入工程. 在导入zxing的android源码之前,先去官方下载zxing的源码:http

android 利用ZXING扫描二维码代码分析

之前给公司做了一个摄影相关的应用,现在要添加二维码扫描的功能,网上找资料后,虽然已经成功集成到app里面,但是总感觉心里没底儿.所以趁这段时间不是很忙,总结一下. 首先是启动扫描的UI类: 1,Activity启动,当然是onCreate方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

Java利用Zxing生成二维码

Zxing是Google提供的关于条码(一维码.二维码)的解析工具,提供了二维码的生成与解析的方法,现在我简单介绍一下使用Java利用Zxing生成与解析二维码 1.二维码的生成 1.1 将Zxing-core.jar 包加入到classpath下. 1.2 二维码的生成需要借助MatrixToImageWriter类,该类是由Google提供的,可以将该类拷贝到源码中,这里我将该类的源码贴上,可以直接使用. import com.google.zxing.common.BitMatrix; i

利用zxing生成二维码

使用zxing类库可以很容易生成二维码QRCode,主要代码如下: private Bitmap createQRCode(String str,int width,int height) { Bitmap bmp=null; if(str.equals(""))return null; try { MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); Map<EncodeHintType, Object&g

Android平台,利用zxing库开发扫码功能

Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing进行二维码开发. 1.如何将zxing的Android源码导入工程. 在导入zxing的android源码之前,先去官方下载zxing的源码:http://code.google.com/p/zxin

完美解决Android使用Zxing扫描二维码改成竖屏后,后面的预览画面出现了拉伸,扭曲的情况

完美解决解决Android使用Zxing扫描二维码改成竖屏后,后面的预览画面出现了拉伸,扭曲的情况 第一步:找到com.zxing.camera包下的CameraConfigurationManager.java文件中的void initFromCameraParameters(Camera camera)方法 第二步:在 Log.d(TAG, "Screen resolution: " + screenResolution);后加上如下的代码 Point screenResoluti

Android 基于google Zxing实现二维码的生成,识别和长按识别的效果

最近项目用到了二维码的生成与识别,之前没有接触这块,然后就上网搜了搜,发现有好多这方面的资源,特别是google Zxing对二维码的封装,实现的已经不错了,可以直接拿过来引用,下载了他们的源码后,只做了少少的改动,就是在Demo中增加了长按识别的功能,网上虽然也有长按识别的Demo,但好多下载下来却无法运行,然后总结了一下,加在了下面的Demo中. 如图所示,引用时直接把用红色圈起来的包放在你项目所对应的文件夹下,当然一些资源文件,比如string.xml里项目的引用你自己添加上就是 当然别忘

2.下拉刷新、二维码

下拉刷新 pulltorefresh 支持listview,webview,gridview,expandableListview众多控件 1,lv_list = refreshListView.getRefreshableView();//需要拿到refreshListView这个组合控件里面的具体的listview,才可以设置adapter 2.refreshListView.setRefreshing();//开始加载数据的时候.应该显示.正在刷新 3.refreshListView.on

ZXing生成二维码和带logo的二维码,模仿微信生成二维码效果

首先说下,QRCode是日本人开发的,ZXing是google开发,barcode4j也是老美开发的,barcode4j对一维条形码处理的很好,而且支持的格式很多,当然也可以对二维码进行处理,效果个人感觉没有前两种好;ZXing对j2me,j2se,还有Android等支持也比较好,如果你是搞Android的或以后准备走Android,建议还是用zxing的比较好,毕竟都一个母亲(goole)生的,QRCode就不用说了吧,虽说技术无国界,但是国人还是有点.... 好,言归正传,java用ZXi