Commit 6b5e0653 authored by 周文华's avatar 周文华

Merge remote-tracking branch 'origin/master'

parents 50c34e87 5a4796e0
package com.base.scanqrclear.bean
data class SimilarBean(
val url: String,
val hashCode: String,
val avgPixel: Int,
val size: Long,
var isSelect: Boolean,
var items: MutableList<SimilarBean> = mutableListOf()
)
\ No newline at end of file
......@@ -29,7 +29,7 @@ object ActivityJumpHelps {
// R.string.app_manager -> AppManagerActivity.start(context)
// R.string.large_files -> requestPermission(context, launcher) { LargeFileCleanActivity.start(context) }
// R.string.image_compressor -> requestPermission(context, launcher) { PhotoCompressionActivity.start(context) }
// R.string.similar_photos -> requestPermission(context, launcher) { SimilarPhotosActivity.start(context) }
// R.string.similar_photos -> requestPermission(context, launcher) { com.base.scanqrclear.luma.SimilarPhotosActivity.start(context) }
}
}
......
......@@ -228,7 +228,7 @@ open class BaseActivity2 : AppCompatActivity() {
// is CleanJunkActivity -> exit(ExitType.CLEAN)
// is LargeFileCleanActivity -> exit(ExitType.LARGE_FILE)
// is com.base.scanqrclear.luma.ScreenshotCleanActivity -> exit(ExitType.SCREENSHOT)
// is SimilarPhotosActivity -> exit(ExitType.SIMILAR_PHOTOS)
// is com.base.scanqrclear.luma.SimilarPhotosActivity -> exit(ExitType.SIMILAR_PHOTOS)
// is com.base.scanqrclear.luma.WhatsappCleanActivity -> exit(ExitType.WHATSAPP)
// is PhotoCompressionActivity -> exit(ExitType.PHOTO_COMPRESSION)
// else -> finishOrToMain()
......
package com.base.scanqrclear.luma
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.ThumbnailUtils
import com.base.scanqrclear.bean.SimilarBean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.math.pow
object ImageHelpers {
private fun loadBitmapFromFile(path: String): Bitmap? {
val bitmap = BitmapFactory.decodeFile(path)
if (bitmap.width <= 0 || bitmap.height <= 0) {
return null
}
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, options)
if (options.outHeight == -1 || options.outWidth == -1) {
return null
}
var inSampleSize =
(0.5f + options.outHeight.toFloat() / bitmap.height.toFloat()).coerceAtLeast(0.5f + options.outWidth.toFloat() / bitmap.width.toFloat())
.toInt()
inSampleSize += 1
options.inSampleSize = inSampleSize.coerceAtLeast(1)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, options)
}
suspend fun createImage(path: String): SimilarBean? {
return withContext(Dispatchers.IO) {
try {
val width = 8
val height = 8
val source = loadBitmapFromFile(path)
val thumb = ThumbnailUtils.extractThumbnail(source, width, height)
val pixels = IntArray(width * height)
for (i in 0 until width) {
for (j in 0 until height) {
pixels[i * height + j] = rgbToGray(thumb.getPixel(i, j))
}
}
val avgPixel = average(pixels)
val comps = IntArray(width * height)
for (i in comps.indices) {
if (pixels[i] >= avgPixel) {
comps[i] = 1
} else {
comps[i] = 0
}
}
val hashCode = StringBuffer()
var i = 0
while (i < comps.size) {
val result = comps[i] * 2.0.pow(3.0).toInt() + (comps[i + 1] * 2.0.pow(2.0)
.toInt()) + (comps[i + 2] * 2.0.pow(1.0).toInt()) + comps[i + 3]
hashCode.append(binaryToHex(result))
i += 4
}
recycleBitmap(thumb)
recycleBitmap(source)
return@withContext SimilarBean(
path,
hashCode.toString(),
avgPixel,
File(path).length(),
false
)
} catch (_: Exception) {
return@withContext null
}
}
}
private fun rgbToGray(pixels: Int): Int {
val red = Color.red(pixels)
val green = Color.green(pixels)
val blue = Color.blue(pixels)
return (0.3 * red + 0.59 * green + 0.11 * blue).toInt()
}
private fun average(pixels: IntArray): Int {
return (pixels.sumOf { it }.toFloat() / pixels.size).toInt()
}
private fun recycleBitmap(thumb: Bitmap?) {
if (thumb?.isRecycled == false) {
thumb.recycle()
}
}
fun similarCondition(first: SimilarBean, second: SimilarBean): Boolean {
return hammingDistance(
first.hashCode,
second.hashCode
) <= 6 && (first.avgPixel.toFloat() / second.avgPixel) in 0.8..1.0
}
private fun hammingDistance(sourceHashCode: String, hashCode: String): Int {
var difference = 0
for (i in sourceHashCode.indices) {
if (sourceHashCode[i] != hashCode[i]) {
difference++
}
}
return difference
}
private fun binaryToHex(binary: Int) = when (binary) {
0 -> '0'
1 -> '1'
2 -> '2'
3 -> '3'
4 -> '4'
5 -> '5'
6 -> '6'
7 -> '7'
8 -> '8'
9 -> '9'
10 -> 'a'
11 -> 'b'
12 -> 'c'
13 -> 'd'
14 -> 'e'
15 -> 'f'
else -> ' '
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.base.scanqrclear.R
import com.base.scanqrclear.bean.SimilarBean
import com.base.scanqrclear.databinding.ActivitySimilarPhotosBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.Semaphore
class SimilarPhotosActivity : BaseActivity2() {
companion object {
fun start(context: Context) {
val intent = Intent(context, SimilarPhotosActivity::class.java)
context.startActivity(intent)
}
}
private lateinit var adapter: SimilarPhotosAdapter
private val semaphore = Semaphore(20)
private var isScanning = true
private val binding by lazy {
ActivitySimilarPhotosBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
initData()
showAnimationAd(LottieType.SIMILAR_PHOTOS) {
if (isScanning)
loadingDialog = DialogHelps.showLoadingDialog(this)
}
AdmobHelps.showNativeAd(this, completed = {
binding.adNative.setNativeAd(it)
}, maxCompleted = { nativeAdLoader, nativeMaxAd ->
binding.adNative.setNativeAd(nativeAdLoader, nativeMaxAd)
})
}
private fun initView() {
adapter = SimilarPhotosAdapter()
adapter.callback = object : BaseAdapter.OnClickCallback<SimilarBean> {
override fun onClicked(view: View, position: Int, item: SimilarBean) {
val list = getAllList(adapter.list)
val total = list.filter { it.isSelect }.sumOf { it.size }
showSelection(total > 0, total)
}
}
binding.rvPhoto.adapter = adapter
binding.rvPhoto.layoutManager = LinearLayoutManager(this)
binding.flBack.setOnClickListener { backPressed() }
binding.ivSelect.setOnClickListener {
it.isSelected = !it.isSelected
setSelectAll(it.isSelected, adapter.list)
}
binding.tvDelete.setOnClickListener {
val list = getAllList(adapter.list).filter { it.isSelect }
if (list.isEmpty()) return@setOnClickListener
val total = list.sumOf { it.size }
DialogHelps.showDeleteDialog(this, binding.tvDelete) {
showAnimationAd {
toCleanResult(total, getString(R.string.similar_photos))
}
deleteFiles(list)
}
}
}
private fun initData() {
lifecycleScope.launch(Dispatchers.IO) {
val images = MediaHelps.findImageFiles(this@SimilarPhotosActivity)
val similarBeans = images.mapNotNull { value ->
lifecycleScope.async(Dispatchers.IO) {
semaphore.acquire() // 请求许可
try {
ImageHelpers.createImage(value.path)
} finally {
semaphore.release() // 释放许可
}
}.await()
}
// val similarBeans = deferredSimilarBeans.mapNotNull { it.await() }
val hasSame = mutableSetOf<String>()
similarBeans.forEach { similar ->
if (hasSame.contains(similar.url)) return@forEach
hasSame.add(similar.url)
similar.items.add(similar)
similarBeans.forEach {
if (!hasSame.contains(it.url) && ImageHelpers.similarCondition(similar, it)) {
hasSame.add(it.url)
similar.items.add(it)
}
}
}
val list = similarBeans.filter { it.items.size > 1 }
withContext(Dispatchers.Main) {
isScanning = false
loadingDialog?.dismiss()
setSelectAll(true, list)
}
}
}
private fun setSelectAll(isSelected: Boolean, list: List<SimilarBean>) {
binding.ivSelect.isSelected = isSelected
list.forEach { similar ->
similar.items.forEachIndexed { index, similarBean ->
similarBean.isSelect = if (index > 0) isSelected else false
}
}
adapter.submitList(list)
binding.ivSelect.setImageResource(if (isSelected) R.mipmap.icon_s else R.mipmap.icon_n)
val total = getAllList(adapter.list).filter { it.isSelect }.sumOf { it.size }
showSelection(isSelected && list.isNotEmpty(), total)
}
private fun deleteFiles(list: List<SimilarBean>) {
lifecycleScope.launch(Dispatchers.IO) {
val paths = list.map { it.url }.toTypedArray()
paths.forEach { FileHelps.deleteFile(it) }
withContext(Dispatchers.Main) {
MediaHelps.updateMedia(applicationContext, paths)
}
}
}
private fun showSelection(isSelection: Boolean, total: Long) {
binding.tvDelete.setBackgroundResource(if (isSelection) R.drawable.gradient else R.drawable.gradient_not_clickable)
val delete = getString(R.string.delete)
binding.tvDelete.text = if (total > 0) "${delete} (${Utils.getFormatSize(total)})" else delete
}
private fun getAllList(beans: List<SimilarBean>): MutableList<SimilarBean> {
val list = mutableListOf<SimilarBean>()
beans.forEach { it.items.forEach { list.add(it) } }
return list
}
}
\ No newline at end of file
package com.base.scanqrclear.luma
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import com.base.scanqrclear.R
import com.base.scanqrclear.bean.SimilarBean
import com.base.scanqrclear.databinding.ItemSimilarPhotosBinding
import com.base.scanqrclear.luma.AppHelps.dpToPx
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
class SimilarPhotosAdapter(
val data: List<SimilarBean> = emptyList()
) : BaseAdapter<SimilarBean, ItemSimilarPhotosBinding>(data) {
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemSimilarPhotosBinding {
return ItemSimilarPhotosBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemSimilarPhotosBinding>,
position: Int,
item: SimilarBean
) {
val total = item.items.filter { it.isSelect }.sumOf { it.size }
holder.binding.tvSize.text = Utils.getFormatSize(total)
val width = holder.itemView.context.resources.displayMetrics.widthPixels
val margin = holder.itemView.context.dpToPx(45).toInt()
val imageWidth = (width - margin) / 4
val largePhotoWidth = imageWidth * 2
val largePhotoHeight = (largePhotoWidth * 0.92).toInt()
holder.binding.ivFirstPhoto.layoutParams.width = largePhotoWidth
holder.binding.ivFirstPhoto.layoutParams.height = largePhotoHeight
val round = holder.itemView.context.dpToPx(8).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.items[0].url)
.override(largePhotoWidth, largePhotoHeight)
.apply(options)
.into(holder.binding.ivFirstPhoto)
holder.binding.ivSelect.setImageResource(if (item.items[0].isSelect) R.mipmap.icon_xuanzhong_photo_on else R.mipmap.icon_weixuanzhong_photo_off)
holder.binding.ivFirstPhoto.setOnClickListener {
item.items[0].isSelect = !item.items[0].isSelect
holder.binding.ivSelect.setImageResource(if (item.items[0].isSelect) R.mipmap.icon_xuanzhong_photo_on else R.mipmap.icon_weixuanzhong_photo_off)
setItemClick(item, item.items[0], 0, holder)
}
val largePhotos = item.items.take(5)
val largeAdapter = SimilarPhotosChildAdapter(largePhotos.subList(1, largePhotos.size))
setItemClick(item, largeAdapter, holder)
holder.binding.rvLargePhoto.layoutManager = GridLayoutManager(holder.itemView.context, 2, GridLayoutManager.VERTICAL, false)
holder.binding.rvLargePhoto.adapter = largeAdapter
val photos = if (item.items.size > 4) item.items.subList(5, item.items.size) else mutableListOf()
val adapter = SimilarPhotosChildAdapter(photos)
setItemClick(item, adapter, holder)
holder.binding.rvPhoto.layoutManager = GridLayoutManager(holder.itemView.context, 4, GridLayoutManager.VERTICAL, false)
holder.binding.rvPhoto.adapter = adapter
}
private fun setItemClick(
item: SimilarBean,
adapter: SimilarPhotosChildAdapter,
holder: BaseViewHolder<ItemSimilarPhotosBinding>
) {
adapter.callback = object : OnClickCallback<SimilarBean> {
override fun onClicked(view: View, position: Int, data: SimilarBean) {
setItemClick(item, data, position, holder)
}
}
}
private fun setItemClick(
item: SimilarBean,
data: SimilarBean,
position: Int,
holder: BaseViewHolder<ItemSimilarPhotosBinding>
) {
val total = item.items.filter { it.isSelect }.sumOf { it.size }
holder.binding.tvSize.text = Utils.getFormatSize(total)
callback?.onClicked(holder.itemView, position, data)
}
}
package com.base.scanqrclear.luma
import android.view.LayoutInflater
import android.view.ViewGroup
import com.base.scanqrclear.R
import com.base.scanqrclear.bean.SimilarBean
import com.base.scanqrclear.databinding.ItemSimilarPhotosChildBinding
import com.base.scanqrclear.luma.AppHelps.dpToPx
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
class SimilarPhotosChildAdapter(
val data: List<SimilarBean>
) : BaseAdapter<SimilarBean, ItemSimilarPhotosChildBinding>(data) {
override fun getViewBinding(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ItemSimilarPhotosChildBinding {
return ItemSimilarPhotosChildBinding.inflate(layoutInflater, parent, false)
}
override fun bind(
holder: BaseViewHolder<ItemSimilarPhotosChildBinding>,
position: Int,
item: SimilarBean
) {
val width = holder.itemView.context.resources.displayMetrics.widthPixels
val margin = holder.itemView.context.dpToPx(45).toInt()
val imageWidth = (width - margin) / 4
holder.binding.ivPhoto.layoutParams.width = imageWidth
holder.binding.ivPhoto.layoutParams.height = (imageWidth * 0.91).toInt()
holder.binding.ivSelect.setImageResource(if (item.isSelect) R.mipmap.icon_xuanzhong_photo_on else R.mipmap.icon_yuan_n)
holder.itemView.setOnClickListener {
item.isSelect = !item.isSelect
holder.binding.ivSelect.setImageResource(if (item.isSelect) R.mipmap.icon_xuanzhong_photo_on else R.mipmap.icon_yuan_n)
callback?.onClicked(it, position, item)
}
val round = holder.itemView.context.dpToPx(8).toInt()
val options = RequestOptions().transform(CenterCrop(), RoundedCorners(round))
Glide.with(holder.itemView.context)
.load(item.url)
.apply(options)
.into(holder.binding.ivPhoto)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_f7fafa">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/gradient_background"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="6dp"
android:paddingBottom="6dp"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/fl_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="11dp"
android:padding="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/icon_return_bar_nor" />
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/similar_photos"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/fl_back"
app:layout_constraintStart_toEndOf="@id/fl_back"
app:layout_constraintTop_toTopOf="@id/fl_back" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:layout_constraintTop_toBottomOf="@id/cl_top">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="13dp"
android:layout_marginStart="15dp"
android:text="@string/auto_select"
android:textColor="@color/black"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:src="@mipmap/icon_n"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.base.scanqrclear.luma.NativeView
android:id="@+id/ad_native"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="15dp"
android:layout_marginTop="10dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/cl_select" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_photo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="20dp"
android:scrollbars="none"
app:layout_constraintBottom_toTopOf="@id/tv_delete"
app:layout_constraintTop_toBottomOf="@id/ad_native" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:layout_marginBottom="55dp"
android:background="@drawable/gradient_not_clickable"
android:gravity="center"
android:paddingVertical="12dp"
android:text="@string/delete"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent" />
<include
android:id="@+id/animation"
layout="@layout/custom_animation"
android:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_occupies"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:text="@string/occupies"
android:textColor="@color/color_9b9595"
android:textSize="14sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:text="0B"
android:textColor="@color/color_9b9595"
android:textSize="14sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_occupies" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/tv_occupies">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_first_photo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="4dp"
android:layout_marginStart="15dp"
android:src="@color/black"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="8dp"
android:src="@mipmap/icon_weixuanzhong_photo_off"
app:layout_constraintTop_toTopOf="@id/iv_first_photo"
app:layout_constraintEnd_toEndOf="@id/iv_first_photo" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_large_photo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="15dp"
android:scrollbars="none"
android:nestedScrollingEnabled="false"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_first_photo" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_photo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingHorizontal="15dp"
android:paddingBottom="10dp"
android:scrollbars="none"
android:nestedScrollingEnabled="false"
app:layout_constraintTop_toBottomOf="@id/cl_large" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_photo"
android:layout_width="80dp"
android:layout_height="73dp"
android:layout_marginTop="4dp"
android:src="@color/color_9b9595"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="8dp"
android:src="@mipmap/icon_weixuanzhong_photo_off"
app:layout_constraintTop_toTopOf="@id/iv_photo"
app:layout_constraintEnd_toEndOf="@id/iv_photo" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment