Compose屏幕适配
一种Compose中屏幕适配的解决方案,灵感参考头条屏幕适配、AndroidAutoSize等,以设计稿宽度和屏幕水平方法大小为准,等比拉伸控件大小。
后文附有本方案的Kotlin语言实现,使用只需要两个步骤即可:
1 2 3 4 5 6 7 8 9 10
| class MainApp : Application() { override fun onCreate() { super.onCreate() SizeEtx.init(this, 375) } }
size(width = 9.composeDp, height = 16.composeDp)
|
主要的设计思想
假设如下变量:设计稿总宽度dpx
,控件在设计稿中的大小n
,屏幕的实际水平dp大小rdp
,以及我们需要求得的控件在设备中的dp值m
。
那么我们不难得到以下方程:
也就可以推导出:
上述值中,只有屏幕水平dp值rdp
还是未知的,又根据(density
在每个设备上都是固定的,DPI
/ 160 = density
,屏幕的总 px 宽度wpx
/ density
= 屏幕的总 dp 宽度rdp
)可知:
所以,我们可以推导出:
1 2 3
| m = n * (rdp / dpx) = n * ( wpx / density ) / dpx = n * wpx / (density * dpx)
|
到这里,等式后面的所有数据都为已知或者在app运行时可知,由此我们可以计算出设计稿中的控件在Compose中对应的dp大小。
下面是以上思路的kotlin实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| import android.content.Context import android.content.res.Configuration import android.content.res.Resources import android.graphics.Point import android.os.Build import android.view.WindowManager import androidx.compose.ui.unit.Dp
class SizeEtx private constructor(context: Context, dpx: Int) {
init { val density = Resources.getSystem().displayMetrics.density var wpx = Resources.getSystem().displayMetrics.widthPixels dpWidthScale = wpx.toFloat() / (dpx * density) dpHeightScale = getScreenRealHeightPx(context).toFloat() / (dpx * density) pxWidthScale = wpx.toFloat() / dpx.toFloat()
}
private fun getScreenRealHeightPx(context: Context): Int { val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager val display = windowManager.defaultDisplay val outPoint = Point() if (Build.VERSION.SDK_INT >= 19) { display.getRealSize(outPoint) } else { display.getSize(outPoint) } return outPoint.y }
companion object {
fun init(context: Context, dpx: Int) { SizeEtx(context, dpx) }
var dpWidthScale = 1.0f var dpHeightScale = 1.0f var pxWidthScale = 1.0f var pxHeightScale = 1.0f } }
inline val Number.composeDp: Dp get() { val isPortrait = isPortrait() return Dp(this.toFloat() * if (isPortrait) SizeEtx.dpWidthScale else SizeEtx.dpHeightScale) }
inline val Number.composePx: Int get() { val isPortrait = isPortrait() return (this.toFloat() * if (isPortrait) SizeEtx.pxWidthScale else SizeEtx.pxHeightScale).toInt() }
fun isPortrait() = Resources.getSystem().configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
参考文章
AndroidAutoSize
一种极低成本的Android屏幕适配方式