From d7a7f35bd775b908cf42f30b6d6c44119edc1b76 Mon Sep 17 00:00:00 2001 From: gedoor Date: Fri, 29 Jan 2021 15:30:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=8C=E7=BB=B4=E7=A0=81?= =?UTF-8?q?=E6=89=AB=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- .../io/legado/app/ui/qrcode/QrCodeActivity.kt | 74 +-- .../io/legado/app/ui/qrcode/QrCodeFragment.kt | 13 + .../java/io/legado/app/utils/BitmapUtils.kt | 29 +- .../io/legado/app/utils/ContextExtensions.kt | 7 +- .../java/io/legado/app/utils/QRCodeUtils.kt | 467 ++++++++++++++++++ .../res/layout/activity_qrcode_capture.xml | 76 +-- 7 files changed, 528 insertions(+), 140 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt create mode 100644 app/src/main/java/io/legado/app/utils/QRCodeUtils.kt diff --git a/app/build.gradle b/app/build.gradle index 51a57aef2..bdf74a1f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -185,7 +185,7 @@ dependencies { 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' diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt index 2d0ed31e6..c59783d53 100644 --- a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt @@ -6,36 +6,29 @@ import android.graphics.BitmapFactory import android.os.Bundle import android.view.Menu import android.view.MenuItem -import android.view.View -import cn.bingoogolapple.qrcode.core.QRCodeView +import com.google.zxing.Result +import com.king.zxing.CameraScan.OnScanResultCallback import io.legado.app.R import io.legado.app.base.BaseActivity import io.legado.app.databinding.ActivityQrcodeCaptureBinding -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat +import io.legado.app.utils.QRCodeUtils import io.legado.app.utils.readBytes -import org.jetbrains.anko.toast -class QrCodeActivity : BaseActivity(), QRCodeView.Delegate { +class QrCodeActivity : BaseActivity(), OnScanResultCallback { private val requestQrImage = 202 - private var flashlightIsOpen: Boolean = false override fun getViewBinding(): ActivityQrcodeCaptureBinding { return ActivityQrcodeCaptureBinding.inflate(layoutInflater) } override fun onActivityCreated(savedInstanceState: Bundle?) { - binding.zXingView.setDelegate(this) - binding.fabFlashlight.setOnClickListener { - if (flashlightIsOpen) { - flashlightIsOpen = false - binding.zXingView.closeFlashlight() - } else { - flashlightIsOpen = true - binding.zXingView.openFlashlight() - } - } + val fTag = "qrCodeFragment" + var qrCodeFragment = supportFragmentManager.findFragmentByTag(fTag) + if (qrCodeFragment == null) qrCodeFragment = QrCodeFragment() + supportFragmentManager.beginTransaction() + .replace(R.id.fl_content, qrCodeFragment, fTag) + .commit() } override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { @@ -55,60 +48,21 @@ class QrCodeActivity : BaseActivity(), QRCodeView. return super.onCompatOptionsItemSelected(item) } - override fun onStart() { - 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) { + override fun onScanResultCallback(result: Result?): Boolean { val intent = Intent() - intent.putExtra("result", result) + intent.putExtra("result", result?.text) setResult(RESULT_OK, intent) finish() - } - - override fun onCameraAmbientBrightnessChanged(isDark: Boolean) { - - } - - override fun onScanQRCodeOpenCameraError() { - toast("打开相机失败") + return true } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) data?.data?.let { - //TODO 显示扫描框,并开始识别 - binding.zXingView.startSpotAndShowRect() - if (resultCode == Activity.RESULT_OK && requestCode == requestQrImage) { - // 本来就用到 QRCodeView 时可直接调 QRCodeView 的方法,走通用的回调 it.readBytes(this)?.let { bytes -> val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) - binding.zXingView.decodeQRCode(bitmap) + onScanResultCallback(QRCodeUtils.parseCodeResult(bitmap)) } } } diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt new file mode 100644 index 000000000..d279ca9eb --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeFragment.kt @@ -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 + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/utils/BitmapUtils.kt b/app/src/main/java/io/legado/app/utils/BitmapUtils.kt index 6be7ea2c3..62dccbc69 100644 --- a/app/src/main/java/io/legado/app/utils/BitmapUtils.kt +++ b/app/src/main/java/io/legado/app/utils/BitmapUtils.kt @@ -1,11 +1,8 @@ package io.legado.app.utils import android.content.Context -import android.graphics.Bitmap +import android.graphics.* import android.graphics.Bitmap.Config -import android.graphics.BitmapFactory -import android.graphics.Canvas -import android.graphics.Color import android.renderscript.Allocation import android.renderscript.Element 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) + + } + /** * 高斯模糊 */ diff --git a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt index d0f693069..d9d0fbf94 100644 --- a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt @@ -18,9 +18,6 @@ import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.content.FileProvider 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.R import org.jetbrains.anko.defaultSharedPreferences @@ -104,9 +101,7 @@ val Context.navigationBarHeight: Int @SuppressLint("SetWorldReadable") fun Context.shareWithQr(title: String, text: String) { - QRCodeEncoder.HINTS[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.L - val bitmap = QRCodeEncoder.syncEncodeQRCode(text, 600) - QRCodeEncoder.HINTS[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H + val bitmap = QRCodeUtils.createQRCode(text) if (bitmap == null) { toast(R.string.text_too_long_qr_error) } else { diff --git a/app/src/main/java/io/legado/app/utils/QRCodeUtils.kt b/app/src/main/java/io/legado/app/utils/QRCodeUtils.kt new file mode 100644 index 000000000..366abfbe3 --- /dev/null +++ b/app/src/main/java/io/legado/app/utils/QRCodeUtils.kt @@ -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 = 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, + 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 = 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 = 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?): 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 = 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 = 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? = 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 + } + + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_qrcode_capture.xml b/app/src/main/res/layout/activity_qrcode_capture.xml index cfc732dd6..379109187 100644 --- a/app/src/main/res/layout/activity_qrcode_capture.xml +++ b/app/src/main/res/layout/activity_qrcode_capture.xml @@ -1,45 +1,9 @@ - - - - + android:layout_height="match_parent" + android:orientation="vertical"> - - - - - - - + android:layout_height="match_parent" /> - - \ No newline at end of file + \ No newline at end of file