Android 图片合成与图片切割-生成九宫格图

1.列表图片放置在根目录下的Pictures里面。

package com.exa.companydemo.utils

import android.graphics.*
import android.os.Environment
import android.os.Environment.DIRECTORY_PICTURES
import android.os.SystemClock
import com.exa.baselib.BaseConstants
import com.exa.baselib.utils.DateUtil
import com.exa.baselib.utils.FileUtils
import com.exa.baselib.utils.L
import java.io.File
import kotlin.math.abs
import kotlin.math.sqrt

/**
 * @Author lsh
 * @Date 2024/2/23 11:09
 * @Description
 */
object ImageUtil {
    private val list = arrayListOf<String>()
    private val pictureDirFile = Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES)

    /** 等同于ImageView.ScaleType.FIT_XY */
    const val SCALE_XY = 1

    /** 等比例缩放 */
    const val SCALE_RES = 2

    /** 从中间裁剪 */
    const val SCALE_CENTER = 3

    interface Callback {
        fun onSplitResult(pathList: ArrayList<String>) {}
        fun onComposeResult(path: String) {}
        fun onError(msg: String) {}
    }

    init {
        pictureDirFile.listFiles()?.forEach {
            if (!it.isDirectory) {
                list.add(it.absolutePath)
            }
        }
        list.sort()
    }

    fun composeImages() {
        composeImages(list, true, SCALE_CENTER)
    }

    fun splitImage() {
        var path = ""
        list.forEach {
            if (it.contains("6.jpg")) {
                path = it
                return@forEach
            }
        }
        splitImage(path)
    }

    /**
     * 将多张图片合并成1张图片
     */
    fun composeImages(
        list: List<String>,
        whiteDivider: Boolean = true,
        scaleType: Int = SCALE_XY,
        callback: Callback? = null
    ) {
        if (list.size == 4 || list.size == 9 || list.size == 16) {
            // 合成后的图片宽高
            val outW = 1920
            val outH = 1080
            // 图片距离
            val rowSize = sqrt(list.size.toDouble()).toInt()
            val imgDis = if (rowSize == 3) 3 else if (rowSize == 4) 4 else 2
            val imgW = (outW - (rowSize - 1) * imgDis) / rowSize
            val imgH = (outH - (rowSize - 1) * imgDis) / rowSize
            // 图片宽高比
            val imgProp = imgW.toFloat() / imgH
            L.d("composeImages w=$imgW h=$imgH rowSize=$rowSize")
            BaseConstants.getFixPool().execute {
                val startTime = SystemClock.elapsedRealtime()
                L.d("composeImages start ${list.size}")
                val bitmap = Bitmap.createBitmap(outW, outH, Bitmap.Config.ARGB_8888)
                val canvas = Canvas(bitmap)
                var orx = 0
                var ory = 0
                if (whiteDivider) {
                    canvas.drawColor(Color.WHITE)
                }
                for (i in list.indices) {
                    if (orx == rowSize) {
                        ory++
                        orx = 0
                    }
                    val rect = Rect(
                        orx * imgW + orx * imgDis,
                        ory * imgH + ory * imgDis,
                        orx * imgW + orx * imgDis + imgW,
                        ory * imgH + ory * imgDis + imgH,
                    )
                    BitmapFactory.decodeFile(list[i]).apply {
                        val proportion = width.toFloat() / height
                        val resRect = Rect(0, 0, width, height)
                        // 宽高比与目标宽高比差距很小,则直接忽略,选用SCALE_XY
                        if (abs(imgProp - proportion) < 0.2F) {
                            rect.set(rect.left, rect.top, rect.right, rect.bottom)
                        } else if (scaleType == SCALE_RES) {
                            if (imgProp > proportion) {
                                // 获取目标宽度差——使用高度缩放比获取目标显示宽度
                                val propW = (imgW - width * imgH.toFloat() / height) / 2
                                rect.set(
                                    rect.left + propW.toInt(),
                                    rect.top,
                                    rect.right - propW.toInt(),
                                    rect.bottom
                                )
                            } else {
                                // 获取目标高度差——使用宽度缩放比获取目标显示高度
                                val propH = (imgH - height * imgW.toFloat() / width) / 2
                                rect.set(
                                    rect.left,
                                    rect.top + propH.toInt(),
                                    rect.right,
                                    rect.bottom - propH.toInt()
                                )
                            }
                        } else if (scaleType == SCALE_CENTER) {
                            if (imgProp > proportion) {
                                // 获取裁剪的高度差——使用宽度缩放比
                                val h = (height - height * imgW.toFloat() / width) / 2
                                resRect.set(0, h.toInt(), width, height - h.toInt())
                            } else {
                                // 获取裁剪的宽度差——使用高度缩放比
                                val w = (width - width * imgH.toFloat() / height) / 2
                                resRect.set(w.toInt(), 0, width - w.toInt(), height)
                            }
                        }
                        L.d("composeImages $i $resRect $rect ${list[i]}")
                        canvas.drawBitmap(
                            this, resRect, rect, Paint()
                        )
                    }.recycle()
                    orx++
                }
                canvas.save()
                val outDir = File(pictureDirFile.absolutePath, "temp").apply {
                    if (exists()) {
                        listFiles()?.forEach {
                            if (it.name.startsWith("compose")) {
                                it.delete()
                            }
                        }
                    } else {
                        mkdir()
                    }
                }.absolutePath
                val savePath = "$outDir/compose${DateUtil.getNowTimeNum()}.jpg"
                FileUtils.saveBitmapToImage(bitmap, savePath)
                callback?.onComposeResult(savePath)
                L.d("composeImages end " + (SystemClock.elapsedRealtime() - startTime))
            }
        }
    }

    /**
     * 将图片分割成9等份
     */
    fun splitImage(path: String, callback: Callback? = null) {
        BaseConstants.getFixPool().execute {
            val startTime = SystemClock.elapsedRealtime()
            L.dd("start")
            try {
                val outDir = File(pictureDirFile.absolutePath, "temp").apply {
                    mkdir()
                }.absolutePath
                val bitmap = BitmapFactory.decodeFile(path)
                val outW = bitmap.width / 3
                val outH = bitmap.height / 3
                var orx = 0
                var ory = 0
                val arrayList = arrayListOf<String>()
                var outTemp = ""
                for (index in 0 until 9) {
                    if (orx == 3) {
                        ory++
                        orx = 0
                    }
                    outTemp = outDir + "/split" + (index + 1) + ".jpg"
                    Bitmap.createBitmap(bitmap, orx * outW, ory * outH, outW, outH)?.let {
                        FileUtils.saveBitmapToImage(it, outTemp)
                        arrayList.add(outTemp)
                    }
                    orx++
                }
                callback?.onSplitResult(arrayList)
            } catch (e: Exception) {
                e.printStackTrace()
                callback?.onError("splitImage fail!" + e.message)
            }
            L.dd("end " + (SystemClock.elapsedRealtime() - startTime))
        }
    }
}
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NineGridImageView 是一个九宫格图片控件。用法1. 首先添加依赖compile 'com.jaeger.ninegridimageview:library:1.0.0'2. 在布局文件中添加 NineGridImageView, 如下所示:nineGridImageView.setAdapter(nineGridViewAdapter);下面是 NineGridImageViewAdapter.class 的源码:public abstract class NineGridImageViewAdapter {     protected abstract void onDisplayImage(Context context, ImageView imageView, T t);     protected void onItemImageClick(Context context, int index, List list) {          }     protected ImageView generateImageView(Context context) {         GridImageView imageView = new GridImageView(context);         imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);         return imageView;     }}T 是你图片的数据类型, 你可以简单的使用 String 类型也可以是你自定义的类型;你必须重写 onDisplayImage(Context context, ImageView imageView, T t) 方法去设置显示图片的方式, 你可以使用 Picasso、Glide 、ImageLoader 或者其他的图片加载库,你也可以给 ImageView 设置一个占位;如果你需要处理图片的点击事件,你可以重写 onItemImageClick(Context context, int index, List list) 方法,加上你自己的处理逻辑;如果你要使用自定义的 ImageView,你可以重写 generateImageView(Context context) 方法, 去生成自定的 ImageView。下面是一段示例代码:private NineGridImageViewAdapter mAdapter = new NineGridImageViewAdapter() { @Override protected void onDisplayImage(Context context, ImageView imageView, Photo photo) { Picasso.with(context)                     .load(photo.getSmallUrl)                     .placeholder(R.drawable.ic_default_image)                     .into(imageView);             }         @Override         protected ImageView generateImageView(Context context) {             return super.generateImageView(context);         }         @Override         protected void onItemImageClick(Context context, int index, List photoList) {            showBigPicture(context, photoList.get(index).getBigUrl());         }     };         ... mNineGridImageView.setAdapter(mAdapter);...4. 给 NineGridImageView 设置图片数据:nineGridImageView.setImagesData(List imageDataList);图片展示:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值