基于Android實(shí)現(xiàn)個(gè)性彩色好看的二維碼
我編碼的風(fēng)格,先給大家展示下效果圖,親們感覺效果還不錯(cuò),很滿意的話,請(qǐng)繼續(xù)往下閱讀。
之前呢,也寫過用安卓實(shí)現(xiàn)二維碼生成彩色的二維碼和帶logo的二維碼,也知道可以使用QRCode和ZXing兩種方式,然后這一篇呢也是寫二維碼使用BarcodeFormat.QR_CODE,主要也是看見很多的非常漂亮的二維碼,這里呢主要模仿qq的二維碼,并且也高仿實(shí)現(xiàn)了長(zhǎng)按發(fā)送給朋友和保存到圖庫的功能,覺得不錯(cuò)呢就請(qǐng)多支持下,哪里不好呢也可以說出來。好了我們一步一步來。
第一步:簡(jiǎn)單二維碼實(shí)現(xiàn)
先來個(gè)最簡(jiǎn)單的二維碼:
看下簡(jiǎn)單代碼實(shí)現(xiàn):
/** * 根據(jù)指定內(nèi)容生成自定義寬高的二維碼圖片 * * @param content * 需要生成二維碼的內(nèi)容 * @param width * 二維碼寬度 * @param height * 二維碼高度 * @throws WriterException * 生成二維碼異常 */ public static Bitmap makeQRImage(String content, int width, int height) throws WriterException { Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>(); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 圖像數(shù)據(jù)轉(zhuǎn)換,使用了矩陣轉(zhuǎn)換 BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); int[] pixels = new int[width * height]; // 按照二維碼的算法,逐個(gè)生成二維碼的圖片,兩個(gè)for循環(huán)是圖片橫列掃描的結(jié)果 for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (bitMatrix.get(x, y))//范圍內(nèi)為黑色的 pixels[y * width + x] = 0xff000000; else//其他的地方為白色 pixels[y * width + x] = 0xffffffff; } } // 生成二維碼圖片的格式,使用ARGB_8888 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); //設(shè)置像素矩陣的范圍 bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; }
第二步:簡(jiǎn)單二維碼加logo
接下來給二維碼加logo:(看圖)
/** * 根據(jù)指定內(nèi)容生成自定義寬高的二維碼圖片 * * param logoBm * logo圖標(biāo) * param content * 需要生成二維碼的內(nèi)容 * param width * 二維碼寬度 * param height * 二維碼高度 * throws WriterException * 生成二維碼異常 */ public static Bitmap makeQRImage(Bitmap logoBmp, String content, int QR_WIDTH, int QR_HEIGHT) throws WriterException { try { // 圖像數(shù)據(jù)轉(zhuǎn)換,使用了矩陣轉(zhuǎn)換 Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);// 容錯(cuò)率 hints.put(EncodeHintType.MARGIN, 2); // default is 4 hints.put(EncodeHintType.MAX_SIZE, 350); hints.put(EncodeHintType.MIN_SIZE, 100); BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints); int[] pixels = new int[QR_WIDTH * QR_HEIGHT]; for (int y = 0; y < QR_HEIGHT; y++) { // 下面這里按照二維碼的算法,逐個(gè)生成二維碼的圖片,//兩個(gè)for循環(huán)是圖片橫列掃描的結(jié)果 for (int x = 0; x < QR_WIDTH; x++) { if (bitMatrix.get(x, y)) pixels[y * QR_WIDTH + x] = 0xff000000; else pixels[y * QR_WIDTH + x] = 0xffffffff; } } // ------------------添加圖片部分------------------// Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888); // 設(shè)置像素點(diǎn) bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT); // 獲取圖片寬高 int logoWidth = logoBmp.getWidth(); int logoHeight = logoBmp.getHeight(); if (QR_WIDTH == 0 || QR_HEIGHT == 0) { return null; } if (logoWidth == 0 || logoHeight == 0) { return bitmap; } // 圖片繪制在二維碼中央,合成二維碼圖片 // logo大小為二維碼整體大小的1/2 float scaleFactor = QR_WIDTH * 1.0f / 2 / logoWidth; try { Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(bitmap, 0, 0, null); canvas.scale(scaleFactor, scaleFactor, QR_WIDTH / 2, QR_HEIGHT / 2); canvas.drawBitmap(logoBmp, (QR_WIDTH - logoWidth) / 2, (QR_HEIGHT - logoHeight) /2, null); canvas.save(Canvas.ALL_SAVE_FLAG); canvas.restore(); return bitmap; } catch (Exception e) { bitmap = null; e.getStackTrace(); } } catch (WriterException e) { e.printStackTrace(); } return null; }
上段代碼可以看出要給二維碼圖片中間加logo,但是圖片不能占據(jù)整個(gè)二維碼圖片的很大一部分。然后還必須設(shè)置容錯(cuò)率:容錯(cuò)率有M,L,Q,H幾個(gè)等級(jí),容錯(cuò)率越高,二維碼的有效像素點(diǎn)就越多。這里使用小寫的utf-8編碼,大寫會(huì)出現(xiàn)]Q2\000026開頭內(nèi)容,為了好看點(diǎn)還設(shè)置了邊距和大小。
第三步:實(shí)現(xiàn)帶logo的彩色二維碼
接下來我們把黑白矩陣變?yōu)椴噬仃嚕?
就把
if (bitMatrix.get(x, y)) pixels[y * width + x] = 0xff000000; else pixels[y * width + x] = 0xffffffff;
替換為:(這里的顏色隨便設(shè)置,效果隨便改)
if (x < QR_WIDTH / 2 && y < QR_HEIGHT / 2) { pixels[y * QR_WIDTH + x] = 0xFF0094FF;// 藍(lán)色 Integer.toHexString(new Random().nextInt()); } else if (x < QR_WIDTH / 2 && y > QR_HEIGHT / 2) { pixels[y * QR_WIDTH + x] = 0xFFFED545;// 黃色 } else if (x > QR_WIDTH / 2 && y > QR_HEIGHT / 2) { pixels[y * QR_WIDTH + x] = 0xFF5ACF00;// 綠色 } else { pixels[y * QR_WIDTH + x] = 0xFF000000;// 黑色 } } else { pixels[y * QR_WIDTH + x] = 0xffffffff;// 白色 }
改后的效果:
第四步:給二維碼加背景
接下來我們來給二維碼圖片加背景:
/** * 給二維碼圖片加背景 * */ public static Bitmap addBackground(Bitmap foreground,Bitmap background){ int bgWidth = background.getWidth(); int bgHeight = background.getHeight(); int fgWidth = foreground.getWidth(); int fgHeight = foreground.getHeight(); Bitmap newmap = Bitmap .createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(newmap); canvas.drawBitmap(background, 0, 0, null); canvas.drawBitmap(foreground, (bgWidth - fgWidth) / 2, (bgHeight - fgHeight) *3 / 5+70, null); canvas.save(Canvas.ALL_SAVE_FLAG); canvas.restore(); return newmap; }
這樣效果就變?yōu)椋?/p>
第五步:給二維碼加水印
然后二維碼的個(gè)性化制作就最后一步了:加水印,位置隨便放
/** * 在圖片右下角添加水印 * * @param srcBMP * 原圖 * @param markBMP * 水印圖片 * @return 合成水印后的圖片 */ public static Bitmap composeWatermark(Bitmap srcBMP, Bitmap markBMP) { if (srcBMP == null) { return null; } // 創(chuàng)建一個(gè)新的和SRC長(zhǎng)度寬度一樣的位圖 Bitmap newb = Bitmap.createBitmap(srcBMP.getWidth(), srcBMP.getHeight(), Bitmap.Config.ARGB_8888); Canvas cv = new Canvas(newb); // 在 0,0坐標(biāo)開始畫入原圖 cv.drawBitmap(srcBMP, 0, 0, null); // 在原圖的右下角畫入水印 cv.drawBitmap(markBMP, srcBMP.getWidth() - markBMP.getWidth()*4/5, srcBMP.getHeight()*2/7 , null); // 保存 cv.save(Canvas.ALL_SAVE_FLAG); // 存儲(chǔ) cv.restore(); return newb; }
這里貼下實(shí)現(xiàn)二維碼個(gè)性化的完整代碼類:
package com.ry.personalizedcode.uitls; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import java.util.Hashtable; import java.util.Random; /** * Created on 2016/2/24. * 生成二維碼的工具類 */ public class MakeQRCodeUtil { /** * 根據(jù)指定內(nèi)容生成自定義寬高的二維碼圖片 * * param logoBm * logo圖標(biāo) * param content * 需要生成二維碼的內(nèi)容 * param width * 二維碼寬度 * param height * 二維碼高度 * throws WriterException * 生成二維碼異常 */ public static Bitmap makeQRImage(Bitmap logoBmp, String content, int QR_WIDTH, int QR_HEIGHT) throws WriterException { try { // 圖像數(shù)據(jù)轉(zhuǎn)換,使用了矩陣轉(zhuǎn)換 Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);// 容錯(cuò)率 hints.put(EncodeHintType.MARGIN, 2); // default is 4 hints.put(EncodeHintType.MAX_SIZE, 350); hints.put(EncodeHintType.MIN_SIZE, 100); BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints); int[] pixels = new int[QR_WIDTH * QR_HEIGHT]; for (int y = 0; y < QR_HEIGHT; y++) { // 下面這里按照二維碼的算法,逐個(gè)生成二維碼的圖片,//兩個(gè)for循環(huán)是圖片橫列掃描的結(jié)果 for (int x = 0; x < QR_WIDTH; x++) { if (bitMatrix.get(x, y)) { if (x < QR_WIDTH / 2 && y < QR_HEIGHT / 2) { pixels[y * QR_WIDTH + x] = 0xFF0094FF;// 藍(lán)色 Integer.toHexString(new Random().nextInt()); } else if (x < QR_WIDTH / 2 && y > QR_HEIGHT / 2) { pixels[y * QR_WIDTH + x] = 0xFFFED545;// 黃色 } else if (x > QR_WIDTH / 2 && y > QR_HEIGHT / 2) { pixels[y * QR_WIDTH + x] = 0xFF5ACF00;// 綠色 } else { pixels[y * QR_WIDTH + x] = 0xFF000000;// 黑色 } } else { pixels[y * QR_WIDTH + x] = 0xffffffff;// 白色 } } } // ------------------添加圖片部分------------------// Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888); // 設(shè)置像素點(diǎn) bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT); // 獲取圖片寬高 int logoWidth = logoBmp.getWidth(); int logoHeight = logoBmp.getHeight(); if (QR_WIDTH == 0 || QR_HEIGHT == 0) { return null; } if (logoWidth == 0 || logoHeight == 0) { return bitmap; } // 圖片繪制在二維碼中央,合成二維碼圖片 // logo大小為二維碼整體大小的1/2 float scaleFactor = QR_WIDTH * 1.0f / 2 / logoWidth; try { Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(bitmap, 0, 0, null); canvas.scale(scaleFactor, scaleFactor, QR_WIDTH / 2, QR_HEIGHT / 2); canvas.drawBitmap(logoBmp, (QR_WIDTH - logoWidth) / 2, (QR_HEIGHT - logoHeight) /2, null); canvas.save(Canvas.ALL_SAVE_FLAG); canvas.restore(); return bitmap; } catch (Exception e) { bitmap = null; e.getStackTrace(); } } catch (WriterException e) { e.printStackTrace(); } return null; } /** * 獲取十六進(jìn)制的顏色代碼.例如 "#6E36B4" , For HTML , * @return String */ public static String getRandColorCode(){ String r,g,b; Random random = new Random(); r = Integer.toHexString(random.nextInt(256)).toUpperCase(); g = Integer.toHexString(random.nextInt(256)).toUpperCase(); b = Integer.toHexString(random.nextInt(256)).toUpperCase(); r = r.length()==1 ? "0" + r : r ; g = g.length()==1 ? "0" + g : g ; b = b.length()==1 ? "0" + b : b ; return r+g+b; } /** * 根據(jù)指定內(nèi)容生成自定義寬高的二維碼圖片 * * @param content * 需要生成二維碼的內(nèi)容 * @param width * 二維碼寬度 * @param height * 二維碼高度 * @throws WriterException * 生成二維碼異常 */ public static Bitmap makeQRImage(String content, int width, int height) throws WriterException { Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>(); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 圖像數(shù)據(jù)轉(zhuǎn)換,使用了矩陣轉(zhuǎn)換 BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); int[] pixels = new int[width * height]; // 按照二維碼的算法,逐個(gè)生成二維碼的圖片,兩個(gè)for循環(huán)是圖片橫列掃描的結(jié)果 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; } /** * 從資源文件中獲取圖片 * * @param context * 上下文 * @param drawableId * 資源文件id * @return */ public static Bitmap gainBitmap(Context context, int drawableId) { Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), drawableId); return bmp; } /** * 在圖片右下角添加水印 * * @param srcBMP * 原圖 * @param markBMP * 水印圖片 * @return 合成水印后的圖片 */ public static Bitmap composeWatermark(Bitmap srcBMP, Bitmap markBMP) { if (srcBMP == null) { return null; } // 創(chuàng)建一個(gè)新的和SRC長(zhǎng)度寬度一樣的位圖 Bitmap newb = Bitmap.createBitmap(srcBMP.getWidth(), srcBMP.getHeight(), Bitmap.Config.ARGB_8888); Canvas cv = new Canvas(newb); // 在 0,0坐標(biāo)開始畫入原圖 cv.drawBitmap(srcBMP, 0, 0, null); // 在原圖的右下角畫入水印 cv.drawBitmap(markBMP, srcBMP.getWidth() - markBMP.getWidth()*4/5, srcBMP.getHeight()*2/7 , null); // 保存 cv.save(Canvas.ALL_SAVE_FLAG); // 存儲(chǔ) cv.restore(); return newb; } /** * 給二維碼圖片加背景 * */ public static Bitmap addBackground(Bitmap foreground,Bitmap background){ int bgWidth = background.getWidth(); int bgHeight = background.getHeight(); int fgWidth = foreground.getWidth(); int fgHeight = foreground.getHeight(); Bitmap newmap = Bitmap .createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(newmap); canvas.drawBitmap(background, 0, 0, null); canvas.drawBitmap(foreground, (bgWidth - fgWidth) / 2, (bgHeight - fgHeight) *3 / 5+70, null); canvas.save(Canvas.ALL_SAVE_FLAG); canvas.restore(); return newmap; } }
第六步:給二維碼實(shí)現(xiàn)長(zhǎng)按功能
最后為了模擬下qq的查看二維碼名片功能,還加了一個(gè)長(zhǎng)按彈出actionSheet的功能。
看效果:
具體的 安卓版actionSheet的實(shí)現(xiàn),前面博客有介紹需要的請(qǐng)移步。
這里我們先來實(shí)現(xiàn)發(fā)送給好友功能:(這里就不做第三方的發(fā)送)
private void sendToFriends() { Intent intent=new Intent(Intent.ACTION_SEND); Uri imageUri= Uri.parse(Environment.getExternalStorageDirectory()+"/code/qrcode.jpg"); intent.setType("image/*"); intent.putExtra(Intent.EXTRA_STREAM, imageUri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(Intent.createChooser(intent, getTitle())); }
發(fā)送給朋友效果圖:
然后就是要實(shí)現(xiàn)保存到本地圖庫的功能:
/** * 先保存到本地再廣播到圖庫 * */ public static void saveImageToGallery(Context context, Bitmap bmp) { // 首先保存圖片 File appDir = new File(Environment.getExternalStorageDirectory(), "code"); if (!appDir.exists()) { appDir.mkdir(); } String fileName = "qrcode.jpg"; file = new File(appDir, fileName); try { FileOutputStream fos = new FileOutputStream(file); bmp.compress(CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 其次把文件插入到系統(tǒng)圖庫 try { MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null); // 最后通知圖庫更新 context.sendBroadcast(new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file))); } catch (FileNotFoundException e) { e.printStackTrace(); } }
總結(jié):
這篇說白了就是使用了大量的Canvas和bitmap的處理,然后篇幅也是有點(diǎn)長(zhǎng),看起來也是有點(diǎn)累。要看完整的代碼請(qǐng)自己下載PersonalizedCode.rar。下一篇我準(zhǔn)備寫webView中的二維碼圖片長(zhǎng)按識(shí)別二維碼功能。
- Android實(shí)現(xiàn)二維碼掃描和生成的簡(jiǎn)單方法
- Android上使用ZXing識(shí)別條形碼與二維碼的方法
- iOS和Android用同一個(gè)二維碼實(shí)現(xiàn)跳轉(zhuǎn)下載鏈接的方法
- Android開發(fā)框架之自定義ZXing二維碼掃描界面并解決取景框拉伸問題
- Android項(xiàng)目實(shí)戰(zhàn)(二十八):使用Zxing實(shí)現(xiàn)二維碼及優(yōu)化實(shí)例
- Android基于google Zxing實(shí)現(xiàn)各類二維碼掃描效果
- Android 二維碼 生成和識(shí)別二維碼 附源碼下載
- Android平臺(tái)生成二維碼并實(shí)現(xiàn)掃描 & 識(shí)別功能
- Android編程實(shí)現(xiàn)二維碼的生成與解析
- Android掃描和生成二維碼
相關(guān)文章
Android中View的炸裂特效實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Android中View的炸裂特效實(shí)現(xiàn)方法,涉及Android組件ExplosionField的相關(guān)定義與使用技巧,需要的朋友可以參考下2016-07-07Android編程之代碼創(chuàng)建布局實(shí)例分析
這篇文章主要介紹了Android編程之代碼創(chuàng)建布局的方法,結(jié)合實(shí)例形式分析了Android通過代碼創(chuàng)建布局的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android中的人臉檢測(cè)的示例代碼(靜態(tài)和動(dòng)態(tài))
本篇文章主要介紹了Android中的人臉檢測(cè)的示例代碼(靜態(tài)和動(dòng)態(tài)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01Android實(shí)現(xiàn)頁面跳轉(zhuǎn)的全過程記錄
對(duì)于android軟件開發(fā)初級(jí)學(xué)習(xí)者來說,簡(jiǎn)單的頁面跳轉(zhuǎn)是必學(xué)的,這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)頁面跳轉(zhuǎn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10Android自定義View實(shí)現(xiàn)隨機(jī)數(shù)驗(yàn)證碼
這篇文章主要為大家詳細(xì)介紹了Android如何利用自定義View實(shí)現(xiàn)隨機(jī)數(shù)驗(yàn)證碼效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-06-06Android開發(fā)實(shí)現(xiàn)瀏覽器全屏顯示功能
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)瀏覽器全屏顯示功能,涉及Android布局修改及相關(guān)屬性動(dòng)態(tài)設(shè)置操作技巧,需要的朋友可以參考下2017-09-09Android的RV列表刷新詳解Payload與Diff方式異同
這篇文章主要為大家介紹了Android的RV列表刷新詳解Payload與Diff方式異同,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10