更新二维码扫码

pull/840/head
gedoor 4 years ago
parent 5e1f106d1b
commit d7a7f35bd7
  1. 2
      app/build.gradle
  2. 74
      app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
  3. 13
      app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt
  4. 29
      app/src/main/java/io/legado/app/utils/BitmapUtils.kt
  5. 7
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  6. 467
      app/src/main/java/io/legado/app/utils/QRCodeUtils.kt
  7. 72
      app/src/main/res/layout/activity_qrcode_capture.xml

@ -185,7 +185,7 @@ dependencies {
implementation 'org.nanohttpd:nanohttpd-websocket:2.3.1' implementation 'org.nanohttpd:nanohttpd-websocket:2.3.1'
// //
implementation 'cn.bingoogolapple:bga-qrcode-zxing:1.3.7' implementation 'com.king.zxing:zxing-lite:2.0.2'
// //
implementation 'com.jaredrummler:colorpicker:1.1.0' implementation 'com.jaredrummler:colorpicker:1.1.0'

@ -6,36 +6,29 @@ import android.graphics.BitmapFactory
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import com.google.zxing.Result
import cn.bingoogolapple.qrcode.core.QRCodeView import com.king.zxing.CameraScan.OnScanResultCallback
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseActivity import io.legado.app.base.BaseActivity
import io.legado.app.databinding.ActivityQrcodeCaptureBinding import io.legado.app.databinding.ActivityQrcodeCaptureBinding
import io.legado.app.help.permission.Permissions import io.legado.app.utils.QRCodeUtils
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.utils.readBytes import io.legado.app.utils.readBytes
import org.jetbrains.anko.toast
class QrCodeActivity : BaseActivity<ActivityQrcodeCaptureBinding>(), QRCodeView.Delegate { class QrCodeActivity : BaseActivity<ActivityQrcodeCaptureBinding>(), OnScanResultCallback {
private val requestQrImage = 202 private val requestQrImage = 202
private var flashlightIsOpen: Boolean = false
override fun getViewBinding(): ActivityQrcodeCaptureBinding { override fun getViewBinding(): ActivityQrcodeCaptureBinding {
return ActivityQrcodeCaptureBinding.inflate(layoutInflater) return ActivityQrcodeCaptureBinding.inflate(layoutInflater)
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.zXingView.setDelegate(this) val fTag = "qrCodeFragment"
binding.fabFlashlight.setOnClickListener { var qrCodeFragment = supportFragmentManager.findFragmentByTag(fTag)
if (flashlightIsOpen) { if (qrCodeFragment == null) qrCodeFragment = QrCodeFragment()
flashlightIsOpen = false supportFragmentManager.beginTransaction()
binding.zXingView.closeFlashlight() .replace(R.id.fl_content, qrCodeFragment, fTag)
} else { .commit()
flashlightIsOpen = true
binding.zXingView.openFlashlight()
}
}
} }
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
@ -55,60 +48,21 @@ class QrCodeActivity : BaseActivity<ActivityQrcodeCaptureBinding>(), QRCodeView.
return super.onCompatOptionsItemSelected(item) return super.onCompatOptionsItemSelected(item)
} }
override fun onStart() { override fun onScanResultCallback(result: Result?): Boolean {
super.onStart()
startCamera()
}
private fun startCamera() {
PermissionsCompat.Builder(this)
.addPermissions(*Permissions.Group.CAMERA)
.rationale(R.string.qr_per)
.onGranted {
binding.zXingView.visibility = View.VISIBLE
//TODO 显示扫描框,并开始识别
binding.zXingView.startSpotAndShowRect()
}.request()
}
override fun onStop() {
//TODO 关闭摄像头预览,并且隐藏扫描框
binding.zXingView.stopCamera()
super.onStop()
}
override fun onDestroy() {
//TODO 销毁二维码扫描控件
binding.zXingView.onDestroy()
super.onDestroy()
}
override fun onScanQRCodeSuccess(result: String) {
val intent = Intent() val intent = Intent()
intent.putExtra("result", result) intent.putExtra("result", result?.text)
setResult(RESULT_OK, intent) setResult(RESULT_OK, intent)
finish() finish()
} return true
override fun onCameraAmbientBrightnessChanged(isDark: Boolean) {
}
override fun onScanQRCodeOpenCameraError() {
toast("打开相机失败")
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
data?.data?.let { data?.data?.let {
//TODO 显示扫描框,并开始识别
binding.zXingView.startSpotAndShowRect()
if (resultCode == Activity.RESULT_OK && requestCode == requestQrImage) { if (resultCode == Activity.RESULT_OK && requestCode == requestQrImage) {
// 本来就用到 QRCodeView 时可直接调 QRCodeView 的方法,走通用的回调
it.readBytes(this)?.let { bytes -> it.readBytes(this)?.let { bytes ->
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
binding.zXingView.decodeQRCode(bitmap) onScanResultCallback(QRCodeUtils.parseCodeResult(bitmap))
} }
} }
} }

@ -0,0 +1,13 @@
package io.legado.app.ui.qrcode
import com.google.zxing.Result
import com.king.zxing.CaptureFragment
class QrCodeFragment : CaptureFragment() {
override fun onScanResultCallback(result: Result?): Boolean {
(activity as? QrCodeActivity)?.onScanResultCallback(result)
return true
}
}

@ -1,11 +1,8 @@
package io.legado.app.utils package io.legado.app.utils
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.*
import android.graphics.Bitmap.Config import android.graphics.Bitmap.Config
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.renderscript.Allocation import android.renderscript.Allocation
import android.renderscript.Element import android.renderscript.Element
import android.renderscript.RenderScript import android.renderscript.RenderScript
@ -221,6 +218,30 @@ object BitmapUtils {
} }
} }
fun changeBitmapSize(bitmap: Bitmap, newWidth: Int, newHeight: Int): Bitmap {
val width = bitmap.width
val height = bitmap.height
//计算压缩的比率
var scaleWidth = newWidth.toFloat() / width
var scaleHeight = newHeight.toFloat() / height
if (scaleWidth > scaleHeight) {
scaleWidth = scaleHeight
} else {
scaleHeight = scaleWidth
}
//获取想要缩放的matrix
val matrix = Matrix()
matrix.postScale(scaleWidth, scaleHeight)
//获取新的bitmap
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true)
}
/** /**
* 高斯模糊 * 高斯模糊
*/ */

@ -18,9 +18,6 @@ import androidx.annotation.StringRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.content.edit import androidx.core.content.edit
import cn.bingoogolapple.qrcode.zxing.QRCodeEncoder
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import io.legado.app.BuildConfig import io.legado.app.BuildConfig
import io.legado.app.R import io.legado.app.R
import org.jetbrains.anko.defaultSharedPreferences import org.jetbrains.anko.defaultSharedPreferences
@ -104,9 +101,7 @@ val Context.navigationBarHeight: Int
@SuppressLint("SetWorldReadable") @SuppressLint("SetWorldReadable")
fun Context.shareWithQr(title: String, text: String) { fun Context.shareWithQr(title: String, text: String) {
QRCodeEncoder.HINTS[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.L val bitmap = QRCodeUtils.createQRCode(text)
val bitmap = QRCodeEncoder.syncEncodeQRCode(text, 600)
QRCodeEncoder.HINTS[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H
if (bitmap == null) { if (bitmap == null) {
toast(R.string.text_too_long_qr_error) toast(R.string.text_too_long_qr_error)
} else { } else {

@ -0,0 +1,467 @@
package io.legado.app.utils
import android.graphics.*
import android.text.TextPaint
import android.text.TextUtils
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
import com.google.zxing.*
import com.google.zxing.common.GlobalHistogramBinarizer
import com.google.zxing.common.HybridBinarizer
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.king.zxing.DecodeFormatManager
import com.king.zxing.util.LogUtils
import java.util.*
import kotlin.math.max
@Suppress("MemberVisibilityCanBePrivate", "unused")
object QRCodeUtils {
const val DEFAULT_REQ_WIDTH = 480
const val DEFAULT_REQ_HEIGHT = 640
/**
* 生成二维码
* @param content 二维码的内容
* @param heightPix 二维码的高
* @param logo 二维码中间的logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3
* @return
*/
fun createQRCode(
content: String,
heightPix: Int = DEFAULT_REQ_HEIGHT,
logo: Bitmap? = null,
@FloatRange(from = 0.0, to = 1.0) ratio: Float = 0.2f,
errorCorrectionLevel: ErrorCorrectionLevel = ErrorCorrectionLevel.H
): Bitmap? {
//配置参数
val hints: MutableMap<EncodeHintType, Any> = EnumMap(EncodeHintType::class.java)
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
//容错级别
hints[EncodeHintType.ERROR_CORRECTION] = errorCorrectionLevel
//设置空白边距的宽度
hints[EncodeHintType.MARGIN] = 1 //default is 4
return createQRCode(content, heightPix, logo, ratio, hints)
}
/**
* 生成二维码
* @param content 二维码的内容
* @param heightPix 二维码的高
* @param logo 二维码中间的logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3
* @param hints
* @param codeColor 二维码的颜色
* @return
*/
fun createQRCode(
content: String?,
heightPix: Int,
logo: Bitmap?,
@FloatRange(from = 0.0, to = 1.0) ratio: Float = 0.2f,
hints: Map<EncodeHintType, *>,
codeColor: Int = Color.BLACK
): Bitmap? {
try {
// 图像数据转换,使用了矩阵转换
val bitMatrix =
QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, heightPix, heightPix, hints)
val pixels = IntArray(heightPix * heightPix)
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (y in 0 until heightPix) {
for (x in 0 until heightPix) {
if (bitMatrix[x, y]) {
pixels[y * heightPix + x] = codeColor
} else {
pixels[y * heightPix + x] = Color.WHITE
}
}
}
// 生成二维码图片的格式
var bitmap = Bitmap.createBitmap(heightPix, heightPix, Bitmap.Config.ARGB_8888)
bitmap!!.setPixels(pixels, 0, heightPix, 0, 0, heightPix, heightPix)
if (logo != null) {
bitmap = addLogo(bitmap, logo, ratio)
}
return bitmap
} catch (e: WriterException) {
LogUtils.w(e.message)
}
return null
}
/**
* 在二维码中间添加Logo图案
* @param src
* @param logo
* @param ratio logo所占比例 因为二维码的最大容错率为30%所以建议ratio的范围小于0.3
* @return
*/
private fun addLogo(
src: Bitmap?,
logo: Bitmap?,
@FloatRange(from = 0.0, to = 1.0) ratio: Float
): Bitmap? {
if (src == null) {
return null
}
if (logo == null) {
return src
}
//获取图片的宽高
val srcWidth = src.width
val srcHeight = src.height
val logoWidth = logo.width
val logoHeight = logo.height
if (srcWidth == 0 || srcHeight == 0) {
return null
}
if (logoWidth == 0 || logoHeight == 0) {
return src
}
//logo大小为二维码整体大小
val scaleFactor = srcWidth * ratio / logoWidth
var bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888)
try {
val canvas = Canvas(bitmap!!)
canvas.drawBitmap(src, 0f, 0f, null)
canvas.scale(
scaleFactor,
scaleFactor,
(srcWidth / 2).toFloat(),
(srcHeight / 2).toFloat()
)
canvas.drawBitmap(
logo,
((srcWidth - logoWidth) / 2).toFloat(),
((srcHeight - logoHeight) / 2).toFloat(),
null
)
canvas.save()
canvas.restore()
} catch (e: Exception) {
bitmap = null
LogUtils.w(e.message)
}
return bitmap
}
/**
* 解析一维码/二维码图片
* @param bitmap 解析的图片
* @param hints 解析编码类型
* @return
*/
fun parseCode(
bitmap: Bitmap,
reqWidth: Int = DEFAULT_REQ_WIDTH,
reqHeight: Int = DEFAULT_REQ_HEIGHT,
hints: Map<DecodeHintType?, Any?> = DecodeFormatManager.ALL_HINTS
): String? {
val result = parseCodeResult(bitmap, reqWidth, reqHeight, hints)
return result?.text
}
/**
* 解析一维码/二维码图片
* @param bitmap 解析的图片
* @param hints 解析编码类型
* @return
*/
fun parseCodeResult(
bitmap: Bitmap,
reqWidth: Int = DEFAULT_REQ_WIDTH,
reqHeight: Int = DEFAULT_REQ_HEIGHT,
hints: Map<DecodeHintType?, Any?> = DecodeFormatManager.ALL_HINTS
): Result? {
if (bitmap.width > reqWidth || bitmap.height > reqHeight) {
val bm = BitmapUtils.changeBitmapSize(bitmap, reqWidth, reqHeight)
return parseCodeResult(getRGBLuminanceSource(bm), hints)
}
return parseCodeResult(getRGBLuminanceSource(bitmap), hints)
}
/**
* 解析一维码/二维码图片
* @param source
* @param hints
* @return
*/
fun parseCodeResult(source: LuminanceSource?, hints: Map<DecodeHintType?, Any?>?): Result? {
var result: Result? = null
val reader = MultiFormatReader()
try {
reader.setHints(hints)
if (source != null) {
result = decodeInternal(reader, source)
if (result == null) {
result = decodeInternal(reader, source.invert())
}
if (result == null && source.isRotateSupported) {
result = decodeInternal(reader, source.rotateCounterClockwise())
}
}
} catch (e: java.lang.Exception) {
LogUtils.w(e.message)
} finally {
reader.reset()
}
return result
}
/**
* 解析二维码图片
* @param bitmapPath 需要解析的图片路径
* @return
*/
fun parseQRCode(bitmapPath: String?): String? {
val result = parseQRCodeResult(bitmapPath)
return result?.text
}
/**
* 解析二维码图片
* @param bitmapPath 需要解析的图片路径
* @param reqWidth 请求目标宽度如果实际图片宽度大于此值会自动进行压缩处理 reqWidth reqHeight都小于或等于0时则不进行压缩处理
* @param reqHeight 请求目标高度如果实际图片高度大于此值会自动进行压缩处理 reqWidth reqHeight都小于或等于0时则不进行压缩处理
* @return
*/
fun parseQRCodeResult(
bitmapPath: String?,
reqWidth: Int = DEFAULT_REQ_WIDTH,
reqHeight: Int = DEFAULT_REQ_HEIGHT
): Result? {
return parseCodeResult(bitmapPath, reqWidth, reqHeight, DecodeFormatManager.QR_CODE_HINTS)
}
/**
* 解析一维码/二维码图片
* @param bitmapPath 需要解析的图片路径
* @return
*/
fun parseCode(
bitmapPath: String?,
reqWidth: Int = DEFAULT_REQ_WIDTH,
reqHeight: Int = DEFAULT_REQ_HEIGHT,
hints: Map<DecodeHintType?, Any?> = DecodeFormatManager.ALL_HINTS
): String? {
return parseCodeResult(bitmapPath, reqWidth, reqHeight, hints)?.text
}
/**
* 解析一维码/二维码图片
* @param bitmapPath 需要解析的图片路径
* @param reqWidth 请求目标宽度如果实际图片宽度大于此值会自动进行压缩处理 reqWidth reqHeight都小于或等于0时则不进行压缩处理
* @param reqHeight 请求目标高度如果实际图片高度大于此值会自动进行压缩处理 reqWidth reqHeight都小于或等于0时则不进行压缩处理
* @param hints 解析编码类型
* @return
*/
fun parseCodeResult(
bitmapPath: String?,
reqWidth: Int = DEFAULT_REQ_WIDTH,
reqHeight: Int = DEFAULT_REQ_HEIGHT,
hints: Map<DecodeHintType?, Any?> = DecodeFormatManager.ALL_HINTS
): Result? {
var result: Result? = null
val reader = MultiFormatReader()
try {
reader.setHints(hints)
val source = getRGBLuminanceSource(compressBitmap(bitmapPath, reqWidth, reqHeight))
result = decodeInternal(reader, source)
if (result == null) {
result = decodeInternal(reader, source.invert())
}
if (result == null && source.isRotateSupported) {
result = decodeInternal(reader, source.rotateCounterClockwise())
}
} catch (e: Exception) {
LogUtils.w(e.message)
} finally {
reader.reset()
}
return result
}
private fun decodeInternal(reader: MultiFormatReader, source: LuminanceSource): Result? {
var result: Result? = null
try {
try {
//采用HybridBinarizer解析
result = reader.decodeWithState(BinaryBitmap(HybridBinarizer(source)))
} catch (e: Exception) {
}
if (result == null) {
//如果没有解析成功,再采用GlobalHistogramBinarizer解析一次
result = reader.decodeWithState(BinaryBitmap(GlobalHistogramBinarizer(source)))
}
} catch (e: Exception) {
}
return result
}
/**
* 压缩图片
* @param path
* @return
*/
private fun compressBitmap(path: String?, reqWidth: Int, reqHeight: Int): Bitmap {
if (reqWidth > 0 && reqHeight > 0) { //都大于进行判断是否压缩
val newOpts = BitmapFactory.Options()
// 开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true //获取原始图片大小
BitmapFactory.decodeFile(path, newOpts) // 此时返回bm为空
val width = newOpts.outWidth.toFloat()
val height = newOpts.outHeight.toFloat()
// 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
var wSize = 1 // wSize=1表示不缩放
if (width > reqWidth) { // 如果宽度大的话根据宽度固定大小缩放
wSize = (width / reqWidth).toInt()
}
var hSize = 1 // wSize=1表示不缩放
if (height > reqHeight) { // 如果高度高的话根据宽度固定大小缩放
hSize = (height / reqHeight).toInt()
}
var size = max(wSize, hSize)
if (size <= 0) size = 1
newOpts.inSampleSize = size // 设置缩放比例
// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
newOpts.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, newOpts)
}
return BitmapFactory.decodeFile(path)
}
/**
* 获取RGBLuminanceSource
* @param bitmap
* @return
*/
private fun getRGBLuminanceSource(bitmap: Bitmap): RGBLuminanceSource {
val width = bitmap.width
val height = bitmap.height
val pixels = IntArray(width * height)
bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
return RGBLuminanceSource(width, height, pixels)
}
/**
* 生成条形码
* @param content
* @param format
* @param desiredWidth
* @param desiredHeight
* @param hints
* @param isShowText
* @param textSize
* @param codeColor
* @return
*/
fun createBarCode(
content: String?,
desiredWidth: Int,
desiredHeight: Int,
format: BarcodeFormat = BarcodeFormat.CODE_128,
hints: Map<EncodeHintType?, *>? = null,
isShowText: Boolean = true,
textSize: Int = 40,
@ColorInt codeColor: Int = Color.BLACK
): Bitmap? {
if (TextUtils.isEmpty(content)) {
return null
}
val writer = MultiFormatWriter()
try {
val result = writer.encode(
content, format, desiredWidth,
desiredHeight, hints
)
val width = result.width
val height = result.height
val pixels = IntArray(width * height)
// All are 0, or black, by default
for (y in 0 until height) {
val offset = y * width
for (x in 0 until width) {
pixels[offset + x] = if (result[x, y]) codeColor else Color.WHITE
}
}
val bitmap = Bitmap.createBitmap(
width, height,
Bitmap.Config.ARGB_8888
)
bitmap.setPixels(pixels, 0, width, 0, 0, width, height)
return if (isShowText) {
addCode(bitmap, content, textSize, codeColor, textSize / 2)
} else bitmap
} catch (e: WriterException) {
LogUtils.w(e.message)
}
return null
}
/**
* 条形码下面添加文本信息
* @param src
* @param code
* @param textSize
* @param textColor
* @return
*/
private fun addCode(
src: Bitmap?,
code: String?,
textSize: Int,
@ColorInt textColor: Int,
offset: Int
): Bitmap? {
if (src == null) {
return null
}
if (TextUtils.isEmpty(code)) {
return src
}
//获取图片的宽高
val srcWidth = src.width
val srcHeight = src.height
if (srcWidth <= 0 || srcHeight <= 0) {
return null
}
var bitmap = Bitmap.createBitmap(
srcWidth,
srcHeight + textSize + offset * 2,
Bitmap.Config.ARGB_8888
)
try {
val canvas = Canvas(bitmap!!)
canvas.drawBitmap(src, 0f, 0f, null)
val paint = TextPaint()
paint.textSize = textSize.toFloat()
paint.color = textColor
paint.textAlign = Paint.Align.CENTER
canvas.drawText(
code!!,
(srcWidth / 2).toFloat(),
(srcHeight + textSize / 2 + offset).toFloat(),
paint
)
canvas.save()
canvas.restore()
} catch (e: Exception) {
bitmap = null
LogUtils.w(e.message)
}
return bitmap
}
}

@ -1,45 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent">
<cn.bingoogolapple.qrcode.zxing.ZXingView
android:id="@+id/z_xing_view"
android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone" android:orientation="vertical">
app:qrcv_animTime="1000"
app:qrcv_barCodeTipText="将条码放入框内,即可自动扫描"
app:qrcv_barcodeRectHeight="120dp"
app:qrcv_borderColor="@android:color/white"
app:qrcv_borderSize="1dp"
app:qrcv_cornerColor="@color/primaryDark"
app:qrcv_cornerDisplayType="center"
app:qrcv_cornerLength="20dp"
app:qrcv_cornerSize="3dp"
app:qrcv_isAutoZoom="true"
app:qrcv_isBarcode="false"
app:qrcv_isOnlyDecodeScanBoxArea="false"
app:qrcv_isScanLineReverse="true"
app:qrcv_isShowDefaultGridScanLineDrawable="false"
app:qrcv_isShowDefaultScanLineDrawable="true"
app:qrcv_isShowLocationPoint="true"
app:qrcv_isShowTipBackground="true"
app:qrcv_isShowTipTextAsSingleLine="false"
app:qrcv_isTipTextBelowRect="false"
app:qrcv_maskColor="#33FFFFFF"
app:qrcv_qrCodeTipText="将二维码放入框内,即可自动扫描"
app:qrcv_rectWidth="300dp"
app:qrcv_scanLineColor="@color/primaryDark"
app:qrcv_scanLineMargin="0dp"
app:qrcv_scanLineSize="0.5dp"
app:qrcv_tipTextColor="@android:color/white"
app:qrcv_tipTextSize="12sp"
app:qrcv_toolbarHeight="?attr/actionBarSize"
app:qrcv_topOffset="100dp"
app:qrcv_verticalBias="-1" />
<io.legado.app.ui.widget.TitleBar <io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar" android:id="@+id/title_bar"
@ -47,35 +11,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:title="@string/scan_qr_code" /> app:title="@string/scan_qr_code" />
<LinearLayout <FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent" />
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:padding="16dp">
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_flashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/read_aloud"
android:src="@drawable/ic_daytime"
android:tint="@color/primaryText"
app:backgroundTint="@color/background_menu"
app:elevation="2dp"
app:fabSize="mini"
app:pressedTranslationZ="2dp" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout> </LinearLayout>
</RelativeLayout>
Loading…
Cancel
Save