From 22e72d47883e5aeb6482018d5ac99ea59b78b132 Mon Sep 17 00:00:00 2001 From: Makito Date: Mon, 8 May 2017 00:17:09 +0800 Subject: [PATCH 1/2] New feature: Embedding a logo image in the QR code; sample/demo application updated; --- .idea/misc.xml | 2 +- README.md | 69 +- app/build.gradle | 4 +- .../awesomeqrsample/MainActivity.java | 95 ++- app/src/main/res/layout/activity_main.xml | 639 +++++++++++------- art/awesome-qr-6.png | Bin 0 -> 484843 bytes library/build.gradle | 4 +- .../sumimakito/awesomeqr/AwesomeQRCode.java | 197 ++++-- 8 files changed, 664 insertions(+), 346 deletions(-) create mode 100644 art/awesome-qr-6.png diff --git a/.idea/misc.xml b/.idea/misc.xml index fbb6828..5d19981 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ - + diff --git a/README.md b/README.md index 95a825a..f845649 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,11 @@ Example 1|Example 2|Example 3 | | -Rounded data dots|Binarized background +Solid dots instead of blocks|Binarized|With logo at the center ------------ | ------------- - | + | | -### Add dependency, 添加依赖项 +### Add dependency into your project, 添加依赖项 Add below lines in build.gradle of your project: ``` @@ -38,37 +38,72 @@ allprojects { Then, add below lines in build.gradle of your app module: ``` dependencies { - compile 'com.github.SumiMakito:AwesomeQRCode:1.0.3' + compile 'com.github.SumiMakito:AwesomeQRCode:1.0.4' } ``` ### Quick start, 快速上手 ```java -Bitmap qrCode = AwesomeQRCode.create("Makito loves Kafuu Chino.", 800, 20, 0.3f, Color.BLACK, Color.WHITE, backgroundBitmap, true, true); +Bitmap qrCode = AwesomeQRCode.create("Makito loves Kafuu Chino.", 800, 20); +Bitmap qrCodeWithBackground = AwesomeQRCode.create("Makito loves Kafuu Chino.", 800, 20, backgroundBitmap); +Bitmap qrCodeWithLogo = AwesomeQRCode.create("Makito loves Kafuu Chino.", 800, 20, null, logoBitmap); +Bitmap qrCodeWithBackgroundAndLogo = AwesomeQRCode.create("Makito loves Kafuu Chino.", 800, 20, backgroundBitmap, logoBitmap); ``` ### Parameters, 参数 +> Here's a full list of all parameters, but some of them are optional. You can view all the possible simplified methods at the end of AwesomeQRCode.java. + +> (Translation) 以下列出全部参数,但其中部分参数是可选的。关于简化的调用方法,请查看 AwesomeQRCode.java 的结尾部分。 + ```java public static Bitmap create( - String contents, // Contents to encode. 欲编码的内容 - int size, // Width as well as the height of the output QR code, includes margin. 尺寸, 长宽一致 - int margin, // Margin to add around the QR code. 二维码边缘的外边距 - float dataDotScale, // Scale the data blocks and makes them appear smaller. 数据点缩小比例 (0 < scale < 1.0f) - int colorDark, // Color of blocks. Will be OVERRIDE by autoColor. (BYTE_DTA, BYTE_POS, BYTE_AGN, BYTE_TMG) 实点的颜色 - int colorLight, // Color of empty space. Will be OVERRIDE by autoColor. (BYTE_EPT) 空白点的颜色 - Bitmap backgroundImage, // The background image to embed in the QR code. If null, no background image will be embedded. 欲嵌入的背景图 - boolean whiteMargin, // If true, background image will not be drawn on the margin area. Default is true. 若为 true, 则背景图将不会绘制到外边距区域 - boolean autoColor, // If true, colorDark will be set to the dominant color of backgroundImage. Default is true. 若为 true, 则将从背景图取主要颜色作为实点颜色 - boolean binarize, // If true, background images will be binarized. Default is false. 若为 true, 背景图像将被二值化处理 - int binarizeThreshold, // Threshold value used while binarizing background images. Default is 128. 0 < threshold < 255. 控制背景图像二值化的阈值 - boolean roundedDataDots // If true, data blocks will appear as filled circles. Default is false. 若为 true, 数据点将以圆形绘制 + String contents, + int size, + int margin, + float dataDotScale, + int colorDark, + int colorLight, + Bitmap backgroundImage, + boolean whiteMargin, + boolean autoColor, + boolean binarize, + int binarizeThreshold, + boolean roundedDataDots, + Bitmap logoImage, + int logoMargin, + int logoCornerRadius, + float logoScale ) throws IllegalArgumentException { ... } ``` +Parameter | Explanation +----|---- +contents (String) | Contents to encode. 欲编码的内容 +size (int-px) | Width as well as the height of the output QR code, includes margin. 尺寸, 长宽一致, 包含外边距 +margin (int-px) | Margin to add around the QR code. 二维码图像的外边距, 默认 20px +dataDotScale (float) | Value used to scale down the data dots' size. (0 < scale < 1.0f) 数据区域点缩小比例 +colorDark (int-color) | Color of "true" blocks. Works only when both colorDark and colorLight are set. (BYTE_DTA, BYTE_POS, BYTE_AGN, BYTE_TMG) 实点的颜色 +colorLight (int-color) | Color of empty space, or "false" blocks. Works only when both colorDark and colorLight are set. (BYTE_EPT) 空白区的颜色 +backgroundImage (Bitmap) | Background image to embed in the QR code. Leave null to disable. 欲嵌入的背景图, 设为 null 以禁用 +whiteMargin (int-px) | If set to true, a white border will appear around the background image. Default is true. 若设为 true, 背景图外将绘制白色边框 +autoColor (boolean) | If set to true, the dominant color of backgroundImage will be used as colorDark. Default is true. 若为 true, 背景图的主要颜色将作为实点的颜色, 即 colorDark +binarize (boolean) | If set to true, the whole image will be binarized with the given threshold, or default threshold if not specified. Default is false. 若为 true, 背景图像将被二值化处理, 未指定阈值则使用默认值 +binarizeThreshold (int) | Threshold used to binarize the whole image. Default is 128. (0 < threshold < 255) 二值化处理的阈值 +roundedDataDots (boolean) | If set to true, data dots will appear as solid dots instead of blocks. Default is false. 若为 true, 数据点将以圆点绘制, 取代默认的小方块 +logoImage (Bitmap) | Logo image to embed at the center of generated QR code. Leave null to disable. 欲嵌入至二维码中心的 LOGO 标识, 设为 null 以禁用 +logoMargin (int-px) | White margin that appears around the logo image. Leave 0 to disable. LOGO 标识周围的空白边框, 设为 0 以禁用 +logoCornerRadius (int-px) | Radius of the logo's corners. Leave 0 to disable. LOGO 标识及其边框的圆角半径, 设为 0 以禁用 +logoScale (float) | Value used to scale the logo image. Larger value may result in decode failure. Size of the logo equals to `logoScale*(size-2*margin)`. Default is 0.2f. 用于计算 LOGO 大小的值, 过大将导致解码失败, LOGO 尺寸计算公式 `logoScale*(size-2*margin)`, 默认 0.2f + + ### Changelog, 更新日志 +#### 1.0.4 +New feature: Embedding a logo image in the QR code. +Sample/Demo application updated. + #### 1.0.3 Added CHARACTER_SET => UTF-8 to QR code's hints before encoding. Fixed an encoding issue mentioned in [#7](https://github.com/SumiMakito/AwesomeQRCode/issues/7). diff --git a/app/build.gradle b/app/build.gradle index 3b08c6c..d0f565a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.github.sumimakito.awesomeqrsample" minSdkVersion 19 targetSdkVersion 25 - versionCode 7 - versionName "1.6" + versionCode 8 + versionName "1.7" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/java/com/github/sumimakito/awesomeqrsample/MainActivity.java b/app/src/main/java/com/github/sumimakito/awesomeqrsample/MainActivity.java index d270663..1856300 100644 --- a/app/src/main/java/com/github/sumimakito/awesomeqrsample/MainActivity.java +++ b/app/src/main/java/com/github/sumimakito/awesomeqrsample/MainActivity.java @@ -13,8 +13,9 @@ import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; -import android.util.Base64; +import android.view.KeyEvent; import android.view.View; +import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -28,13 +29,12 @@ import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.IOException; public class MainActivity extends AppCompatActivity { - private final int SELECT_FILE_REQUEST_CODE = 822; + private final int BKG_IMAGE = 822; + private final int LOGO_IMAGE = 379; private ImageView qrCodeImageView; private EditText etColorLight, etColorDark, etContents, etMargin, etSize; @@ -53,12 +53,23 @@ public class MainActivity extends AppCompatActivity { private EditText etBinarizeThreshold; private Bitmap qrBitmap; private Button btOpen; + private EditText etLogoMargin; + private EditText etLogoScale; + private EditText etLogoCornerRadius; + private Button btRemoveLogoImage; + private Button btSelectLogo; + private Bitmap logoImage; + private ViewGroup configViewContainer, resultViewContainer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + configViewContainer = (ViewGroup) findViewById(R.id.configViewContainer); + resultViewContainer = (ViewGroup) findViewById(R.id.resultViewContainer); + + scrollView = (ScrollView) findViewById(R.id.scrollView); scrollView = (ScrollView) findViewById(R.id.scrollView); tvAuthorHint = (TextView) findViewById(R.id.authorHint); tvJSHint = (TextView) findViewById(R.id.jsHint); @@ -70,7 +81,9 @@ protected void onCreate(Bundle savedInstanceState) { etMargin = (EditText) findViewById(R.id.margin); etDotScale = (EditText) findViewById(R.id.dotScale); btSelectBG = (Button) findViewById(R.id.backgroundImage); + btSelectLogo = (Button) findViewById(R.id.logoImage); btRemoveBackgroundImage = (Button) findViewById(R.id.removeBackgroundImage); + btRemoveLogoImage = (Button) findViewById(R.id.removeLogoImage); btGenerate = (Button) findViewById(R.id.generate); btOpen = (Button) findViewById(R.id.open); ckbWhiteMargin = (CheckBox) findViewById(R.id.whiteMargin); @@ -78,6 +91,10 @@ protected void onCreate(Bundle savedInstanceState) { ckbBinarize = (CheckBox) findViewById(R.id.binarize); ckbRoundedDataDots = (CheckBox) findViewById(R.id.rounded); etBinarizeThreshold = (EditText) findViewById(R.id.binarizeThreshold); + etLogoMargin = (EditText) findViewById(R.id.logoMargin); + etLogoScale = (EditText) findViewById(R.id.logoScale); + etLogoCornerRadius = (EditText) findViewById(R.id.logoRadius); + etBinarizeThreshold = (EditText) findViewById(R.id.binarizeThreshold); ckbAutoColor.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override @@ -100,7 +117,17 @@ public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); - startActivityForResult(intent, SELECT_FILE_REQUEST_CODE); + startActivityForResult(intent, BKG_IMAGE); + } + }); + + btSelectLogo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("image/*"); + startActivityForResult(intent, LOGO_IMAGE); } }); @@ -119,6 +146,14 @@ public void onClick(View v) { } }); + btRemoveLogoImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + logoImage = null; + Toast.makeText(MainActivity.this, "Logo image removed.", Toast.LENGTH_SHORT).show(); + } + }); + btGenerate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -134,7 +169,11 @@ public void onClick(View v) { ckbAutoColor.isChecked(), ckbBinarize.isChecked(), etBinarizeThreshold.getText().length() == 0 ? 128 : Integer.parseInt(etBinarizeThreshold.getText().toString()), - ckbRoundedDataDots.isChecked() + ckbRoundedDataDots.isChecked(), + logoImage, + etLogoMargin.getText().length() == 0 ? 10 : Integer.parseInt(etLogoMargin.getText().toString()), + etLogoCornerRadius.getText().length() == 0 ? 8 : Integer.parseInt(etLogoCornerRadius.getText().toString()), + etLogoScale.getText().length() == 0 ? 10 : Float.parseFloat(etLogoScale.getText().toString()) ); } catch (Exception e) { Toast.makeText(MainActivity.this, "Error occurred, please check your configs.", Toast.LENGTH_LONG).show(); @@ -190,14 +229,23 @@ private void acquireStoragePermissions() { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == SELECT_FILE_REQUEST_CODE && resultCode == RESULT_OK && data.getData() != null) { + if (resultCode == RESULT_OK && data.getData() != null) { try { Uri imageUri = data.getData(); - backgroundImage = BitmapFactory.decodeFile(ContentHelper.absolutePathFromUri(this, imageUri)); - Toast.makeText(this, "Background image added.", Toast.LENGTH_SHORT).show(); + if (requestCode == BKG_IMAGE) { + backgroundImage = BitmapFactory.decodeFile(ContentHelper.absolutePathFromUri(this, imageUri)); + Toast.makeText(this, "Background image added.", Toast.LENGTH_SHORT).show(); + } else if (requestCode == LOGO_IMAGE) { + logoImage = BitmapFactory.decodeFile(ContentHelper.absolutePathFromUri(this, imageUri)); + Toast.makeText(this, "Logo image added.", Toast.LENGTH_SHORT).show(); + } } catch (Exception e) { e.printStackTrace(); - Toast.makeText(this, "Failed to add the background image.", Toast.LENGTH_SHORT).show(); + if (requestCode == BKG_IMAGE) { + Toast.makeText(this, "Failed to add the background image.", Toast.LENGTH_SHORT).show(); + } else if (requestCode == LOGO_IMAGE) { + Toast.makeText(this, "Failed to add the logo image.", Toast.LENGTH_SHORT).show(); + } } } super.onActivityResult(requestCode, resultCode, data); @@ -205,7 +253,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { private void generate(final String contents, final int size, final int margin, final float dotScale, final int colorDark, final int colorLight, final Bitmap background, final boolean whiteMargin, - final boolean autoColor, final boolean binarize, final int binarizeThreshold, final boolean roundedDD) { + final boolean autoColor, final boolean binarize, final int binarizeThreshold, final boolean roundedDD, + final Bitmap logoImage, final int logoMargin, final int logoCornerRadius, final float logoScale) { if (generating) return; generating = true; progressDialog = new ProgressDialog.Builder(this).setMessage("Generating...").setCancelable(false).create(); @@ -216,17 +265,13 @@ public void run() { try { qrBitmap = AwesomeQRCode.create(contents, size, margin, dotScale, colorDark, colorLight, background, whiteMargin, autoColor, binarize, binarizeThreshold, - roundedDD); + roundedDD, logoImage, logoMargin, logoCornerRadius, logoScale); runOnUiThread(new Runnable() { @Override public void run() { qrCodeImageView.setImageBitmap(qrBitmap); - scrollView.post(new Runnable() { - @Override - public void run() { - scrollView.fullScroll(View.FOCUS_DOWN); - } - }); + configViewContainer.setVisibility(View.GONE); + resultViewContainer.setVisibility(View.VISIBLE); if (progressDialog != null) progressDialog.dismiss(); generating = false; } @@ -237,6 +282,8 @@ public void run() { @Override public void run() { if (progressDialog != null) progressDialog.dismiss(); + configViewContainer.setVisibility(View.VISIBLE); + resultViewContainer.setVisibility(View.GONE); generating = false; } }); @@ -271,4 +318,16 @@ public static File getPublicContainer() { aqr.mkdirs(); return aqr; } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (configViewContainer.getVisibility() != View.VISIBLE) { + configViewContainer.setVisibility(View.VISIBLE); + resultViewContainer.setVisibility(View.GONE); + return true; + } + } + return super.onKeyDown(keyCode, event); + } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 049fcab..51edac5 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,270 +8,405 @@ android:orientation="vertical" tools:context="com.github.sumimakito.awesomeqrsample.MainActivity"> - + + + android:layout_below="@+id/authorHint" + android:orientation="vertical" + android:visibility="visible"> - + android:layout_height="match_parent" + android:layout_above="@+id/generate" + android:clipToPadding="false" + android:fadingEdge="vertical" + android:fillViewport="true"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -