仿商城商品生成分享海报图片和识别图中二维码并跳转商品详情页

购物商城里面的商品分享给朋友的时候会生成一张海报图片,图片上附带这二维码图片,朋友拿到这张图片扫描上面的二维码就可以进入商品详情页查看此商品了。今天来做一下这种功能,先生成商品海报图片,然后长按这张图片识别图中二维码。先附上两张效果图:

      

这里面会用到二维码的生成与识别,使用的是google ZXing,要在app的build.gradle里面引入

dependencies {
    compile 'com.google.zxing:core:3.3.0'//zxing二维码
}

图中还有一处现售价文字中间有一横线,此处是这样实现的

        //中间加横线
        tv_nowPrice.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
        //底部加横线:
        tv_nowPrice .getPaint().setFlags(Paint. UNDERLINE_TEXT_FLAG );

首先来说下海报图片的生成,海报样式根据UI新建个布局文件,这里以我做的例子为例来说:

新建一个generateposter.xml布局文件,效果图和布局代码如下

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

    <ImageView
        android:id="@+id/img_good"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:src="@mipmap/pic_share"
        android:scaleType="fitXY"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:orientation="horizontal">
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"
            android:orientation="vertical">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="15dp"
                android:text="包邮  卤料包卤料卤菜四川味100g×4卤味调料秘制包配方五香鸭脖自家卤水"
                android:textSize="16dp"
                android:textColor="@color/edittext_color"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="10dp"
                android:text="券:15"
                android:textSize="16dp"
                android:textColor="@color/colorAccent"/>
            <TextView
                android:id="@+id/tv_nowPrice"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="10dp"
                android:text="现售价  ¥29.8"
                android:textSize="16dp"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="10dp"
                android:text="券后价  ¥14.8"
                android:textSize="16dp"
                android:textColor="@color/colorAccent"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/img_erweima"
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:layout_marginTop="15dp"
                android:scaleType="fitXY"
                android:layout_gravity="center_horizontal"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:layout_marginTop="10dp"
                android:text="长按扫码购买商品"
                android:textSize="16dp"
                android:textColor="@color/edittext_color"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

把布局文件view转换成图片

    //然后View和其内部的子View都具有了实际大小,也就是完成了布局,相当与添加到了界面上。接着就可以创建位图并在上面绘制了:
    public static void layoutView(View v, int width, int height) {
        // 整个View的大小 参数是左上角 和右下角的坐标
        v.layout(0, 0, width, height);
        int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
        int measuredHeight = View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.AT_MOST);
        /** 当然,measure完后,并不会实际改变View的尺寸,需要调用View.layout方法去进行布局。
         * 按示例调用layout函数后,View的大小将会变成你想要设置成的大小。
         */
        v.measure(measuredWidth, measuredHeight);
        v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    }

   /**
     * 把一个View转换成图片
     * @param v
     * @return
     */
    public static Bitmap loadBitmapFromView(View v) {
        int w = v.getWidth();
        int h = v.getHeight();
        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);

        /** 如果不设置canvas画布为白色,则生成透明 */
        c.drawColor(Color.WHITE);

        v.layout(0, 0, w, h);
        v.draw(c);

        return bmp;
    }

二维码生成

/**
     * 生成二维码 要转换的地址或字符串,可以是中文
     *
     * @param url
     * @param width
     * @param height
     * @return
     */
    public static Bitmap createQRImage(String url, int width, int height, boolean needDeleteWhiteBorder) {
        try {
            // 判断URL合法性
            if (url == null || "".equals(url) || url.length() < 1) {
                return null;
            }
            Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            // 图像数据转换,使用了矩阵转换
            BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, width, height, hints);
            if (needDeleteWhiteBorder) {
                bitMatrix = deleteWhite(bitMatrix);//删除白边
            }
            width = bitMatrix.getWidth();
            height = bitMatrix.getHeight();
            int[] pixels = new int[width * height];
            // 下面这里按照二维码的算法,逐个生成二维码的图片,
            // 两个for循环是图片横列扫描的结果
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    if (bitMatrix.get(x, y)) {
                        pixels[y * width + x] = 0xff000000;
                    } else {
                        pixels[y * width + x] = 0xffffffff;
                    }
                }
            }
            // 生成二维码图片的格式,使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

二维码的识别

   /**
     * 识别图中是否有二维码
     * @param bitmap
     * @return
     */
    public static Result handleQRCodeFormBitmap(Bitmap bitmap) {
        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);

        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int[] data = new int[width * height];
        bitmap.getPixels(data, 0, width, 0, 0, width, height);
        RGBLuminanceSource source = new RGBLuminanceSource(width, height, data);
        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
        QRCodeReader reader2 = new QRCodeReader();
        Result result = null;
        try {
            result = reader2.decode(bitmap1, hints);
            Log.e("tag","result="+result.getText());
        } catch (Exception e) {
            e.printStackTrace();
            if (source != null) {
                BinaryBitmap bitmap2 = new BinaryBitmap(new GlobalHistogramBinarizer(source));
                try {
                    result = reader2.decode(bitmap2, hints);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
        return result;
    }

如果图片上有二维码,长按图片弹窗显示识别图中二维码,如果图片上没有二维码,长按图片弹窗中则不显示识别图中二维码,这里的处理逻辑为,先识别二维码,通过对识别的结果判断图中是否有二维码,如果有识别结果则证明图片上有二维码,如果没有识别结果或者识别结果为空,这表明图片上没有二维码;下面是两种情况的图片进行对比:

    

 代码如下:

img_show.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                Bitmap obmp = ((BitmapDrawable) (img_show.getDrawable())).getBitmap();
                Result result = handleQRCodeFormBitmap(obmp);
                if (result == null) {
                    Log.e("tag", "图上无二维码");
                    showAlert(obmp);
                } else {
                    Log.e("tag", "图中二维码识别结果=" + result.getText());
                    showSelectAlert(obmp,result.toString());
                }
                return false;
            }
        });

   /**
     * 图中无二维码显示此弹窗
     * @param bitmap
     */
    private void showAlert(final Bitmap bitmap) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("保存图片")
                .setCancelable(false)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterfacem, int i) {
                        SaveImgToSD.saveMyBitmap("img_goods",bitmap);
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterfacem, int i) {
                    }
                });
        builder.show();
    }

    /**
     * 图中有二维码显示此弹窗
     * @param bitmap
     * @param url
     */
    private void showSelectAlert(final Bitmap bitmap, final String url) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("请选择");
        String str[] = {"保存图片", "识别图中二维码"};
        builder.setItems(str, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterfacem, int i) {
                switch (i) {
                    case 0:
                        SaveImgToSD.saveMyBitmap("img_goods",bitmap);
                    break;
                    case 1:
                        //用默认浏览器打开扫描得到的地址
                        Intent intent = new Intent();
                        intent.setAction("android.intent.action.VIEW");
                        Uri content_url = Uri.parse(url);
                        intent.setData(content_url);
                        startActivity(intent);
                    break;
                }
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterfacem, int i) {

            }
        });
        builder.show();
    }

以上就实现的全部功能,下面附上整个工程的完整代码:

有两个布局文件,一个是activity类的布局,另一个是生成海报图片的布局。

activity类的布局文件activity_generateposter_null.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        style="@style/textview_title"
        android:text="生成海报" />
    <Button
        android:id="@+id/btn_generate"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="20dp"
        android:text="生成海报"/>
    <ImageView
        android:id="@+id/img_show"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

生成海报图片的布局文件activity_generateposter.xml

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

    <ImageView
        android:id="@+id/img_good"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:src="@mipmap/pic_share"
        android:scaleType="fitXY"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:orientation="horizontal">
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"
            android:orientation="vertical">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="15dp"
                android:text="包邮  卤料包卤料卤菜四川味100g×4卤味调料秘制包配方五香鸭脖自家卤水"
                android:textSize="16dp"
                android:textColor="@color/edittext_color"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="10dp"
                android:text="券:15"
                android:textSize="16dp"
                android:textColor="@color/colorAccent"/>
            <TextView
                android:id="@+id/tv_nowPrice"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="10dp"
                android:text="现售价  ¥29.8"
                android:textSize="16dp"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="10dp"
                android:text="券后价  ¥14.8"
                android:textSize="16dp"
                android:textColor="@color/colorAccent"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/img_erweima"
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:layout_marginTop="15dp"
                android:scaleType="fitXY"
                android:layout_gravity="center_horizontal"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:layout_marginTop="10dp"
                android:text="长按扫码购买商品"
                android:textSize="16dp"
                android:textColor="@color/edittext_color"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

activity类文件GeneratePosterActivity.class

package com.junto.text.Posters;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.zxing.Result;
import com.junto.text.R;
import com.junto.text.tools.SaveImgToSD;
import com.junto.text.tools.ZXingUtils;

import static com.junto.text.Posters.ImageUtils.loadBitmapFromView;
import static com.junto.text.tools.ZXingUtils.handleQRCodeFormBitmap;

/**
 * Created by WangJinyong on 2019/10/23.
 * 生成海报
 */
public class GeneratePosterActivity extends Activity {

    View view;
    ImageView img_show, img_erweima;
    Button btn_generate;
    TextView tv_nowPrice;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_generateposter_null);
        initView();
    }

    private void initView() {
        view = View.inflate(this, R.layout.activity_generateposter, null);
        img_show = findViewById(R.id.img_show);
        tv_nowPrice = view.findViewById(R.id.tv_nowPrice);//现售价
        //中间加横线
        tv_nowPrice.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
        //底部加横线:
//        tv_nowPrice .getPaint().setFlags(Paint. UNDERLINE_TEXT_FLAG );
        img_erweima = view.findViewById(R.id.img_erweima);
        btn_generate = findViewById(R.id.btn_generate);
        btn_generate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                img_erweima.setImageBitmap(ZXingUtils.createQRImage("http://www.taobao.com", 90, 90, true));
                ImageUtils.layoutView(view, 840, 1200);
                // 把一个View转换成图片
                Bitmap cachebmp = loadBitmapFromView(view);
                img_show.setImageBitmap(cachebmp);//直接展示转化的bitmap
//                ImageUtils.viewSaveToImage(view,"GeneratePoster",img_show);
            }
        });

        img_show.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                Bitmap obmp = ((BitmapDrawable) (img_show.getDrawable())).getBitmap();
                Result result = handleQRCodeFormBitmap(obmp);
                if (result == null) {
                    Log.e("tag", "图上无二维码");
                    showAlert(obmp);
                } else {
                    Log.e("tag", "图中二维码识别结果=" + result.getText());
                    showSelectAlert(obmp,result.toString());
                }
                return false;
            }
        });
    }

    /**
     * 图中无二维码显示此弹窗
     * @param bitmap
     */
    private void showAlert(final Bitmap bitmap) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("保存图片")
                .setCancelable(false)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterfacem, int i) {
                        SaveImgToSD.saveMyBitmap("img_goods",bitmap);
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterfacem, int i) {
                    }
                });
        builder.show();
    }

    /**
     * 图中有二维码显示此弹窗
     * @param bitmap
     * @param url
     */
    private void showSelectAlert(final Bitmap bitmap, final String url) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("请选择");
        String str[] = {"保存图片", "识别图中二维码"};
        builder.setItems(str, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterfacem, int i) {
                switch (i) {
                    case 0:
                        SaveImgToSD.saveMyBitmap("img_goods",bitmap);
                    break;
                    case 1:
                        //用默认浏览器打开扫描得到的地址
                        Intent intent = new Intent();
                        intent.setAction("android.intent.action.VIEW");
                        Uri content_url = Uri.parse(url);
                        intent.setData(content_url);
                        startActivity(intent);
                    break;
                }
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterfacem, int i) {

            }
        });
        builder.show();
    }
}

用到的工具类:

1.二维码的生成与识别工具ZXingUtils
package com.junto.text.tools;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;

import java.util.EnumMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * 生成条形码和二维码的工具
 */
public class ZXingUtils {

    /**
     * 生成二维码 要转换的地址或字符串,可以是中文
     *
     * @param url
     * @param width
     * @param height
     * @return
     */
    public static Bitmap createQRImage(String url, int width, int height, boolean needDeleteWhiteBorder) {
        try {
            // 判断URL合法性
            if (url == null || "".equals(url) || url.length() < 1) {
                return null;
            }
            Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            // 图像数据转换,使用了矩阵转换
            BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, width, height, hints);
            if (needDeleteWhiteBorder) {
                bitMatrix = deleteWhite(bitMatrix);//删除白边
            }
            width = bitMatrix.getWidth();
            height = bitMatrix.getHeight();
            int[] pixels = new int[width * height];
            // 下面这里按照二维码的算法,逐个生成二维码的图片,
            // 两个for循环是图片横列扫描的结果
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    if (bitMatrix.get(x, y)) {
                        pixels[y * width + x] = 0xff000000;
                    } else {
                        pixels[y * width + x] = 0xffffffff;
                    }
                }
            }
            // 生成二维码图片的格式,使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成的二维码  删除白边
     * @param matrix
     * @return
     */
    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;
    }

    /**
     * 生成条形码
     *
     * @param context
     * @param contents      需要生成的内容
     * @param desiredWidth  生成条形码的宽带
     * @param desiredHeight 生成条形码的高度
     * @param displayCode   是否在条形码下方显示内容
     * @return
     */
    public static Bitmap creatBarcode(Context context, String contents,
                                      int desiredWidth, int desiredHeight, boolean displayCode) {
        Bitmap ruseltBitmap = null;
        /**
         * 图片两端所保留的空白的宽度
         */
        int marginW = 20;
        /**
         * 条形码的编码类型
         */
        BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128;
        if (displayCode) {
            Bitmap barcodeBitmap = encodeAsBitmap(contents, barcodeFormat, desiredWidth, desiredHeight);
            Bitmap codeBitmap = creatCodeBitmap(contents, desiredWidth + 2 * marginW, desiredHeight, context);
            ruseltBitmap = mixtureBitmap(barcodeBitmap, codeBitmap, new PointF(0, desiredHeight));
        } else {
            ruseltBitmap = encodeAsBitmap(contents, barcodeFormat, desiredWidth, desiredHeight);
        }
        return ruseltBitmap;
    }

    /**
     * 生成条形码的Bitmap
     *
     * @param contents 需要生成的内容
     * @param format 编码格式
     * @param desiredWidth
     * @param desiredHeight
     * @return
     * @throws WriterException
     */
    protected static Bitmap encodeAsBitmap(String contents, BarcodeFormat format, int desiredWidth, int desiredHeight) {
        final int WHITE = 0xFFFFFFFF;
        final int BLACK = 0xFF000000;

        MultiFormatWriter writer = new MultiFormatWriter();
        BitMatrix result = null;
        try {
            result = writer.encode(contents, format, desiredWidth,
                    desiredHeight, null);
        } catch (WriterException e) {
            e.printStackTrace();
        }

        int width = result.getWidth();
        int height = result.getHeight();
        int[] pixels = new int[width * height];
        // All are 0, or black, by default
        for (int y = 0; y < height; y++) {
            int offset = y * width;
            for (int x = 0; x < width; x++) {
                pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
            }
        }
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
        return bitmap;
    }

    /**
     * 生成显示编码的Bitmap
     *
     * @param contents
     * @param width
     * @param height
     * @param context
     * @return
     */
    protected static Bitmap creatCodeBitmap(String contents, int width, int height, Context context) {
        TextView tv = new TextView(context);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        tv.setLayoutParams(layoutParams);
        tv.setText(contents);
        tv.setHeight(height);
        tv.setGravity(Gravity.CENTER_HORIZONTAL);
        tv.setWidth(width);
        tv.setDrawingCacheEnabled(true);
        tv.setTextColor(Color.BLACK);
        tv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());

        tv.buildDrawingCache();
        Bitmap bitmapCode = tv.getDrawingCache();
        return bitmapCode;
    }

    /**
     * 将两个Bitmap合并成一个
     *
     * @param first
     * @param second
     * @param fromPoint 第二个Bitmap开始绘制的起始位置(相对于第一个Bitmap)
     * @return
     */
    protected static Bitmap mixtureBitmap(Bitmap first, Bitmap second, PointF fromPoint) {
        if (first == null || second == null || fromPoint == null) {
            return null;
        }
        int marginW = 20;
        Bitmap newBitmap = Bitmap.createBitmap(
                first.getWidth() + second.getWidth() + marginW,
                first.getHeight() + second.getHeight(), Bitmap.Config.ARGB_4444);
        Canvas cv = new Canvas(newBitmap);
        cv.drawBitmap(first, marginW, 0, null);
        cv.drawBitmap(second, fromPoint.x, fromPoint.y, null);
        cv.save();
        cv.restore();
        return newBitmap;
    }

    /**
     * 识别图中是否有二维码
     * @param bitmap
     * @return
     */
    public static Result handleQRCodeFormBitmap(Bitmap bitmap) {
        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);

        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int[] data = new int[width * height];
        bitmap.getPixels(data, 0, width, 0, 0, width, height);
        RGBLuminanceSource source = new RGBLuminanceSource(width, height, data);
        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
        QRCodeReader reader2 = new QRCodeReader();
        Result result = null;
        try {
            result = reader2.decode(bitmap1, hints);
            Log.e("tag","result="+result.getText());
        } catch (Exception e) {
            e.printStackTrace();
            if (source != null) {
                BinaryBitmap bitmap2 = new BinaryBitmap(new GlobalHistogramBinarizer(source));
                try {
                    result = reader2.decode(bitmap2, hints);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
        return result;
    }
}

2.布局文件生成海报图片的工具类ImageUtils

package com.junto.text.Posters;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by WangJinyong on 2019/10/23.
 * 布局生成分享图片 工具类
 */
public class ImageUtils {

    //然后View和其内部的子View都具有了实际大小,也就是完成了布局,相当与添加到了界面上。接着就可以创建位图并在上面绘制了:
    public static void layoutView(View v, int width, int height) {
        // 整个View的大小 参数是左上角 和右下角的坐标
        v.layout(0, 0, width, height);
        int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
        int measuredHeight = View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.AT_MOST);
        /** 当然,measure完后,并不会实际改变View的尺寸,需要调用View.layout方法去进行布局。
         * 按示例调用layout函数后,View的大小将会变成你想要设置成的大小。
         */
        v.measure(measuredWidth, measuredHeight);
        v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    }

    public static String viewSaveToImage(View view, String child, ImageView img_show) {
        Log.e("tag","viewSaveToImage");
        /**
         * View组件显示的内容可以通过cache机制保存为bitmap
         * 我们要获取它的cache先要通过setDrawingCacheEnable方法把cache开启,
         * 然后再调用getDrawingCache方法就可 以获得view的cache图片了
         * 。buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,
         * 若果 cache没有建立,系统会自动调用buildDrawingCache方法生成cache。
         * 若果要更新cache, 必须要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。
         */
        //        view.setDrawingCacheEnabled(true);
        //        view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
        //设置绘制缓存背景颜色
        //        view.setDrawingCacheBackgroundColor(Color.WHITE);

        // 把一个View转换成图片
        Bitmap cachebmp = loadBitmapFromView(view);

        img_show.setImageBitmap(cachebmp);//直接展示转化的bitmap

        //保存在本地 产品还没决定要不要保存在本地
        FileOutputStream fos;
        try {
            // 判断手机设备是否有SD卡
            boolean isHasSDCard = Environment.getExternalStorageState().equals(
                    android.os.Environment.MEDIA_MOUNTED);
            if (isHasSDCard) {
                // SD卡根目录
                File sdRoot = Environment.getExternalStorageDirectory();
                Log.e("tag","SD卡根目录="+sdRoot.toString());
                File file = new File(sdRoot, child+".png");
                fos = new FileOutputStream(file);
            } else
                throw new Exception("创建文件失败!");
            //压缩图片 30 是压缩率,表示压缩70%; 如果不压缩是100,表示压缩率为0
            cachebmp.compress(Bitmap.CompressFormat.PNG, 90, fos);

            fos.flush();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        view.destroyDrawingCache();
        return sharePic(cachebmp,child);
    }

    /**
     * 把一个View转换成图片
     * @param v
     * @return
     */
    public static Bitmap loadBitmapFromView(View v) {
        int w = v.getWidth();
        int h = v.getHeight();
        Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmp);

        /** 如果不设置canvas画布为白色,则生成透明 */
        c.drawColor(Color.WHITE);

        v.layout(0, 0, w, h);
        v.draw(c);

        return bmp;
    }

    //保存在本地并一键分享
    private static String sharePic(Bitmap cachebmp,String child) {
        final File qrImage = new File(Environment.getExternalStorageDirectory(), child+".jpg");
        if(qrImage.exists())
        {
            qrImage.delete();
        }
        try {
            qrImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        FileOutputStream fOut = null;
        try {
            fOut = new FileOutputStream(qrImage);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        if(cachebmp == null)
        {
            return "";
        }
        cachebmp.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
        try {
            fOut.flush();
            fOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
//        Toast.makeText(this, "保存成功 " + qrImage.getPath().toString(), Toast.LENGTH_SHORT).show();
        return qrImage.getPath();
    }
}

3.保存图片到本地的工具类SaveImgToSD,这个类可以和上面第二个工具类整理合并成一个,里面有好几个方法在此例子里面的用不到的。

package com.junto.text.tools;

import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import static java.lang.System.out;

/**
 * Created by WangJinyong on 2018/9/18.
 */

public class SaveImgToSD {

    public static String filePath = "/sdcard/UVCCamera/";

    //将Bitmap图片保存到sd卡
    public static void saveBitmapToSD(Bitmap bt) {
        File path = Environment.getExternalStorageDirectory();
        File file = new File(path, System.currentTimeMillis() + ".jpg");
        out.println(Environment.getExternalStorageState() + "/Cool/" +"000000000000000000000000000");
        try {
            FileOutputStream out = new FileOutputStream(file);
            bt.compress(Bitmap.CompressFormat.JPEG, 90, out);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        out.flush();
        out.close();
    }

    /**
     * 保存bitmap到SD卡
     * @param bitName 保存的名字
     * @param mBitmap 图片对像
     * return 生成压缩图片后的图片路径
     */
    public static String saveMyBitmap(String bitName, Bitmap mBitmap) {
        makeRootDirectory();
        File f = new File(filePath + bitName + ".png");
        try {
            f.createNewFile();
        } catch (IOException e) {
            out.println("在保存图片时出错:" + e.toString());
        }
        FileOutputStream fOut = null;
        try {
            fOut = new FileOutputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
        } catch (Exception e) {
            return "create_bitmap_error";
        }
        try {
            fOut.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return filePath + bitName + ".png";
    }

    /**
     * 保存bitmap到SD卡
     * @param bitmap
     * @param imagename
     */
    public static String saveBitmapToSDCard(Bitmap bitmap, String imagename) {
        makeRootDirectory();
        String path = filePath + "img-" + imagename + ".jpg";
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path);
            if (fos != null) {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
                fos.close();
            }

            return path;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //生成文件夹
    public static void makeRootDirectory(){
        File file = null;
        try {
            file = new File(filePath);
            if (!file.exists()){//判断指定的路径或者指定的目录文件是否已经存在。
                file.mkdir();//建立文件夹
                Log.e("cacacaca","新建文件夹成功,路径为:" + filePath);
            }
        }catch (Exception e){
            Log.e("error:", e+"");
        }
    }
}

到此,这篇文章就结束啦,有不足之处还请大家多多指正。

发布了92 篇原创文章 · 获赞 38 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/u013184970/article/details/102738576