Android屏幕适配利器:Kotlin动态尺寸计算工具类完整封装

Kotlin 动态计算尺寸工具类封装

以下是使用 Kotlin 实现的动态计算尺寸工具类,包含基础版和增强版功能。

1. 基础工具类封装

kotlin 复制代码
import android.content.Context
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup

object ViewSizeUtils {

    /**
     * 按屏幕宽度百分比设置View的宽度
     * @param view 需要设置的View
     * @param widthPercent 宽度百分比 (0.0 - 1.0)
     */
    fun setWidthByScreenPercent(view: View, widthPercent: Float) {
        if (widthPercent <= 0 || widthPercent > 1) return
        
        val displayMetrics = getDisplayMetrics(view.context)
        view.updateLayoutParams {
            width = (displayMetrics.widthPixels * widthPercent).toInt()
        }
    }

    /**
     * 按屏幕高度百分比设置View的高度
     * @param view 需要设置的View
     * @param heightPercent 高度百分比 (0.0 - 1.0)
     */
    fun setHeightByScreenPercent(view: View, heightPercent: Float) {
        if (heightPercent <= 0 || heightPercent > 1) return
        
        val displayMetrics = getDisplayMetrics(view.context)
        view.updateLayoutParams {
            height = (displayMetrics.heightPixels * heightPercent).toInt()
        }
    }

    /**
     * 按屏幕宽高百分比同时设置View的宽高
     * @param view 需要设置的View
     * @param widthPercent 宽度百分比 (0.0 - 1.0)
     * @param heightPercent 高度百分比 (0.0 - 1.0)
     */
    fun setSizeByScreenPercent(view: View, widthPercent: Float, heightPercent: Float) {
        if (widthPercent <= 0 || widthPercent > 1 || heightPercent <= 0 || heightPercent > 1) return
        
        val displayMetrics = getDisplayMetrics(view.context)
        view.updateLayoutParams {
            width = (displayMetrics.widthPixels * widthPercent).toInt()
            height = (displayMetrics.heightPixels * heightPercent).toInt()
        }
    }

    /**
     * 按父容器宽度百分比设置View的宽度
     * @param view 需要设置的View
     * @param widthPercent 宽度百分比 (0.0 - 1.0)
     */
    fun setWidthByParentPercent(view: View, widthPercent: Float) {
        if (widthPercent <= 0 || widthPercent > 1 || view.parent !is View) return
        
        val parent = view.parent as View
        val parentWidth = parent.width
        if (parentWidth <= 0) {
            parent.post {
                setViewWidth(view, (parent.width * widthPercent).toInt())
            }
        } else {
            setViewWidth(view, (parentWidth * widthPercent).toInt())
        }
    }

    private fun setViewWidth(view: View, width: Int) {
        view.updateLayoutParams {
            this.width = width
        }
    }

    private fun getDisplayMetrics(context: Context): DisplayMetrics {
        return context.resources.displayMetrics
    }

    private inline fun View.updateLayoutParams(block: ViewGroup.LayoutParams.() -> Unit) {
        val params = layoutParams ?: ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        ).also { layoutParams = it }
        params.block()
        layoutParams = params
    }
}

2. 增强版工具类(支持更多功能)

kotlin 复制代码
import android.content.Context
import android.view.View
import android.view.ViewGroup
import kotlin.math.roundToInt

object ViewSizeHelper {

    /**
     * 设置View的宽高比(基于宽度计算高度)
     * @param view 目标View
     * @param aspectRatio 宽高比(宽度/高度)
     */
    fun setAspectRatio(view: View, aspectRatio: Float) {
        if (aspectRatio <= 0) return
        
        view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
            override fun onLayoutChange(
                v: View,
                left: Int, top: Int, right: Int, bottom: Int,
                oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int
            ) {
                val width = right - left
                if (width > 0) {
                    v.updateLayoutParams {
                        height = (width / aspectRatio).roundToInt()
                    }
                    v.removeOnLayoutChangeListener(this)
                }
            }
        })
    }

    /**
     * 设置View的最小高度(单位:dp)
     */
    fun setMinHeightDp(view: View, minHeightDp: Int) {
        view.minimumHeight = dpToPx(view.context, minHeightDp)
    }

    /**
     * 设置View的最小宽度(单位:dp)
     */
    fun setMinWidthDp(view: View, minWidthDp: Int) {
        view.minimumWidth = dpToPx(view.context, minWidthDp)
    }

    /**
     * 设置View的固定尺寸(单位:dp)
     */
    fun setFixedSizeDp(view: View, widthDp: Int, heightDp: Int) {
        view.updateLayoutParams {
            width = dpToPx(view.context, widthDp)
            height = dpToPx(view.context, heightDp)
        }
    }

    /**
     * 设置View的宽度(单位:dp)
     */
    fun setWidthDp(view: View, widthDp: Int) {
        view.updateLayoutParams {
            width = dpToPx(view.context, widthDp)
        }
    }

    /**
     * 设置View的高度(单位:dp)
     */
    fun setHeightDp(view: View, heightDp: Int) {
        view.updateLayoutParams {
            height = dpToPx(view.context, heightDp)
        }
    }

    /**
     * 设置View的margin(单位:dp)
     */
    fun setMarginsDp(
        view: View,
        leftDp: Int = Int.MIN_VALUE,
        topDp: Int = Int.MIN_VALUE,
        rightDp: Int = Int.MIN_VALUE,
        bottomDp: Int = Int.MIN_VALUE
    ) {
        val params = view.layoutParams as? ViewGroup.MarginLayoutParams ?: return
        
        if (leftDp != Int.MIN_VALUE) params.leftMargin = dpToPx(view.context, leftDp)
        if (topDp != Int.MIN_VALUE) params.topMargin = dpToPx(view.context, topDp)
        if (rightDp != Int.MIN_VALUE) params.rightMargin = dpToPx(view.context, rightDp)
        if (bottomDp != Int.MIN_VALUE) params.bottomMargin = dpToPx(view.context, bottomDp)
        
        view.layoutParams = params
    }

    private fun dpToPx(context: Context, dp: Int): Int {
        return (dp * context.resources.displayMetrics.density).roundToInt()
    }

    private inline fun View.updateLayoutParams(block: ViewGroup.LayoutParams.() -> Unit) {
        val params = layoutParams ?: ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        ).also { layoutParams = it }
        params.block()
        layoutParams = params
    }
}

3. 扩展函数版本(更 Kotlin 化的实现)

kotlin 复制代码
import android.content.Context
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import kotlin.math.roundToInt

// 扩展属性:获取屏幕宽度
val Context.screenWidth: Int
    get() = resources.displayMetrics.widthPixels

// 扩展属性:获取屏幕高度
val Context.screenHeight: Int
    get() = resources.displayMetrics.heightPixels

// 扩展函数:dp转px
fun Context.dpToPx(dp: Int): Int = (dp * resources.displayMetrics.density).roundToInt()

// 扩展函数:更新LayoutParams
inline fun View.updateLayoutParams(block: ViewGroup.LayoutParams.() -> Unit) {
    val params = layoutParams ?: ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.WRAP_CONTENT,
        ViewGroup.LayoutParams.WRAP_CONTENT
    ).also { layoutParams = it }
    params.block()
    layoutParams = params
}

// 基础功能扩展函数
fun View.setWidthByScreenPercent(widthPercent: Float) {
    if (widthPercent <= 0 || widthPercent > 1) return
    updateLayoutParams {
        width = (context.screenWidth * widthPercent).toInt()
    }
}

fun View.setHeightByScreenPercent(heightPercent: Float) {
    if (heightPercent <= 0 || heightPercent > 1) return
    updateLayoutParams {
        height = (context.screenHeight * heightPercent).toInt()
    }
}

fun View.setSizeByScreenPercent(widthPercent: Float, heightPercent: Float) {
    if (widthPercent <= 0 || widthPercent > 1 || heightPercent <= 0 || heightPercent > 1) return
    updateLayoutParams {
        width = (context.screenWidth * widthPercent).toInt()
        height = (context.screenHeight * heightPercent).toInt()
    }
}

// 增强功能扩展函数
fun View.setAspectRatio(aspectRatio: Float) {
    if (aspectRatio <= 0) return
    
    addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
        override fun onLayoutChange(
            v: View,
            left: Int, top: Int, right: Int, bottom: Int,
            oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int
        ) {
            val width = right - left
            if (width > 0) {
                v.updateLayoutParams {
                    height = (width / aspectRatio).roundToInt()
                }
                v.removeOnLayoutChangeListener(this)
            }
        }
    })
}

fun View.setMinHeightDp(minHeightDp: Int) {
    minimumHeight = context.dpToPx(minHeightDp)
}

fun View.setMinWidthDp(minWidthDp: Int) {
    minimumWidth = context.dpToPx(minWidthDp)
}

fun View.setFixedSizeDp(widthDp: Int, heightDp: Int) {
    updateLayoutParams {
        width = context.dpToPx(widthDp)
        height = context.dpToPx(heightDp)
    }
}

4. 使用示例

基础工具类使用

kotlin 复制代码
// 设置按钮宽度为屏幕的50%
ViewSizeUtils.setWidthByScreenPercent(myButton, 0.5f)

// 设置图片高度为屏幕的30%
ViewSizeUtils.setHeightByScreenPercent(myImageView, 0.3f)

// 设置TextView宽高分别为屏幕的80%和20%
ViewSizeUtils.setSizeByScreenPercent(myTextView, 0.8f, 0.2f)

// 设置RecyclerView宽度为父容器的70%
ViewSizeUtils.setWidthByParentPercent(recyclerView, 0.7f)

增强工具类使用

kotlin 复制代码
// 设置图片16:9的宽高比
ViewSizeHelper.setAspectRatio(myImageView, 16f/9f)

// 设置按钮最小高度为48dp
ViewSizeHelper.setMinHeightDp(myButton, 48)

// 设置固定尺寸为100dp×50dp
ViewSizeHelper.setFixedSizeDp(iconView, 100, 50)

// 设置margin
ViewSizeHelper.setMarginsDp(
    view = myView,
    leftDp = 16,
    topDp = 8,
    rightDp = 16,
    bottomDp = 8
)

扩展函数版本使用

kotlin 复制代码
// 使用扩展函数更简洁
myButton.setWidthByScreenPercent(0.5f)
myImageView.setHeightByScreenPercent(0.3f)
myTextView.setSizeByScreenPercent(0.8f, 0.2f)

// 设置宽高比
myVideoView.setAspectRatio(16f/9f)

// 设置最小尺寸
floatingButton.setMinWidthDp(48)
floatingButton.setMinHeightDp(48)

// 设置固定尺寸
iconView.setFixedSizeDp(100, 50)

这些 Kotlin 实现充分利用了语言特性,如扩展函数、属性、lambda 表达式等,使代码更加简洁易用。根据项目需求,可以选择工具类形式或扩展函数形式来实现动态尺寸计算。

相关推荐
MiyamuraMiyako22 分钟前
从 0 到发布:Gradle 插件双平台(MavenCentral + Plugin Portal)发布记录与避坑
android
NRatel1 小时前
Unity 游戏提升 Android TargetVersion 相关记录
android·游戏·unity·提升版本
叽哥3 小时前
Kotlin学习第 1 课:Kotlin 入门准备:搭建学习环境与认知基础
android·java·kotlin
风往哪边走4 小时前
创建自定义语音录制View
android·前端
用户2018792831674 小时前
事件分发之“官僚主义”?或“绕圈”的艺术
android
用户2018792831674 小时前
Android事件分发为何喜欢“兜圈子”?不做个“敞亮人”!
android
Kapaseker6 小时前
你一定会喜欢的 Compose 形变动画
android
QuZhengRong6 小时前
【数据库】Navicat 导入 Excel 数据乱码问题的解决方法
android·数据库·excel
zhangphil7 小时前
Android Coil3视频封面抽取封面帧存Disk缓存,Kotlin(2)
android·kotlin
程序员码歌13 小时前
【零代码AI编程实战】AI灯塔导航-总结篇
android·前端·后端