效果图如下:
图片
代码如下
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.graphics.*
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.view.*
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.alibaba.fastjson.JSON
import com.google.gson.Gson
import com.lzy.okgo.model.Response
import kotlinx.android.synthetic.main.hybrid_activity_image_gallery.*
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import kotlin.math.abs
/**
* 支持画廊式查看大图的弹窗
*/
class ImageGalleryDialog1 : DialogFragment() {
companion object {
const val END_ITEM_VIEW_TYPE = 0
const val ITEM_VIEW_TYPE = 1
/**
* @param dataSourceJson H5传递过来的json
*/
fun newInstance(dataSourceJson: String): ImageGalleryDialog {
val args = Bundle()
args.putString("imageData", dataSourceJson)
val fragment = ImageGalleryDialog()
fragment.arguments = args
return fragment
}
}
/**
* 源数据
*/
private var dataSource: GalleryImageActivityBean? = null
private var adapter: ImageGalleryAdapter? = null
private var pageSize: Int = 0
private var pageIndex: Int = 0
private val selfTag = "ImageGalleryDialog"
/**
* 如果是重拍,这个值会不等于-1
*/
private var reviewPhotoPosition = -1
var resultJson = ""
var onImageDialogDismissListener: OnImageDialogDismissListener? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.hybrid_activity_image_gallery, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initData()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
super.onActivityCreated(savedInstanceState)
dialog?.window?.setBackgroundDrawable(ColorDrawable(0x00000000))
dialog?.window?.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT
)
}
private fun initData() {
val imageDataJson = arguments?.getString("imageData", "")
kotlin.runCatching {
dataSource = Gson().fromJson(imageDataJson, GalleryImageActivityBean::class.java)
pageSize = dataSource?.imageList?.size?: 0
pageIndex =
if (dataSource?.currentPosition?: 0 >= pageSize) 0 else dataSource?.currentPosition
?: 0
}.onSuccess {
initWidget()
}.onFailure {
ToastUtil.showToast("数据错误!")
dismissAllowingStateLoss()
}
}
/**
* 初始化控件
*/
@SuppressLint("SetTextI18n")
private fun initWidget() {
adapter = ImageGalleryAdapter(context, dataSource?.imageList, dataSource?.isReview)
imagePager.offscreenPageLimit = 2
imagePager.setPageTransformer(ImageGalleryZoomOutPageTransformer())
imagePager.adapter = adapter
tvIndex.text = "${pageIndex}/${adapter?.itemCount}"
imagePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
pageIndex = position
tvIndex.text = "${pageIndex + 1}/${adapter?.itemCount}"
if (adapter?.getItemViewType(position) != ITEM_VIEW_TYPE || dataSource?.isReview == false) {
ivReset.visibility = View.GONE
ivDeleted.visibility = View.GONE
}else {
ivReset.visibility = View.VISIBLE
ivDeleted.visibility = View.VISIBLE
}
if ((imagePager?.getChildAt(0) is RecyclerView)) {
if ((imagePager?.getChildAt(0) as RecyclerView).findViewHolderForAdapterPosition(adapter?.selectedPosition?:0) is ImageGalleryViewHolder) {
val oldHolder = (imagePager?.getChildAt(0) as RecyclerView).findViewHolderForAdapterPosition(adapter?.selectedPosition?:0) as ImageGalleryViewHolder
oldHolder.itemRootView?.isSelected = false
}
if ((imagePager?.getChildAt(0) as RecyclerView).findViewHolderForAdapterPosition(position) is ImageGalleryViewHolder) {
val newSelectedHolder = (imagePager?.getChildAt(0) as RecyclerView).findViewHolderForAdapterPosition(position) as ImageGalleryViewHolder
newSelectedHolder.itemRootView?.isSelected = true
}
}
adapter?.selectedPosition = position
}
})
adapter?.onImageGalleryAddPhotoClickListener = object : OnImageGalleryAddPhotoClickListener {
override fun onAddPhotoItemClick() {
if (dataSource?.imageList?.size ?: 0 < 9) {
takePhoto()
}
}
}
ivReset.click {
// 重拍
reviewPhotoPosition = imagePager.currentItem
takePhoto()
}
ivDeleted.click {
// 删除照片
if (adapter?.getItemViewType(imagePager.currentItem) == ITEM_VIEW_TYPE) {
val currentPosition = imagePager.currentItem
dataSource?.imageList?.removeAt(currentPosition)
adapter?.notifyItemRemoved(currentPosition)
imagePager?.post {
imagePager?.requestTransform()
}
}
}
imageGalleryRootView.click {
// 点击空白区域关闭
dismiss()
}
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
if (dataSource?.isReview == true) {
resultJson = Gson().toJson(dataSource)
}
onImageDialogDismissListener?.onDismiss(resultJson)
dismiss()
}
/**
* 去拍照
*/
private fun takePhoto() {
context?.let { context ->
val isHuar = TextUtils.equals("KNC", Build.MANUFACTURER) || TextUtils.equals("X1", Build.MODEL)
var camera_type = SharedPreferenceUtil.getIntValueFromSP("hbdPicture" , CAMERA_TYPE , -1)
camera_type = if(isHuar) { // 华瑞安机器
if(camera_type == -1) AXXCameraActivity.CAMERA_TYPE_FRONT else camera_type
} else {
if(camera_type == -1) AXXCameraActivity.CAMERA_TYPE_BACK else camera_type
}
val cameraWrapper = AXXAlbumCamera(context).image().cameraType(camera_type)
cameraWrapper
.onResult {
showLoadingDialog()
getToken(filePath = it)
}
.onCancel {
}
.start()
}
}
private fun showLoadingDialog() {
if (context is BaseActivity) {
(context as BaseActivity).showLoadingProgressDialog()
}
}
private fun hideLoadingDialog() {
if (context is BaseActivity) {
(context as BaseActivity).dismissLoadingProgressDialog()
}
}
/**
* 图片上传成功了
*/
private fun onUploadSuccess(itemBean: GalleryImageItemBean) {
if (reviewPhotoPosition != -1) {
// 重拍的
dataSource?.imageList?.set(reviewPhotoPosition, itemBean)
adapter?.notifyItemChanged(reviewPhotoPosition)
reviewPhotoPosition = -1
}else {
// 新增的
dataSource?.imageList?.add(itemBean)
if (dataSource?.imageList?.size?:0 >= 9) {
adapter?.notifyItemRangeChanged(0, 9)
imagePager?.post {
imagePager?.requestTransform()
}
}else {
adapter?.notifyItemInserted((dataSource?.imageList?.size?:0) - 1)
}
}
}
/**↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓下面这一坨是拍照上传的逻辑↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
// 摄像头类型(2 前 or 1 后)
private val CAMERA_TYPE = "camera_type"
/**
* 获取Token
*/
private fun getToken(filePath: String) {
GGGGRequest.startRequest(
SDKConfig.getBaseUrl() + "api/common/storageToken",
GGGGRequest.GET,
HashMap<String, String>(),
object : GGGGGStringCallback() {
override fun onResponseSuccess1(response: Response<*>?, code: Int, result: String) {
ReportLog.method(
selfTag,
"takeJzxPhoto",
"获取Token onResponseSuccess: $code result: $result"
)
val resultJson = JSON.parseObject(result)
val body = resultJson.getJSONObject("body")
val token = body.getString("token")
upload(token, filePath)
}
override fun onResponseError1(response: Response<*>?, code: Int, message: String?) {
ReportLog.method(
selfTag,
"takeJzxPhoto",
"获取Token onResponseError: $code result: $result"
)
hideLoadingDialog()
}
})
}
/**
* 上传图片
* if (isHuar) convertBitmap(BitmapUtil.getBitmapFormPath(filePath)) else
*/
private fun upload(token: String, filePath: String) {
val isHuar =
TextUtils.equals("KNC", Build.MANUFACTURER) || TextUtils.equals("X1", Build.MODEL)
val originalBitmap = getBitmapFormPath(filePath , isHuar)
if (originalBitmap?.bitmap == null) {
ReportLog.method(selfTag , "takeJzxPhoto" , "originalBitmap == null")
hideLoadingDialog()
return
}
// 写入存储,用于记录是前摄还是后摄
SharedPreferenceUtil.setIntDataIntoSP("", CAMERA_TYPE , originalBitmap?.source)
val convertBitmap = if (originalBitmap?.source == AXXCameraActivity.CAMERA_TYPE_FRONT) {
convertBitmap(originalBitmap?.bitmap!!)
} else {
originalBitmap.bitmap
}
val bitmapToBase64 = BitmapUtil.bitmapToBase64(convertBitmap)
Upload.upload(
0,
token,
true,
bitmapToBase64,
"${System.currentTimeMillis()}.jpg",
false,
object : Upload.UploadListener {
override fun uploadSuccess(vararg p0: String?) {
deletePath(filePath)
val key = p0[0]
val url = p0[1] ?: ""
ReportLog.method(selfTag, "takeJzxPhoto", "图片上传 uploadSuccess: $key : $url")
onUploadSuccess(GalleryImageItemBean(url, key))
hideLoadingDialog()
}
override fun uploadError() {
ReportLog.method(selfTag, "takeJzxPhoto", "图片上传失败")
hideLoadingDialog()
}
})
}
/**
* 镜像转换
*/
private fun convertBitmap(srcBitmap: Bitmap): Bitmap? {
val width = srcBitmap.width
val height = srcBitmap.height
val canvas = Canvas()
val matrix = Matrix()
matrix.postScale(-1f, 1f)
val newBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, width, height, matrix, true)
canvas.drawBitmap(
newBitmap,
Rect(0, 0, width, height),
Rect(0, 0, width, height), null
)
return newBitmap
}
/**
* 删除文件
*/
private fun deletePath(filePathName: String): Boolean {
if (TextUtils.isEmpty(filePathName)) return false
val file = File(filePathName)
if (file.exists() && file.isFile) {
if (file.delete()) {
return true
}
}
return false
}
/**
* 通过图片判断前摄还是后摄
*/
data class BitmapEntity(
var bitmap: Bitmap? = null,
var source: Int = 0
)
/**
* 从文件路径中读取Bitmap
*/
@Throws(IOException::class)
fun getBitmapFormPath(path: String? , isHuar: Boolean) {
val bitmapEntity = BitmapEntity()
var input: InputStream = FileInputStream(File(path))
val onlyBoundsOptions = BitmapFactory.Options()
onlyBoundsOptions.inJustDecodeBounds = true
onlyBoundsOptions.inDither = true
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888
BitmapFactory.decodeStream(input, null as Rect?, onlyBoundsOptions)
input.close()
val originalWidth = onlyBoundsOptions.outWidth
val originalHeight = onlyBoundsOptions.outHeight
if(isHuar) {
// 通过分辨率判断是不是后摄
if(originalWidth == 4096
|| originalHeight == 4096) {
bitmapEntity.source = AXXCameraActivity.CAMERA_TYPE_BACK
} else {
bitmapEntity.source = AXXCameraActivity.CAMERA_TYPE_FRONT
}
} else {
bitmapEntity.source = -1 // 无记录
}
return if (originalWidth != -1 && originalHeight != -1) {
var hh = 720.0f
var ww = 1080.0f
if(originalWidth < originalHeight) {
hh = 1080.0f
ww = 720.0f
}
var be = 1
if (originalWidth > originalHeight && originalWidth.toFloat() > ww) {
be = (originalWidth.toFloat() / ww).toInt()
} else if (originalWidth < originalHeight && originalHeight.toFloat() > hh) {
be = (originalHeight.toFloat() / hh).toInt()
}
if (be <= 0) {
be = 1
}
val bitmapOptions = BitmapFactory.Options()
bitmapOptions.inSampleSize = be
bitmapOptions.inDither = true
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888
input = FileInputStream(File(path))
val bitmap = BitmapFactory.decodeStream(input, null as Rect?, bitmapOptions)
input.close()
bitmapEntity.bitmap = bitmap
bitmapEntity
} else {
bitmapEntity
}
}
/**↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑上面这一坨是拍照上传的逻辑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/
}
/**
* 图片列表的适配器
*/
class ImageGalleryAdapter1(
var context: Context?,
var list: ArrayList<GalleryImageItemBean>?,
var isReview: Boolean? = true,
var onImageGalleryAddPhotoClickListener: OnImageGalleryAddPhotoClickListener? = null
) : RecyclerView.Adapter<ImageGalleryViewHolder>() {
var selectedPosition = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageGalleryViewHolder {
if (!isEndItemView(viewType)) {
return ImageGalleryViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.hybrid_item_image_gallery, parent, false)
)
}
return ImageGalleryViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.hybrid_item_image_gallery_photograph, parent, false)
)
}
override fun onBindViewHolder(holder: ImageGalleryViewHolder, position: Int) {
if (!isEndItemView(getItemViewType(position))) {
list?.get(position)?.imgUrl?.apply {
holder.ivPhoto?.loadImageByUrl(this)
}
} else {
holder.itemRootView?.click {
onImageGalleryAddPhotoClickListener?.onAddPhotoItemClick()
}
}
}
override fun getItemCount(): Int {
if (list?.size ?: 0 >= 9) {
return 9
}
return if(isReview == true) (list?.size ?: 0) + 1 else (list?.size ?: 0)
}
override fun getItemViewType(position: Int): Int {
if (position < list?.size ?: 0 - 1) {
return ITEM_VIEW_TYPE
}
return END_ITEM_VIEW_TYPE
}
private fun isEndItemView(viewType: Int): Boolean {
return viewType == END_ITEM_VIEW_TYPE
}
}
/**
* 点击继续拍照的点击事件回调
*/
interface OnImageGalleryAddPhotoClickListener1 {
fun onAddPhotoItemClick()
}
class ImageGalleryViewHolder1(itemView: View) : RecyclerView.ViewHolder(itemView) {
var itemRootView: ConstraintLayout? = null
var ivPhoto: ImageView? = null
var ivAddPhoto: ImageView? = null
init {
ivPhoto = itemView.findViewById(R.id.ivPhoto)
ivAddPhoto = itemView.findViewById(R.id.ivAddPhoto)
itemRootView = itemView.findViewById(R.id.itemRootView)
ivPhoto?.setRound(itemView.context.resources.getDimensionPixelSize(R.dimen.hybrid_image_gallery_dialog_round_size)
.toFloat())
}
}
class ImageGalleryZoomOutPageTransformer1 : ViewPager2.PageTransformer {
override fun transformPage(view: View, position: Float) {
val pageWidth = view.width
val pageHeight = view.height
val scaleFactor = MIN_SCALE.coerceAtLeast(1 - abs(position))
val vertMargin = pageHeight * (1 - scaleFactor) / 2
val horzMargin = pageWidth * (1 - scaleFactor) / 2
when {
position < -1 -> {
view.translationX = horzMargin - vertMargin / 2
// [-Infinity,-1)
// This page is way off-screen to the left.
view.alpha = MIN_ALPHA
view.scaleX = MIN_SCALE
view.scaleY = MIN_SCALE
}
position <= 1 -> {
// [-1,1]
if (position < 0) {
view.translationX = horzMargin - vertMargin / 2
view.scaleX = 1 + 0.3f * position
view.scaleY = 1 + 0.3f * position
} else {
view.translationX = -horzMargin + vertMargin / 2
view.scaleX = 1 - 0.3f * position
view.scaleY = 1 - 0.3f * position
}
// Scale the page down (between MIN_SCALE and 1)
// Fade the page relative to its size.
view.alpha =
MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA)
}
else -> { // (1,+Infinity]
view.translationX = -horzMargin + vertMargin / 2
view.scaleX = MIN_SCALE
view.scaleY = MIN_SCALE
view.alpha = MIN_ALPHA
}
}
}
companion object {
private const val MIN_SCALE = 0.7f
private const val MIN_ALPHA = 0.5f
}
}
interface OnImageDialogDismissListener1 {
fun onDismiss(resultJson:String)
}
class AXXAlbumCamera(var context: Context): Camera<AXXImageCameraWrapper, VideoCameraWrapper> {
override fun image(): AXXImageCameraWrapper {
return AXXImageCameraWrapper(context)
}
override fun video(): VideoCameraWrapper {
return VideoCameraWrapper(context)
}
}
public class AXXCameraActivity extends BaseActivity {
public static final String INSTANCE_CAMERA_TYPE = "INSTANCE_CAMERA_TYPE";
private int cameraType = 1; // 前置、后置 1 后置 2 前置
private static final int CODE_PERMISSION_IMAGE = 1;
public static final int CAMERA_TYPE_BACK = 1; // 后
public static final int CAMERA_TYPE_FRONT = 2; // 前
private static final int CODE_ACTIVITY_TAKE_IMAGE = 1;
public static Action<String> sResult;
public static Action<String> sCancel;
private String mCameraFilePath;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SystemBar.setStatusBarColor(this, Color.TRANSPARENT);
SystemBar.setNavigationBarColor(this, Color.TRANSPARENT);
SystemBar.invasionNavigationBar(this);
if (savedInstanceState == null) {
Bundle bundle = getIntent().getExtras();
assert bundle != null;
cameraType = bundle.getInt(INSTANCE_CAMERA_TYPE);
requestPermission(PERMISSION_TAKE_PICTURE , CODE_PERMISSION_IMAGE);
} else {
cameraType = savedInstanceState.getInt(INSTANCE_CAMERA_TYPE);
}
}
@Override
protected void onPermissionGranted(int code) {
super.onPermissionGranted(code);
if(code == CODE_PERMISSION_IMAGE) {
mCameraFilePath = AlbumUtils.randomJPGPath(this);
ReportLog.INSTANCE.method("AXXCameraActivity"
, "onPermissionGranted"
, "要拍照的路径:" + mCameraFilePath);
takeImage(this, CODE_ACTIVITY_TAKE_IMAGE, new File(mCameraFilePath));
}
}
@Override
protected void onPermissionDenied(int code) {
super.onPermissionDenied(code);
if(code == CODE_PERMISSION_IMAGE) {
ToastUtil.showToast("您还没有拍照和存储的权限,请前往权限中心进行设置");
callbackCancel();
}
}
/**
* 拍照
* @param activity
* @param requestCode
* @param outPath
*/
private void takeImage(@NonNull Activity activity, int requestCode, File outPath) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = AlbumUtils.getUri(activity, outPath);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
if(cameraType == CAMERA_TYPE_FRONT) {
intent.putExtra("android.intent.extra.USE_FRONT_CAMERA", true);
} else {
intent.putExtra("android.intent.extra.USE_FRONT_CAMERA", false);
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
activity.startActivityForResult(intent, requestCode);
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(INSTANCE_CAMERA_TYPE, cameraType);
super.onSaveInstanceState(outState);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CODE_ACTIVITY_TAKE_IMAGE: {
if (resultCode == RESULT_OK) {
callbackResult();
} else {
callbackCancel();
}
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
private void callbackResult() {
if (sResult != null) sResult.onAction(mCameraFilePath);
sResult = null;
sCancel = null;
finish();
}
private void callbackCancel() {
if (sCancel != null) sCancel.onAction("User canceled.");
sResult = null;
sCancel = null;
finish();
}
}
hybrid_activity_image_gallery.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/imageGalleryRootView"
android:background="#00000000"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvIndex"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/pageGroup"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="0/0"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintVertical_bias="0.8"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pageGroup"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipChildren="false"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHeight_percent="0.6">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/imagePager"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipChildren="false"
android:overScrollMode="never"
app:layout_constraintHeight_percent="1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintDimensionRatio="h,548:750"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivReset"
app:layout_constraintTop_toBottomOf="@+id/pageGroup"
app:layout_constraintLeft_toLeftOf="@id/pageGroup"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@+id/ivDeleted"
app:layout_constraintHorizontal_chainStyle="packed"
android:src="@mipmap/icon_image_gallery_reset"
app:layout_constraintDimensionRatio="64:100"
app:layout_constraintHeight_percent="0.09"
android:layout_marginRight="17dp"
android:layout_width="0dp"
android:layout_height="0dp"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivDeleted"
app:layout_constraintTop_toBottomOf="@+id/pageGroup"
app:layout_constraintRight_toRightOf="@id/pageGroup"
app:layout_constraintLeft_toRightOf="@+id/ivReset"
app:layout_constraintBottom_toBottomOf="parent"
android:src="@mipmap/icon_image_gallery_deleted"
android:layout_marginLeft="17dp"
app:layout_constraintDimensionRatio="64:100"
app:layout_constraintHeight_percent="0.09"
android:layout_width="0dp"
android:layout_height="0dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
hybrid_item_image_gallery.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/itemRootView"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/hybrid_bg_photo_grallery">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivPhoto"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
hybrid_item_image_gallery_photograph.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/itemRootView"
android:background="@drawable/hybrid_bg_add_photo_grallery"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivAddPhoto"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintDimensionRatio="100:83"
app:layout_constraintWidth_percent="0.13"
android:src="@mipmap/icon_photograph"/>
</androidx.constraintlayout.widget.ConstraintLayout>