Android二维码扫描.(生成,扫描,相册,闪光灯)

项目用到了二维码的相关功能,作为菜鸟我,自己写,不可能存在的,无奈,只好去百度,找找有没有好用的第三方的,拿来用一用,实现实现功能呗。
地址一:https://github.com/bingoogolapple/BGAQRCode-Android
   这个大家一百度就能百度的到,我也用项目了,但是测试的过程中,发现bug了,长时间扫描不出来,会出来一串随机数字,github上众多用户都在说这个问题,作者好像并没有解决这个问题。除了扫描结果有点不尽人意之外,其他都挺好用的。如果真的想用,那就判断下结果吧,如果是纯数字,那就重新扫描,但是如果你的二维码是纯数字生成的,那就完蛋咯,只能换个库了。
地址二:https://github.com/mylhyl/Android-Zxing 
   这个我写了demo,试了试,扫描和相册是可以的.
地址三:https://github.com/journeyapps/zxing-android-embedded
   我用的这个,下载下来,界面不是很美观,还有点晕乎乎的,但是仔细看,仔细找,你会发现,扫描速度堪比微信啊,贼快贼快的,不过只有扫描和闪光灯功能,相册识别和生成的功能,自己加上吧。
地址四:https://www.imooc.com/article/20971
   嘿嘿,根据这链接来做的。这个链接是根据"地址三"来自定义布局的做出来的.(我也是在这个链接里看到“地址二”的,[皱眉][皱眉])

大家先看看地址三和四,然后直接复制下面的代码,可以实现功能[吼吼][吼吼]
-------------------------------------上图上代码-------------------------------------
图:
扫描.gif

相册.gif
代码:

build.gradle:

 implementation('com.journeyapps:zxing-android-embedded:3.6.0') { transitive = false }
 implementation 'com.google.zxing:core:3.3.0'

上面是github提供的,但是我没把 zxing:core 下载下来,我老大下载的jar包,放进项目里的,只要能编译过去,咋的都行,管他哪儿来的嘛.还有,关于相册识别二维码,这个是个耗时操作,需要在子线程,我们的项目使用rxjava来转换线程的,需要依赖的哦implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'implementation 'io.reactivex.rxjava2:rxjava:2.1.5',用异步任务也可以,但是注意下,内存泄露.

HomeFragment
ps:这里是入口,对相机是否可以使用进行了判断,百度来的,[羞羞].因为我们的targetSdkVersion=22.直接在清单文件设置权限就好了,但是要是手动关闭了相机权限,还是能进去二维码界面的,只是相机不能使用,一片黢黑黢黑的.

 @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.home_scan:
                if (!cameraIsCanUse()) {
                    toastShort("请开启相机权限");
                } else {
                    startActivity(new Intent(getActivity(), QRCodeActivity.class));
                }
                break;
  }
      public boolean cameraIsCanUse() {//百度判断相机的
        boolean isCanUse = true;
        Camera mCamera = null;
        try {
            mCamera = Camera.open();
            Camera.Parameters mParameters = mCamera.getParameters(); //针对魅族手机
            mCamera.setParameters(mParameters);
        } catch (Exception e) {
            isCanUse = false;
        }

        if (mCamera != null) {
            try {
                mCamera.release();
            } catch (Exception e) {
                e.printStackTrace();
                return isCanUse;
            }
        }
        return isCanUse;
    }

QRCodeActivity.Java
ps:二维码界面,这里是二维码的主界面.照葫芦,画瓢,写写呗.

二维码界面图.jpg

public class QRCodeActivity extends BaseToolbarActivity {
    private DecoratedBarcodeView barcodeView;
    private boolean isLight = false;

    @NonNull
    @Override
    protected int getLayoutId() {
        return R.layout.activity_qrcode;
    }

    @Override
    protected void initIntentData(Intent intent) {

    }

    @Override
    protected void initViews() {
        setTitleWithBack("扫一扫");
        barcodeView = findViewById(R.id.barcode_scanner);
        Collection<BarcodeFormat> formats = Arrays.asList(BarcodeFormat.QR_CODE, BarcodeFormat.CODE_39);
        barcodeView.getBarcodeView().setDecoderFactory(new DefaultDecoderFactory(formats));
        barcodeView.decodeContinuous(callback);
        addOnClickListeners(R.id.tv_zxing_gallery, R.id.tv_zxing_flashlight);
    }

    @Override
    public void onClick(View v) {
        super.onClick(v);
        switch (v.getId()) {
            case R.id.tv_zxing_gallery:
                EasyPhotos.createAlbum(this, true, GlideEngine.getInstance())
                        .setFileProviderAuthority(Constants.fileProvider)
                        .setCount(1)
                        .start(Constants.PIC);
                break;
            case R.id.tv_zxing_flashlight:
                if (!isLight) {
                    barcodeView.setTorchOn();
                    isLight = true;
                } else {
                    isLight = false;
                    barcodeView.setTorchOff();
                    break;
                }
        }
    }

    private BarcodeCallback callback = new BarcodeCallback() {
        @Override
        public void barcodeResult(BarcodeResult result) {
            if (result.getText() == null) {
                // Prevent duplicate scans
                return;
            }
            barcodeView.setStatusText(result.getText());
            parseQRCode(result.getText(), new Bundle());
        }

        @Override
        public void possibleResultPoints(List<ResultPoint> resultPoints) {
        }
    };

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                case Constants.PIC:
                    if (data != null) {
                        showProgressDialog();
                        List<String> pathList = data.getStringArrayListExtra(EasyPhotos.RESULT_PATHS);
                        final String picturePath = pathList.get(0).toString();
                        barcodeView.pause();
                        Observable.create(new ObservableOnSubscribe<Result>() {
                            @Override
                            public void subscribe(ObservableEmitter<Result> e) throws Exception {
                                Result result = PicUtils.scanningImage(picturePath);
                                e.onNext(result);
                            }
                        }).subscribeOn(Schedulers.io())
                                .observeOn(AndroidSchedulers.mainThread())
                                .compose(this.<Result>bindToLifecycle())
                                .subscribe(new Observer<Result>() {
                                    @Override
                                    public void onSubscribe(Disposable d) {

                                    }

                                    @Override
                                    public void onNext(Result result) {
                                        onComplete();
                                        parseQRCode(result.getText(), new Bundle());
                                    }

                                    @Override
                                    public void onError(Throwable e) {
                                        barcodeView.resume();
                                        onComplete();
                                        ToastUtils.showToast("照片中未识别到二维码");
                                    }

                                    @Override
                                    public void onComplete() {
                                        hideProgressDialog();
                                    }
                                });
                    } else {
                        ToastUtils.showToast("获取图片失败");
                    }
                    break;
            }
        }

    }

    //解析二维码中的字符串
    private void parseQRCode(String result, Bundle bundle) {
        LogUtils.e("result is =========" + result);
        hideProgressDialog();
        if (TextUtils.isEmpty(result)) {
            ToastUtils.showToast("照片中未识别到二维码");
            return;
        }
        if (NetWorkUtil.isNetWorkUrl(result)) {
            startActivity(new Intent(this, WebViewActivity.class).putExtra("url", result));
            finish();
            return;
        } else {
            bundle.putString("result", result);
            startActivity(new Intent(this, ResultActivity.class).putExtras(bundle));
        }

    }



    @Override
    protected void getNetWorkData() {

    }

    @Override
    public Object newPresenter() {
        return null;
    }

    @Override
    protected void onResume() {
        super.onResume();
        barcodeView.resume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        barcodeView.pause();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return barcodeView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
    }

activity_qrcode.xml
ps:主界面的布局,直接复制就好

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <com.journeyapps.barcodescanner.DecoratedBarcodeView
        android:id="@+id/barcode_scanner"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:zxing_scanner_layout="@layout/scaner">

    </com.journeyapps.barcodescanner.DecoratedBarcodeView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal"
        android:paddingBottom="@dimen/dpValue35">

        <View
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1" />

        <TextView
            android:id="@+id/tv_zxing_gallery"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawablePadding="@dimen/dpValue12"
            android:drawableTop="@drawable/zxing_gallery"
            android:gravity="center"
            android:paddingLeft="@dimen/dpValue10"
            android:paddingRight="@dimen/dpValue10"
            android:text="相册"
            android:textColor="@color/white"
            android:textSize="@dimen/textSizeValue12" />

        <View
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="2" />

        <TextView
            android:id="@+id/tv_zxing_flashlight"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawablePadding="@dimen/dpValue12"
            android:drawableTop="@drawable/zxing_flashlight"
            android:gravity="center"
            android:paddingLeft="@dimen/dpValue10"
            android:paddingRight="@dimen/dpValue10"
            android:text="手电筒"
            android:textColor="@color/white"
            android:textSize="@dimen/textSizeValue12" />


        <View
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1" />
    </LinearLayout>

</RelativeLayout>

scaner.xml
ps:不要改这里控件的id,重写的第三方控件,改了就找不到了,直接复制就好.

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <com.journeyapps.barcodescanner.BarcodeView
        android:id="@+id/zxing_barcode_surface"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:zxing_framing_rect_height="258dp"
        app:zxing_framing_rect_width="258dp" />

    <com.yangsu.chaodao.view.ZxingViewFinderView
        android:id="@+id/zxing_viewfinder_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:scannerBoundColor="@color/white"
        app:scannerBoundCornerHeight="24dp"
        app:scannerBoundCornerWith="1.5dp"
        app:scannerBoundWidth="0.5dp"
        app:scannerLaserResId="@drawable/scan_icon_scanline"
        app:scannerTipText="将二维码图片放到取景框内即可自动扫描"
        app:scannerTipTextColor="@color/white"
        app:scannerTipTextGravity="false"
        app:scannerTipTextMargin="43dp"
        app:scannerTipTextSize="14sp"
        app:zxing_possible_result_points="@color/zxing_custom_possible_result_points"
        app:zxing_result_view="@color/zxing_custom_result_view"
        app:zxing_viewfinder_laser="@color/zxing_custom_viewfinder_laser"
        app:zxing_viewfinder_mask="@color/zxing_custom_viewfinder_mask" />
</merge>

ZxingViewFinderView.Java
ps:这个是自定义的自定义的扫描框,复制粘贴即可,copy地址四的.

public class ZxingViewFinderView extends ViewfinderView {
    private int scannerBoundColor;
    private float scannerBoundWidth; // viewfinder width
    private int scannerBoundCornerColor; // viewfinder corner color
    private float scannerBoundCornerWidth; // viewfinder corner width
    private float scannerBoundCornerHeight; // viewfinder corner height
    private int scannerLaserResId; // laser resource
    private String scannerTipText; // tip text
    private float scannerTipTextSize; // tip text size
    private int scannerTipTextColor; // tip text color
    private float scannerTipTextMargin; // tip text margin between viewfinder
    private boolean tipTextGravityBottom; // tip text gravity
    private Bitmap scannerLaserBitmap; // laser bitmap
    private int scannerLaserTop; // laser top position
    private final static int LASER_MOVE_DISTANCE_PER_UNIT_TIME = 10;
    private int LASER_MOVE_DIRECTION = 1;

    public ZxingViewFinderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Resources resources = getResources(); // Get attributes on view
        TypedArray attributes = getContext().obtainStyledAttributes(attrs, R.styleable.ZxingViewFinderView);
        scannerBoundColor = attributes.getColor(R.styleable.ZxingViewFinderView_scannerBoundColor, resources.getColor(R.color.white));
        scannerBoundWidth = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerBoundWidth, 0.5f);
        scannerBoundCornerColor = attributes.getColor(R.styleable.ZxingViewFinderView_scannerBoundCornerColor, resources.getColor(R.color.color_blue));
        scannerBoundCornerWidth = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerBoundCornerWith, 1.5f);
        scannerBoundCornerHeight = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerBoundCornerHeight, 24f);
        scannerLaserResId = attributes.getResourceId(R.styleable.ZxingViewFinderView_scannerLaserResId, 0);
        scannerTipText = attributes.getString(R.styleable.ZxingViewFinderView_scannerTipText);
        scannerTipTextSize = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerTipTextSize, 14f);
        scannerTipTextColor = attributes.getColor(R.styleable.ZxingViewFinderView_scannerTipTextColor, resources.getColor(R.color.white));
        scannerTipTextMargin = attributes.getDimension(R.styleable.ZxingViewFinderView_scannerTipTextMargin, 40f);
        tipTextGravityBottom = attributes.getBoolean(R.styleable.ZxingViewFinderView_scannerTipTextGravity, true);
        attributes.recycle();
    }

    @Override
    public void onDraw(Canvas canvas) {
        refreshSizes();
        if (framingRect == null || previewFramingRect == null) {
            return;
        }
        Rect frame = framingRect;
        Rect previewFrame = previewFramingRect;
        int width = canvas.getWidth();
        int height = canvas.getHeight(); // Draw the exterior
        drawExteriorDarkened(canvas, frame, width, height);
        if (resultBitmap != null) { // Draw the opaque result bitmap over the scanning rectangle
            paint.setAlpha(CURRENT_POINT_OPACITY);
            canvas.drawBitmap(resultBitmap, null, frame, paint);
        } else {
            drawFrameBound(canvas, frame);
            drawFrameCorner(canvas, frame);
            drawLaserLine(canvas, frame);
            drawTipText(canvas, frame, width);
            drawResultPoint(canvas, frame, previewFrame); // Request another update at the animation interval,
            // but only repaint the laser line, // not the entire viewfinder mask.
            postInvalidateDelayed(ANIMATION_DELAY, frame.left - POINT_SIZE, frame.top - POINT_SIZE, frame.right + POINT_SIZE, frame.bottom + POINT_SIZE);
        }
    }

    /**
     * Draw a "laser scanner" line to show decoding is active
     */
    private void drawLaserLine(Canvas canvas, Rect frame) {
        if (scannerLaserResId == 0) {
            paint.setColor(laserColor);
            paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
            scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
            int middle = frame.height() / 2 + frame.top;
            canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
        } else {
            if (scannerLaserBitmap == null) {
                scannerLaserBitmap = BitmapFactory.decodeResource(getResources(), scannerLaserResId);
            }
            if (scannerLaserBitmap != null) {
                int LaserHeight = scannerLaserBitmap.getHeight();
                if (scannerLaserTop < frame.top) {
                    scannerLaserTop = frame.top;
                    LASER_MOVE_DIRECTION = 1;
                }
                if (scannerLaserTop > frame.bottom - LaserHeight) {
                    scannerLaserTop = frame.bottom - LaserHeight;
                    LASER_MOVE_DIRECTION = -1;
                }
                Rect laserBitmapRect = new Rect(frame.left, scannerLaserTop, frame.right, scannerLaserTop + LaserHeight);
                canvas.drawBitmap(scannerLaserBitmap, null, laserBitmapRect, paint);
                scannerLaserTop = scannerLaserTop + LASER_MOVE_DISTANCE_PER_UNIT_TIME * LASER_MOVE_DIRECTION;
            }
        }
    }

    /**
     * Draw result points
     */
    private void drawResultPoint(Canvas canvas, Rect frame, Rect previewFrame) {
        float scaleX = frame.width() / (float) previewFrame.width();
        float scaleY = frame.height() / (float) previewFrame.height();
        List<ResultPoint> currentPossible = possibleResultPoints;
        List<ResultPoint> currentLast = lastPossibleResultPoints;
        int frameLeft = frame.left;
        int frameTop = frame.top;
        if (currentPossible.isEmpty()) {
            lastPossibleResultPoints = null;
        } else {
            possibleResultPoints = new ArrayList<>(5);
            lastPossibleResultPoints = currentPossible;
            paint.setAlpha(CURRENT_POINT_OPACITY);
            paint.setColor(resultPointColor);
            for (ResultPoint point : currentPossible) {
                canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), POINT_SIZE, paint);
            }
        }
        if (currentLast != null) {
            paint.setAlpha(CURRENT_POINT_OPACITY / 2);
            paint.setColor(resultPointColor);
            float radius = POINT_SIZE / 2.0f;
            for (ResultPoint point : currentLast) {
                canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), radius, paint);
            }
        }
    }

    /**
     * Draw tip text
     */
    private void drawTipText(Canvas canvas, Rect frame, int width) {
        if (TextUtils.isEmpty(scannerTipText)) {
            scannerTipText = "提示";
        }
        paint.setColor(scannerTipTextColor);
        paint.setTextSize(scannerTipTextSize);
        float textWidth = paint.measureText(scannerTipText);
        float x = (width - textWidth) / 2; //根据 drawTextGravityBottom 文字在扫描框上方还是下文,默认下方
        float y = tipTextGravityBottom ? frame.bottom + scannerTipTextMargin : frame.top - scannerTipTextMargin;
        canvas.drawText(scannerTipText, x, y, paint);
    }

    /**
     * Draw scanner frame bound * Note: draw inside frame
     */
    private void drawFrameBound(Canvas canvas, Rect frame) {
        if (scannerBoundWidth <= 0) {
            return;
        }
        paint.setColor(scannerBoundColor); // top
        canvas.drawRect(frame.left, frame.top, frame.right, frame.top + scannerBoundWidth, paint); // left
        canvas.drawRect(frame.left, frame.top, frame.left + scannerBoundWidth, frame.bottom, paint); // right
        canvas.drawRect(frame.right - scannerBoundWidth, frame.top, frame.right, frame.bottom, paint); // bottom
        canvas.drawRect(frame.left, frame.bottom - scannerBoundWidth, frame.right, frame.bottom, paint);
    }

    /**
     * Draw scanner frame corner
     */
    private void drawFrameCorner(Canvas canvas, Rect frame) {
        if (scannerBoundCornerWidth <= 0 || scannerBoundCornerHeight <= 0) {
            return;
        }
        paint.setColor(scannerBoundCornerColor); // left top
        canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.top - scannerBoundCornerWidth, frame.left + scannerBoundCornerHeight, frame.top, paint);
        canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.top - scannerBoundCornerWidth, frame.left, frame.top + scannerBoundCornerHeight, paint); // left bottom
        canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.bottom + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.left, frame.bottom + scannerBoundCornerWidth, paint);
        canvas.drawRect(frame.left - scannerBoundCornerWidth, frame.bottom, frame.left - scannerBoundCornerWidth + scannerBoundCornerHeight, frame.bottom + scannerBoundCornerWidth, paint); // right top
        canvas.drawRect(frame.right + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.top - scannerBoundCornerWidth, frame.right + scannerBoundCornerWidth, frame.top, paint);
        canvas.drawRect(frame.right, frame.top - scannerBoundCornerWidth, frame.right + scannerBoundCornerWidth, frame.top - scannerBoundCornerWidth + scannerBoundCornerHeight, paint); // right bottom
        canvas.drawRect(frame.right + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.bottom, frame.right + scannerBoundCornerWidth, frame.bottom + scannerBoundCornerWidth, paint);
        canvas.drawRect(frame.right, frame.bottom + scannerBoundCornerWidth - scannerBoundCornerHeight, frame.right + scannerBoundCornerWidth, frame.bottom + scannerBoundCornerWidth, paint);
    }

    /**
     * Draw the exterior (i.e. outside the framing rect) darkened
     */
    private void drawExteriorDarkened(Canvas canvas, Rect frame, int width, int height) {
        paint.setColor(resultBitmap != null ? resultColor : maskColor); //top
        canvas.drawRect(0, 0, width, frame.top, paint); //left
        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); //right
        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); //bottom
        canvas.drawRect(0, frame.bottom + 1, width, height, paint);
    }
}

attrs.xml
ps:这个不用多说了

        <attr name="scannerBoundColor" format="color" />
        <attr name="scannerBoundCornerColor" format="color" />
        <attr name="scannerTipTextColor" format="color" />
        <attr name="scannerBoundWidth" format="dimension" />
        <attr name="scannerBoundCornerWith" format="dimension" />
        <attr name="scannerBoundCornerHeight" format="dimension" />
        <attr name="scannerTipTextSize" format="dimension" />
        <attr name="scannerTipTextMargin" format="dimension" />

        <attr name="pstsTabBackground" format="reference" />
        <attr name="scannerLaserResId" format="integer" />
        <attr name="scannerTipText" format="string" />
        <attr name="scannerTipTextGravity" format="boolean" />
    </declare-styleable>

PicUtils.Java
处理图片,解析二维码图片使用

public class PicUtils {
    static Uri uritempFile;

    public static Uri getUritempFile() {
        return uritempFile;
    }

    public static Uri pathToUri(Context context, String path) {
        if (path != null) {
            path = Uri.decode(path);
            ContentResolver cr = context.getContentResolver();
            StringBuffer buff = new StringBuffer();
            buff.append("(")
                    .append(MediaStore.Images.ImageColumns.DATA)
                    .append("=")
                    .append("'" + path + "'")
                    .append(")");
            Cursor cur = cr.query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    new String[]{MediaStore.Images.ImageColumns._ID},
                    buff.toString(), null, null);
            int index = 0;
            for (cur.moveToFirst(); !cur.isAfterLast(); cur
                    .moveToNext()) {
                index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID);
                // set _id value
                index = cur.getInt(index);
            }
            if (index == 0) {
                //do nothing
            } else {
                Uri uri_temp = Uri
                        .parse("content://media/external/images/media/"
                                + index);
                return uri_temp;
            }
        }
        return null;
    }


    /**
     * 裁剪图片
     */

    public static void startPhotoZoom2(Activity context, Uri uri, int width, int height) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        // crop为true是设置在开启的intent中设置显示的view可以剪裁
        intent.putExtra("crop", "true");
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);

        // outputX,outputY 是剪裁图片的宽高
        if (width != 0 && height != 0) {
            intent.putExtra("outputX", 500);
            intent.putExtra("outputY", 500 * height / width);
        } else {
            intent.putExtra("outputX", 300);
            intent.putExtra("outputY", 300);
        }

        //裁剪后的图片Uri路径,uritempFile为Uri类变量
        uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + String.valueOf(System.currentTimeMillis()) + ".jpg");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        context.startActivityForResult(intent, Constants.ZOOM_IMAGE);
    }


    /**
     * 扫描图片
     *
     * @param path
     * @return
     */
     public static Result scanningImage(String path) {
        if (TextUtils.isEmpty(path)) {
            return null;
        }

        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(path, opt);
        // 获取到这个图片的原始宽度和高度
        int picWidth = opt.outWidth;
        int picHeight = opt.outHeight;
        // 获取画布中间方框的宽度和高度
        int screenWidth = 1200;
        int screenHeight = 675;
        // isSampleSize是表示对图片的缩放程度,比如值为2图片的宽度和高度都变为以前的1/2
        opt.inSampleSize = 1;
        // 根据屏的大小和图片大小计算出缩放比例
        if (picWidth > picHeight) {
            if (picWidth > screenWidth)
                opt.inSampleSize = picWidth / screenWidth;
        } else {
            if (picHeight > screenHeight)
                opt.inSampleSize = picHeight / screenHeight;
        }
        // 生成有像素经过缩放了的bitmap
        opt.inJustDecodeBounds = false;
        bitmap = BitmapFactory.decodeFile(path, opt);
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        if (bitmap == null) {
            return null;
        }
        return QrUtils.decodeImage(QrUtils.getYUV420sp(width, height, bitmap), width, height);
    }

}

QrUtils.Java

public class QrUtils {
     private static byte[] yuvs;

    /**
     * YUV420sp
     *
     * @param inputWidth
     * @param inputHeight
     * @param scaled
     * @return
     */
    public static byte[] getYUV420sp(int inputWidth, int inputHeight, Bitmap scaled) {
        int[] argb = new int[inputWidth * inputHeight];

        scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);

        /**
         * 需要转换成偶数的像素点,否则编码YUV420的时候有可能导致分配的空间大小不够而溢出。
         */
        int requiredWidth = inputWidth % 2 == 0 ? inputWidth : inputWidth + 1;
        int requiredHeight = inputHeight % 2 == 0 ? inputHeight : inputHeight + 1;

        int byteLength = requiredWidth * requiredHeight * 3 / 2;
        if (yuvs == null || yuvs.length < byteLength) {
            yuvs = new byte[byteLength];
        } else {
            Arrays.fill(yuvs, (byte) 0);
        }

        encodeYUV420SP(yuvs, argb, inputWidth, inputHeight);

        scaled.recycle();

        return yuvs;
    }
    /*public static Bitmap decodeSampledBitmapFromFile(String imgPath, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imgPath, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(imgPath, options);
    }*/

    /**
     * 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.
     */
    public static Result decodeImage(byte[] data, int width, int height) {
        // 处理
        Result result = null;
        try {
            Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
            hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
            PlanarYUVLuminanceSource source =
                    new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);
            /**
             * HybridBinarizer算法使用了更高级的算法,但使用GlobalHistogramBinarizer识别效率确实比HybridBinarizer要高一些。
             *
             * GlobalHistogram算法:(http://kuangjianwei.blog.163.com/blog/static/190088953201361015055110/)
             *
             * 二值化的关键就是定义出黑白的界限,我们的图像已经转化为了灰度图像,每个点都是由一个灰度值来表示,就需要定义出一个灰度值,大于这个值就为白(0),低于这个值就为黑(1)。
             * 在GlobalHistogramBinarizer中,是从图像中均匀取5行(覆盖整个图像高度),每行取中间五分之四作为样本;以灰度值为X轴,每个灰度值的像素个数为Y轴建立一个直方图,
             * 从直方图中取点数最多的一个灰度值,然后再去给其他的灰度值进行分数计算,按照点数乘以与最多点数灰度值的距离的平方来进行打分,选分数最高的一个灰度值。接下来在这两个灰度值中间选取一个区分界限,
             * 取的原则是尽量靠近中间并且要点数越少越好。界限有了以后就容易了,与整幅图像的每个点进行比较,如果灰度值比界限小的就是黑,在新的矩阵中将该点置1,其余的就是白,为0。
             */
//            BinaryBitmap bitmap1 = new BinaryBitmap(new GlobalHistogramBinarizer(source));
            BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
            QRCodeReader reader2 = new QRCodeReader();
            result = reader2.decode(bitmap1, hints);
        } catch (ReaderException e) {
        }
        return result;
    }


    /**
     * 将bitmap里得到的argb数据转成yuv420sp格式
     * 这个yuv420sp数据就可以直接传给MediaCodec, 通过AvcEncoder间接进行编码
     *
     * @param yuv420sp 用来存放yuv429sp数据
     * @param argb     传入argb数据
     * @param width    bmpWidth
     * @param height   bmpHeight
     */
    private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
        // 帧图片的像素大小
        final int frameSize = width * height;
        // Y的index从0开始
        int yIndex = 0;
        // UV的index从frameSize开始
        int uvIndex = frameSize;
        // YUV数据, ARGB数据
        int Y, U, V, a, R, G, B;
        ;
        int argbIndex = 0;
        // ---循环所有像素点,RGB转YUV---
        for (int j = 0; j < height; j++) {
            for (int i = 0; i < width; i++) {

                // a is not used obviously
                a = (argb[argbIndex] & 0xff000000) >> 24;
                R = (argb[argbIndex] & 0xff0000) >> 16;
                G = (argb[argbIndex] & 0xff00) >> 8;
                B = (argb[argbIndex] & 0xff);
                argbIndex++;

                // well known RGB to YUV algorithm
                Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
                U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
                V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

                Y = Math.max(0, Math.min(Y, 255));
                U = Math.max(0, Math.min(U, 255));
                V = Math.max(0, Math.min(V, 255));

                // NV21 has a plane of Y and interleaved planes of VU each
                // sampled by a factor of 2
                // meaning for every 4 Y pixels there are 1 V and 1 U. Note the
                // sampling is every other
                // pixel AND every other scanline.
                // ---Y---
                yuv420sp[yIndex++] = (byte) Y;
                // ---UV---
                if ((j % 2 == 0) && (i % 2 == 0)) {
                    yuv420sp[uvIndex++] = (byte) V;
                    yuv420sp[uvIndex++] = (byte) U;
                }
            }
        }
    }
二维码示例.png

ZXingCodeUtils(生成二维码)
ps:这是二维码生成的工具类,四种情况,带logo,不带logo,带文字带logo,带文字无logo.(文字就是指二维码的邀请码部分)嗯.....公司需求,这里是将二维码和文字画在一个背景图上面的,要是不需要,改改就是了,上图,说我们公司需要的的样式.

public class ZXingCodeUtils {
    private volatile static ZXingCodeUtils instance;

    public static ZXingCodeUtils getInstance() {
        if (instance == null) {
            synchronized (ZXingCodeUtils.class) {
                instance = new ZXingCodeUtils();
            }
        }
        return instance;
    }

    //不带logo
    public Bitmap createQRCode(String msg, int width, int height) {
        return createQRCode(msg, width, height, null);
    }

    //带logo
    public Bitmap createQRCode(String msg, int width, int height, Bitmap logo) {
        if (!TextUtils.isEmpty(msg)) {
            LogUtils.e("url is ------"+msg);
        }
        try {
            int[] tempSize = new int[]{width, height};//要求生成的二维码的宽高
            Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            hints.put(EncodeHintType.MARGIN, 1);
            BitMatrix matrix = new QRCodeWriter().encode(msg, BarcodeFormat.QR_CODE, width, height, hints);
            //调用去除白边方法
            matrix = deleteWhite(matrix);
            width = matrix.getWidth();
            height = matrix.getHeight();

            int[] pixels = new int[width * height];
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    if (matrix.get(x, y)) {
                        pixels[y * width + x] = Color.BLACK;
                    } else {
                        pixels[y * width + x] = Color.WHITE;
                    }
                }
            }
            // 生成二维码图片的格式,使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            bitmap = addLogoToQRCode(bitmap, logo);
            bitmap = zoomImg(bitmap, tempSize[0], tempSize[1]);//处理去掉白边后,二维码的尺寸变化问题。
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }


    //带文字无logo
    public Bitmap createQRCodeWithText(ShareBitmapBean dataBean) {
        return createQRCodeWithText(dataBean, null);
    }

    //带文字有logo


    public Bitmap createQRCodeWithText(ShareBitmapBean dataBean, Bitmap logo) {

        if (dataBean == null) {
            throw new IllegalArgumentException("shareBitmap data can't be null");
        }

        Bitmap bg = dataBean.getBg();
        if (bg == null) {
            throw new IllegalArgumentException("background bitmap can't be null");
        }
        bg = bg.copy(Bitmap.Config.ARGB_8888, true);
        Canvas canvas = new Canvas(bg);
        Paint paint = new Paint();

        //绘制二维码
        if (dataBean.getQrCodeList() != null) {
            for (ShareBitmapBean.QRCodeBean qrCodeBean : dataBean.getQrCodeList()) {
                Bitmap qrCode = createQRCode(qrCodeBean.getCodeMsg(), qrCodeBean.getCodeWidth(), qrCodeBean.getCodeHeight(), logo);
                canvas.drawBitmap(qrCode, qrCodeBean.getCodeX(), qrCodeBean.getCodeY(), paint);
            }
        }

        //绘制文字
        if (dataBean.getTextList() != null) {
            for (ShareBitmapBean.TextBean textBean : dataBean.getTextList()) {
                int textTop = textBean.getSizePX() * 7 / 8 + textBean.getTextY();
                paint.setColor(textBean.getTextColor());
                paint.setTextSize(textBean.getSizePX());
                if (textBean.isCenter()) {
                    paint.setTextAlign(Paint.Align.CENTER);
                    canvas.drawText(textBean.getText(), 360, textTop, paint);
                } else {
                    paint.setTextAlign(Paint.Align.LEFT);
                    canvas.drawText(textBean.getText(), textBean.getTextX(), textTop, paint);
                }

            }
        }

        //绘制bitmap图像
        if (dataBean.getBitmapList() != null) {
            for (ShareBitmapBean.BitmapBean bitmapBean : dataBean.getBitmapList()) {
                canvas.drawBitmap(bitmapBean.getBitmap(), bitmapBean.getBitmapX(), bitmapBean.getBitmapY(), paint);
            }
        }
        return bg;
    }


    //添加logo到二维码图片上
    private Bitmap addLogoToQRCode(Bitmap src, Bitmap logo) {
        if (src == null || logo == null) {
            return src;
        }
        int srcWidth = src.getWidth();
        int srcHeight = src.getHeight();
        int logoWidth = logo.getWidth();
        int logoHeight = logo.getHeight();

        float scaleFactor = srcWidth * 1.0f / 5 / logoWidth;
        Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
        try {
            Canvas canvas = new Canvas(bitmap);
            canvas.drawBitmap(src, 0, 0, null);
            canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
            canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null);
            canvas.save();
            canvas.restore();
        } catch (Exception e) {
            bitmap = null;
        }
        return bitmap;
    }

    private static BitMatrix deleteWhite(BitMatrix matrix) {
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] + 1;
        int resHeight = rec[3] + 1;

        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1]))
                    resMatrix.set(i, j);
            }
        }
        return resMatrix;
    }

    /**
     * 按照指定的尺寸缩放Bitmap
     *
     * @param bm
     * @param newWidth
     * @param newHeight
     * @return
     */
    public Bitmap zoomImg(Bitmap bm, float newWidth, float newHeight) {
        if (bm == null) {
            return null;
        }
        // 获得图片的宽高
        int width = bm.getWidth();
        int height = bm.getHeight();
        // 计算缩放比例
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 取得想要缩放的matrix参数
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        // 得到新的图片   www.2cto.com
        Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
        return newbm;
    }
}

ShareBitmapBean.Java
这个是二维码使用Bean,使用文字,背景图片用的,需要就用,不需要就不用是了,具体都有备注的.

public class ShareBitmapBean implements Serializable{

    private Bitmap bg;

    private List<QRCodeBean> qrCodeList;

    private List<TextBean> textList;

    private List<BitmapBean> bitmapList;

    public Bitmap getBg() {
        return bg;
    }

    public void setBg(Bitmap bg) {
        this.bg = bg;
    }

    public List<QRCodeBean> getQrCodeList() {
        return qrCodeList;
    }

    public void setQrCodeList(List<QRCodeBean> qrCodeList) {
        this.qrCodeList = qrCodeList;
    }

    public List<TextBean> getTextList() {
        return textList;
    }

    public void setTextList(List<TextBean> textList) {
        this.textList = textList;
    }

    public List<BitmapBean> getBitmapList() {
        return bitmapList;
    }

    public void setBitmapList(List<BitmapBean> bitmapList) {
        this.bitmapList = bitmapList;
    }

    public static class QRCodeBean implements Serializable{

        //二维码内的信息
        private String codeMsg;
        //二维码的宽度
        private int codeWidth;
        //二维码的高度
        private int codeHeight;
        //二维码的X坐标
        private int codeX;
        //二维码的Y坐标
        private int codeY;

        public QRCodeBean(String codeMsg, int codeWidth, int codeHeight, int codeX, int codeY) {
            this.codeMsg = codeMsg;
            this.codeWidth = codeWidth;
            this.codeHeight = codeHeight;
            this.codeX = codeX;
            this.codeY = codeY;
        }

        public String getCodeMsg() {
            return codeMsg;
        }

        public void setCodeMsg(String codeMsg) {
            this.codeMsg = codeMsg;
        }

        public int getCodeWidth() {
            return codeWidth;
        }

        public void setCodeWidth(int codeWidth) {
            this.codeWidth = codeWidth;
        }

        public int getCodeHeight() {
            return codeHeight;
        }

        public void setCodeHeight(int codeHeight) {
            this.codeHeight = codeHeight;
        }

        public int getCodeX() {
            return codeX;
        }

        public void setCodeX(int codeX) {
            this.codeX = codeX;
        }

        public int getCodeY() {
            return codeY;
        }

        public void setCodeY(int codeY) {
            this.codeY = codeY;
        }

        public List<QRCodeBean> toList() {
            List<QRCodeBean> list = new ArrayList<>();
            list.add(this);
            return list;
        }
    }


    public static class TextBean implements Serializable{
        //文字信息
        private String text;
        //字号大小,单位像素
        private int sizePX;
        //文字的X坐标
        private int textX;
        //文字的Y坐标
        private int textY;
        //文字颜色
        private int textColor;
        private boolean isCenter=false;

        public boolean isCenter() {
            return isCenter;
        }

        public void setCenter(boolean center) {
            isCenter = center;
        }

        public TextBean(String text, int sizePX, int textX, int textY, int textColor) {
            this.text = text;
            this.sizePX = sizePX;
            this.textX = textX;
            this.textY = textY;
            this.textColor = textColor;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public int getSizePX() {
            return sizePX;
        }

        public void setSizePX(int sizePX) {
            this.sizePX = sizePX;
        }

        public int getTextX() {
            return textX;
        }

        public void setTextX(int textX) {
            this.textX = textX;
        }

        public int getTextY() {
            return textY;
        }

        public void setTextY(int textY) {
            this.textY = textY;
        }

        public int getTextColor() {
            return textColor;
        }

        public void setTextColor(int textColor) {
            this.textColor = textColor;
        }

        public List<TextBean> toList() {
            List<TextBean> list = new ArrayList<>();
            list.add(this);
            return list;
        }
    }


    public static class BitmapBean implements Serializable{
        //bitmap
        private Bitmap bitmap;

        private int bitmapWidth;

        private int bitmapHeight;

        private int bitmapX;

        private int bitmapY;

        public BitmapBean(Bitmap bitmap, int bitmapWidth, int bitmapHeight, int bitmapX, int bitmapY) {
            this.bitmap = bitmap;
            this.bitmapWidth = bitmapWidth;
            this.bitmapHeight = bitmapHeight;
            this.bitmapX = bitmapX;
            this.bitmapY = bitmapY;
        }

        public Bitmap getBitmap() {
            return bitmap;
        }

        public void setBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
        }

        public int getBitmapWidth() {
            return bitmapWidth;
        }

        public void setBitmapWidth(int bitmapWidth) {
            this.bitmapWidth = bitmapWidth;
        }

        public int getBitmapHeight() {
            return bitmapHeight;
        }

        public void setBitmapHeight(int bitmapHeight) {
            this.bitmapHeight = bitmapHeight;
        }

        public int getBitmapX() {
            return bitmapX;
        }

        public void setBitmapX(int bitmapX) {
            this.bitmapX = bitmapX;
        }

        public int getBitmapY() {
            return bitmapY;
        }

        public void setBitmapY(int bitmapY) {
            this.bitmapY = bitmapY;
        }

        public List<BitmapBean> toList() {
            List<BitmapBean> list = new ArrayList<>();
            list.add(this);
            return list;
        }
    }


}

----------------------好了,到此为止了,大功告成---------------------

以上资源均来源于网络,如有侵权,联系我立刻删除!!!!

提供四张二维码供扫描:


1.png
2.png
3.png
4.png

此二维码liabrary:
链接:https://pan.baidu.com/s/1UbmscfIQyEiEzw2jrjPkwg
提取码:onqr
github地址:https://github.com/Mchunyan/QRTest

一只萌级小菜鸟,哈哈哈哈哈隔 [糗大了][糗大了]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容