zxing活动扫描嵌入的二维码

时间:2015-02-03 07:20:39

标签: java android zxing qr-code

在这里,我期待zxing-core有一些可以从其他应用程序启动的活动,这就是他们如何集成而无需启动一些第三方应用来扫描QR码

我尝试添加

compile 'com.google.zxing:core:3.1.0'
compile 'com.google.zxing:android-integration:3.1.0'

build.gradle文件我可以看到这些罐子是导入的,但是我没有看到这个类存在于jar中

com.google.zxing.client.android.CaptureActivity

并在旁边的罐子里

$ unzip -l ./modules-2/files-2.1/com.google.zxing/core/3.1.0/908e18674f895e3e1fa8f4a954b8c637a23d2801/core-3.1.0.jar | grep -i 'activity'

我没有看到核心模块Here

的类部分

我做错了什么?

Suppressed: java.lang.ClassNotFoundException: com.google.zxing.client.android.CaptureActivity
        at java.lang.Class.classForName(Native Method)
        at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
        at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:504)

我正在使用这种方法:

Intent intent = new Intent("com.google.zxing.client.android.SCAN");
                intent.putExtra("com.google.zxing.client.android.SCAN.SCAN_MODE", "QR_CODE_MODE");
                startActivityForResult(intent, 0);

只是为了确保:我的期望是,当用户想要使用嵌入式扫描程序而不是将扫描委派给其他应用程序时,活动是用户库的一部分

1 个答案:

答案 0 :(得分:8)

这是一个非常长的答案所以仔细阅读以确保您不会遗漏任何内容。

您可以在我的Github Repo

上查看示例项目

好的,首先你需要将核心zxing库jar添加到项目目录中的libs文件夹中。您还必须为zxing类创建一个包。见下文

foo.bar.yourpackagename.zxing
foo.bar.yourpackagename.zxing.view

在第一个包foo.bar.yourpackagename.zxing中添加以下类


<强> CameraManager.java

import android.app.Activity;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.Log;
import android.view.Surface;
import com.google.zxing.PlanarYUVLuminanceSource;

/**
 * Camera manager
 */
public class CameraManager {
    /**
     * Fraction of bounds size in view
     */
    private static final double BOUNDS_FRACTION = 0.6;
    /**
     * Fraction of height of bounds in view
     */
    private static final double VERTICAL_HEIGHT_FRACTION = 0.3;

    /**
     * Camera instance
     */
    private Camera camera;
    /**
     * Id of camera instance
     */
    private int cameraId;
    /**
     * Current orientation of camera
     * Possible values : 0, 90, 180, 270
     */
    private int orientation;

    public CameraManager() {
        this.camera = getCameraInstance();
    }

    /**
     * Getter for camera
     *
     * @return camera instance, if it has been initialized
     */
    public Camera getCamera() {
        return camera;
    }

    /**
     * Starts preview of camera, if it has been initialized
     */
    public synchronized void startPreview() {
        if (camera != null) {
            camera.startPreview();
        }
    }

    /**
     * Stops preview of camera, if it has been initialized
     */
    public synchronized void stopPreview() {
        if (camera != null) {
            camera.stopPreview();
        }
    }

    /**
     * Release camera, if it has been initialized
     */
    public synchronized void release() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }

    /**
     * @return if camera has been initialized<br/>( <code>camera != null</code> )
     */
    public synchronized boolean hasCamera() {
        return camera != null;
    }

    /**
     * @return bounding rect for ui
     */
    public final synchronized Rect getBoundingRectUi(int uiWidth, int uiHeight) {
        double heightFraction = BOUNDS_FRACTION;
        double widthFraction = BOUNDS_FRACTION;
        if (orientation == 90 || orientation == 270) {
            heightFraction = VERTICAL_HEIGHT_FRACTION;
        }

        Log.d("CameraManager", "dimsUI[" + uiWidth + ", " + uiHeight + "]");

        int height = (int) (uiHeight * heightFraction);
        int width = (int) (uiWidth * widthFraction);
        int left = (int) (uiWidth * ((1 - widthFraction) / 2));
        int top = (int) (uiHeight * ((1 - heightFraction) / 2));
        int right = left + width;
        int bottom = top + height;

        return new Rect(left, top, right, bottom);
    }

    /**
     * @return bounding rect for camera
     */
    public final synchronized Rect getBoundingRect() {
        if (camera != null) {
            Camera.Size previewSize = camera.getParameters().getPreviewSize();
            int previewHeight = previewSize.height;
            int previewWidth = previewSize.width;

            double heightFraction = BOUNDS_FRACTION;
            double widthFraction = BOUNDS_FRACTION;
            if (orientation == 90 || orientation == 270) {
                widthFraction = VERTICAL_HEIGHT_FRACTION;
            }
            Log.d("CameraManager", "dimsPrev[" + previewWidth + ", " + previewHeight + "]");

            int height = (int) (previewHeight * heightFraction);
            int width = (int) (previewWidth * widthFraction);
            int left = (int) (previewWidth * ((1 - widthFraction) / 2));
            int top = (int) (previewHeight * ((1 - heightFraction) / 2));
            int right = left + width;
            int bottom = top + height;

            return new Rect(left, top, right, bottom);
        }
        return null;
    }

    /**
     * executes <br/> <code>camera.setOneShotPreviewCallback(callback)</code> if <br/>
     * <code>camera != null</code>
     * @param callback callback to provide
     */
    public synchronized void requestNextFrame(Camera.PreviewCallback callback) {
        if (camera != null) {
            camera.setOneShotPreviewCallback(callback);
        }
    }

    /**
     * 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 synchronized PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height, Rect boundingRect) {
        switch (orientation) {
            case 0:
                //data = flip(data);
                break;
            case 90:
                rotate90(data, width, height);
                return new PlanarYUVLuminanceSource(data, height, width, boundingRect.top, boundingRect.left,
                        boundingRect.height(), boundingRect.width(), false);

            case 180:
                break;
            case 270:
                rotate90(data, width, height);
                break;
        }

        return new PlanarYUVLuminanceSource(data, width, height, boundingRect.left, boundingRect.top,
                boundingRect.width(), boundingRect.height(), false);
    }

    /**
     * Rotates image data
     * @param data raw image data
     * @param width width of image
     * @param height height of image
     */
    public void rotate90(byte[] data, int width, int height) {
        int length = height * width;
        int lengthDec = length - 1;
        int i = 0;
        do {
            int k = (i * height) % lengthDec;
            while (k > i) k = (height * k) % lengthDec;
            if (k != i) swap(data, k, i);
        } while (++i <= (length - 2));
    }

    /**
     * Sets camera display orientation depending on current activity orientation
     * @param activity activity, which holds camera preview
     */
    public void setCameraDisplayOrientation(Activity activity) {
        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360; // compensate the mirror
        } else { // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        orientation = result;
        camera.setDisplayOrientation(result);
    }


    /**
     * A safe way to get an instance of the Camera object.
     */
    private Camera getCameraInstance() {
        Camera c = null;
        try {
            cameraId = 0;
            c = Camera.open(); // attempt to get a Camera instance
            Camera.Parameters p = c.getParameters();
            p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
            c.setParameters(p);
        } catch (Exception e) {
            Log.e(CameraManager.class.getSimpleName(), "Camera error", e);
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }

    /**
     * Swaps two elements in array
     * @param data array
     * @param k first element to swap
     * @param i second element to swap
     */
    private static void swap(byte[] data, int k, int i) {
        byte temp = data[k];
        data[k] = data[i];
        data[i] = temp;
    }
}

<强> CaptureHandler.java

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;

/**
 */
public class CaptureHandler extends Handler {
    public static final String DECODED_DATA = "decoded_data";
    private CameraManager cameraManager;
    private Context context;
    private OnDecodedCallback callback;

    public CaptureHandler(CameraManager cameraManager, Context context, OnDecodedCallback callback) {
        this.cameraManager = cameraManager;
        this.context = context;
        this.callback = callback;
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case R.id.decoded:
                String data = msg.getData().getString(DECODED_DATA);
                Toast.makeText(context, data, Toast.LENGTH_LONG).show();
                if (callback != null){
                    callback.onDecoded(data);
                }
                break;
            case R.id.decode_failed:
                //getting new frame
                cameraManager.requestNextFrame(new PreviewCallback(this, cameraManager));
                break;
        }
    }

    public static interface OnDecodedCallback {
        void onDecoded(String decodedData);
    }
}

<强> PreviewCallback.java

import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.google.zxing.*;
import com.google.zxing.common.HybridBinarizer;
import foo.bar.yourpackagename.R;

/**
 * Camera preview callback
 */
public class PreviewCallback implements Camera.PreviewCallback {
    private static final String TAG = PreviewCallback.class.getSimpleName();
    /**
     * Xzing multi format reader
     */
    private final MultiFormatReader multiFormatReader = new MultiFormatReader();
    /**
     * Handler to send messages
     *
     * @see CaptureHandler
     */
    private Handler handler;
    /**
     * Camera manager
     */
    private CameraManager cameraManager;

    public PreviewCallback(Handler handler, CameraManager cameraManager) {
        this.handler = handler;
        this.cameraManager = cameraManager;
    }

    @Override
    public void onPreviewFrame(byte[] bytes, Camera camera) {
        try {
            Camera.Size previewSize = camera.getParameters().getPreviewSize();
            new DecodeAsyncTask(previewSize.width, previewSize.height).execute(bytes);
        } catch (Exception e) {
            //Random errors from zxing
        }
    }

    /**
     * Asynchronous task for decoding and finding barcode
     */
    private class DecodeAsyncTask extends AsyncTask<byte[], Void, Result> {
        /**
         * Width of image
         */
        private int width;
        /**
         * Height of image
         */
        private int height;

        /**
         * @param width  Width of image
         * @param height Height of image
         */
        private DecodeAsyncTask(int width, int height) {
            this.width = width;
            this.height = height;
        }

        @Override
        protected void onPostExecute(Result result) {
            if (result != null) {
                Log.i(TAG, "Decode success.");
                if (handler != null) {
                    Message message = Message.obtain(handler, R.id.decoded);
                    Bundle bundle = new Bundle();
                    bundle.putString(CaptureHandler.DECODED_DATA, result.toString());
                    message.setData(bundle);
                    message.sendToTarget();
                }
            } else {
                Log.i(TAG, "Decode fail.");
                if (handler != null) {
                    Message message = Message.obtain(handler, R.id.decode_failed);
                    message.sendToTarget();
                }
            }
        }

        @Override
        protected Result doInBackground(byte[]... datas) {
            if (!cameraManager.hasCamera()) {
                return null;
            }
            Result rawResult = null;
            final PlanarYUVLuminanceSource source =
                    cameraManager.buildLuminanceSource(datas[0], width,
                            height, cameraManager.getBoundingRect());
            if (source != null) {
                BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
                try {
                    rawResult = multiFormatReader.decodeWithState(bitmap);
                } catch (Exception re) {
                    Log.e(TAG, "ERROR", re);
                } finally {
                    multiFormatReader.reset();
                }
            }

            return rawResult;
        }
    }
}

<强> 然后 将这三个类添加到foo.bar.yourpackagename.zxing包后,您必须将以下类添加到foo.bar.yourpackagename.zxing.view


<强> BoundingView.java

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import foo.bar.yourpackagename.zxing.CameraManager;

/**
 * View for displaying bounds for active camera region
 */
public class BoundingView extends View {
    /**
     * Camera manager
     */
    private CameraManager cameraManager;

    public BoundingView(Context context) {
        super(context);
    }

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

    /**
     * Sets camera manger
     * @param cameraManager
     */
    public void setCameraManager(CameraManager cameraManager) {
        this.cameraManager = cameraManager;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (cameraManager != null) {
             Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
             paint.setColor(Color.parseColor("#00000000"));

//             Rect boundingRect = cameraManager.getBoundingRectUi(canvas.getWidth(), canvas.getHeight());
             Rect boundingRect = cameraManager.getBoundingRect();
             canvas.drawRect(boundingRect, paint);

             paint = new Paint(Paint.ANTI_ALIAS_FLAG);
             paint.setColor(Color.parseColor("#44FF0000"));
             paint.setStrokeWidth(5);
             int incY = (canvas.getHeight() - boundingRect.height())/2;
             int incX = (canvas.getWidth() - boundingRect.width())/2;

             int y = (boundingRect.height()/2) + incY;
             int x = boundingRect.width() + incX;
             canvas.drawLine(incX, y, x, y, paint);
             canvas.drawRect(new Rect(incX, boundingRect.top, x, boundingRect.bottom), paint);
        }
    }
}

<强> CameraPreviewView.java

import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import foo.bar.yourpackagename.zxing.CameraManager;

import java.io.IOException;

/**
 * Camera preview view. Shows camera preview data
 */
public class CameraPreviewView extends SurfaceView implements SurfaceHolder.Callback {
    private static final String TAG = CameraPreviewView.class.getSimpleName();

    /**
     * Surface holder for camera preview data
     */
    private SurfaceHolder surfaceHolder;
    /**
     * Camera manager
     */
    private CameraManager cameraManager;

    public CameraPreviewView(Context context) {
        super(context);

        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public CameraPreviewView(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);

        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    /**
     * Setter for camera manager
     * @param cameraManager camera manager to set
     */
    public void setCameraManager(CameraManager cameraManager) {
        this.cameraManager = cameraManager;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            cameraManager.setCameraDisplayOrientation((Activity) getContext());
            cameraManager.getCamera().setPreviewDisplay(holder);
            cameraManager.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // empty. Taking care of releasing the Camera preview in activity.
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (surfaceHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            cameraManager.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            cameraManager.setCameraDisplayOrientation((Activity) getContext());
            cameraManager.getCamera().setPreviewDisplay(surfaceHolder);
            cameraManager.startPreview();

        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

完成后,您可以从实施Activity开始。 请注意,某些类要求您为某些州创建ID,例如

R.id.decode_failed

您需要在应用内的资源文件夹中创建这些ID。接下来,我们可以进入实施Activity


<强> CaptureActivity.java

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.RelativeLayout;

import foo.bar.yourpackagename.R;
import foo.bar.yourpackagename.zxing.CameraManager;
import foo.bar.yourpackagename.zxing.CaptureHandler;
import foo.bar.yourpackagename.zxing.PreviewCallback;
import foo.bar.yourpackagename.zxing.view.BoundingView;
import foo.bar.yourpackagename.zxing.view.CameraPreviewView;

/**
 * Capture activity (camera barcode activity)
 */
public class CaptureActivity extends Activity {
    public static String PUBLIC_STATIC_STRING_IDENTIFIER;

    /**
     * Camera preview view
     */
    private CameraPreviewView cameraPreview;
    /**
     * Camera manager
     */
    private CameraManager cameraManager;

    /**
     * Capture handler
     */
    private Handler captureHandler;

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

        setContentView(getContentView());

        initializeCamera();
        initializeCameraPreview();
        initializeBoundingView();
    }

    private View getContentView() {
        RelativeLayout contentView = new RelativeLayout(this);
        contentView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        contentView.addView(getBoundingView());
        contentView.addView(getCameraPreviewView());

        return contentView;
    }

    private CameraPreviewView getCameraPreviewView() {
        //This is the camera SurfaceView
        CameraPreviewView camPreview = new CameraPreviewView(this);
        camPreview.setId(R.id.camera_preview);
        camPreview.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        return camPreview;
    }

    private BoundingView getBoundingView() {
        //Displays the bounding content
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        params.addRule(RelativeLayout.CENTER_IN_PARENT);

        BoundingView boundingView = new BoundingView(this);
        boundingView.setId(R.id.bounding_view);
        boundingView.setLayoutParams(params);

        return boundingView;
    }

    private void initializeCamera() {
        // Create an instance of Camera
        cameraManager = new CameraManager();
        captureHandler = new CaptureHandler(cameraManager, this, new OnDecoded());

        //requesting next frame for decoding
        cameraManager.requestNextFrame(new PreviewCallback(captureHandler, cameraManager));
    }

    private void initializeCameraPreview() {
        // Create our Preview view and set it as the content of our activity.
        cameraPreview = (CameraPreviewView) findViewById(R.id.camera_preview);
        cameraPreview.setCameraManager(cameraManager);
    }

    private void initializeBoundingView() {
        //Set the cameraManager to the bounding view
        ((BoundingView) findViewById(R.id.bounding_view)).setCameraManager(cameraManager);
    }

    @Override
    protected void onPause() {
        super.onPause();

        //We don't want the cameraManager to take up unneeded 
        //resources so we release it from memory onPause
        cameraManager.release();
    }

    /**
     * This handles the decoded content from the Zxing framework. As
     * soon as the framework has completed the decoding process the CaptureHandler
     * will pass it back via this listener for you to handle it further.
     *
     */
    private class OnDecoded implements CaptureHandler.OnDecodedCallback {
        @Override
        public void onDecoded(String decodedData) {
            Intent resultIntent = new Intent();
            resultIntent.putExtra(PUBLIC_STATIC_STRING_IDENTIFIER, decodedData);
            setResult(Activity.RESULT_OK, resultIntent);
            finish();
        }
    }
}

要开始活动,您只需致电

startActivity(new Intent(this, CaptureActivity.class));

还要记住添加以下权限

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

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />

您的Activity

<activity
    android:name=".CaptureActivity"
    android:configChanges="orientation|keyboardHidden"
    android:screenOrientation="landscape"
    android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
    android:windowSoftInputMode="stateAlwaysHidden" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <intent-filter>
        <action android:name="com.google.zxing.client.android.SCAN" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Manifest

如果您正确实现了所有这些类,那么您应该能够开始扫描条形码。