实现: 一种极低成本的Android屏幕适配方式
参考 一种极低成本的Android屏幕适配方式
单个 Activity 适配
1 2 3 4 5
| class MainActivity : ComponentActivity() { override fun attachBaseContext(newBase: Context) { super.attachBaseContext(newBase.createCustomDensityContext()) } }
|
全局适配
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MainActivity : ComponentActivity() { override fun attachBaseContext(newBase: Context) { newBase.setCustomDensity() super.attachBaseContext(newBase) } }
class MainApplication: Application() { override fun attachBaseContext(base: Context) { base.setCustomDensity() super.attachBaseContext(base) } }
|
只适配 这个 View 及其 子 view
谨慎使用,createCustomDensityContext 返回的 context 不是 Activity,
如果 子view 有地方拿 context 当做 Activity 使用就会有问题
1 2 3
| class FooView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : FrameLayout(context.createCustomDensityContext(), attrs)
|
实现代码
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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
| package com.xechoz.lib
import android.app.Application import android.content.ComponentCallbacks import android.content.Context import android.content.res.Configuration import android.util.AttributeSet import android.util.DisplayMetrics import android.view.ContextThemeWrapper import android.widget.FrameLayout
fun Context.setCustomDensity(uiWidthDp: Int = 360) { resources.displayMetrics.setCustomDensity(newCustomDensity(uiWidthDp)) }
fun Context.createCustomDensityContext(uiWidthDp: Int = 360): Context { val custom = newCustomDensity(uiWidthDp)
val copyConfiguration = Configuration() copyConfiguration.densityDpi = custom.targetDensityDpi copyConfiguration.setTo(resources.configuration)
val copyContext = createConfigurationContext(copyConfiguration)
val copyMetrics = DisplayMetrics() copyMetrics.setTo(resources.displayMetrics) copyMetrics.setCustomDensity(custom)
copyContext.resources.displayMetrics.setTo(copyMetrics) return if (this is ContextThemeWrapper) ContextThemeWrapper(copyContext, theme) else copyContext }
class CustomDensityLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : FrameLayout(context.createCustomDensityContext(uiWidthDp), attrs) { companion object { var uiWidthDp = 360 } }
private var nonCompatDensity = 0f private var nonCompatScaledDensity = 0f
private fun Context.newCustomDensity(toWidthDp: Int): CustomDensity { (this as? Application ?: applicationContext as? Application)?.let(::updateNonCompatDensity)
val targetDensity = 1f * resources.displayMetrics.widthPixels / toWidthDp val targetScaleDensity = 1f * targetDensity * (nonCompatScaledDensity / nonCompatDensity) val targetDensityDpi = (160 * targetScaleDensity).toInt()
return CustomDensity( targetDensity = targetDensity, targetScaledDensity = targetScaleDensity, targetDensityDpi = targetDensityDpi ) }
private fun updateNonCompatDensity(application: Application) { val displayMetrics = application.resources.displayMetrics if (nonCompatDensity == 0f) { nonCompatDensity = displayMetrics.density nonCompatScaledDensity = displayMetrics.scaledDensity
application.registerComponentCallbacks(object : ComponentCallbacks { override fun onConfigurationChanged(newConfig: Configuration) { if (newConfig.fontScale > 0) { nonCompatScaledDensity = application.resources.displayMetrics.scaledDensity } }
override fun onLowMemory() { } }) } }
private fun DisplayMetrics.setCustomDensity(custom: CustomDensity) { density = custom.targetDensity scaledDensity = custom.targetScaledDensity densityDpi = custom.targetDensityDpi }
private data class CustomDensity( val targetDensity: Float, val targetScaledDensity: Float, val targetDensityDpi: Int )
|