Android屏幕适配
屏幕适配
原因:Android设备碎片化,导致APP的界面元素在不同的屏幕尺寸上显示不一致。
目的:让布局,布局组件,资源,用户界面流程,匹配不同的屏幕尺寸。
屏幕适配常见方式
- 布局适配
1、避免写死控件尺寸,使用match_parent,wrap_content。
2、LinearLayout使用android:layout_weight=“1”,android:weightSum="4"等等。
3、RelativeLayout的android:layout_centerInParent=“true”,
android:layout_centerVertical=“true”,android:layout_centerHorizontal=“true”
4、ConstraintLayout(RelativeLayout的加强版,更强大的功能)
5、百分比库
- 图片资源适配
1、使用.9或者svg图实现缩放
2、使用多套位图匹配不同的屏幕分辨率
- 用户流程适配
1、根据业务逻辑执行不同的跳转逻辑
2、根据别名展示不同的界面
- 限定符适配
1、分辨率限定符mipmap-hdpi,drawable-hdpi等等
2、尺寸限定符:layout-small,layout-large等
3、最小宽度限定符:values-sw360dp,values-sw480dp等
4、屏幕方向限定符:layout-land,layout-port等
- 刘海屏适配
Android 9.0官方适配
华为、OPPO、VIVO,、小米等等
一、像素适配
自定义像素适配,自定义View来适配。
以一个特定宽度尺寸的设备为参考,在View的加载过程中,根据当前设备的实际像素换算出目标像素,再作用在控件上。
工具类,计算屏幕宽高,求出目标View和参考图的缩放比
STANDARD_WIDTH
STANDARD_HEIGHT
mScreenWidth
mScreenHeight
mInstance
context
mInstance
mInstance
mInstance context
mInstance
context
mScreenHeight mScreenWidth
wm contextWINDOW_SERVICE
wm
metrics
wmmetrics
metricswidthPixels metricsheightPixels
mScreenWidth metricsheightPixels
mScreenHeight metricswidthPixels
mScreenWidth metricswidthPixels
mScreenHeight metricsheightPixels context
context
resId context
resId
contextresId
mScreenWidth STANDARD_WIDTH
mScreenHeight STANDARD_HEIGHT
适配的ViewGroup,在onMeasure中根据缩放比,重新去计算目标View的宽高
* 是否被测量
isMeasure
context
context
context attrs
context attrs
context attrs defStyleAttr
context attrs defStyleAttr
widthMeasureSpec heightMeasureSpec
isMeasure
horizontalScale
verticalScale
childCount
i i childCount i
child i
params child
paramswidth paramswidth horizontalScale
paramsheight paramsheight horizontalScale
paramsleftMargin paramsleftMargin horizontalScale
paramsrightMargin paramsrightMargin horizontalScale
paramstopMargin paramstopMargin verticalScale
paramsbottomMargin paramsbottomMargin verticalScale
widthMeasureSpec heightMeasureSpec
布局文件
xmlnsandroid
androidorientation
androidlayout_width
androidlayout_height
androidlayout_width
androidlayout_height
androidbackground
自定义View像素适配,不能穿透适配,也就是当前容器是像素适配容器,它其中如果还有容器需要适配,也依然要使用像素适配容器。造成的结果就是如果需求更改,会改的很惨。O(∩_∩)O哈哈~。
二、百分比布局适配
自定义百分比适配,继承对应的ViewGroup,在使用百分比适配的情况下,不影响ViewGroup本身的属性的使用。
Google本身提供了百分比库,我们正常情况下需要使用直接导入Google的百分比库就好。
以下例子我们直接继承RelativeLayout,在
onMeasure()中去重新计算我们的宽高。
继承RelativeLayout的LayoutParams,实现以下构造方法LayoutParams(Context c, AttributeSet attrs),在构造方法中去获取我们自定义属性,这个可以参考RelativeLayout本身的实现。
实现generateLayoutParams,将我们自己的LayoutParams返回
context
context
context attrs
context attrs
context attrs defStyleAttr
context attrs defStyleAttr
widthMeasureSpec heightMeasureSpec
parentWidth widthMeasureSpec
parentHeight heightMeasureSpec
childCount
i i childCount i
child i
layoutParams child
layoutParams
params layoutParams
widthPercent paramswidthPercent
heightPercent paramsheightPercent
marginLeftPercent paramsmarginLeftPercent
marginRightPercent paramsmarginRightPercent
marginTopPercent paramsmarginTopPercent
marginBottomPercent paramsmarginBottomPercent
widthPercent
paramswidth parentWidth widthPercent
heightPercent
paramsheight parentHeight heightPercent
marginLeftPercent
paramsleftMargin parentWidth marginLeftPercent
marginRightPercent
paramsrightMargin parentWidth marginRightPercent
marginTopPercent
paramstopMargin parentHeight marginTopPercent
marginBottomPercent
paramsbottomMargin parentHeight marginBottomPercent
widthMeasureSpec heightMeasureSpec
attrs
attrs
p
p
widthPercent
heightPercent
marginLeftPercent
marginRightPercent
marginTopPercent
marginBottomPercent
c attrs
c attrs
typedArray cattrs styleablePercentRelativeLayout
widthPercent typedArraystyleablePercentRelativeLayout_widthPercent
heightPercent typedArraystyleablePercentRelativeLayout_heightPercent
marginLeftPercent typedArraystyleablePercentRelativeLayout_marginLeftPercent
marginRightPercent typedArraystyleablePercentRelativeLayout_marginRightPercent
marginTopPercent typedArraystyleablePercentRelativeLayout_marginTopPercent
marginBottomPercent typedArraystyleablePercentRelativeLayout_marginBottomPercent
typedArray
具体使用
xmlnsandroid
xmlnstools
xmlnsapp
androidorientation
androidlayout_width
androidlayout_height
androidlayout_width
androidlayout_height
androidbackground
androidtextSize
androidtext
appwidthPercent
appheightPercent
androidgravity
我们设置的百分比都是相对于父容器的,相对于父容器的宽度百分比,高度百分比
三、修改像素密度
我最早知道是今日头条开放出来的,具体可以参考
字节跳动:一种极低成本的Android屏幕适配方式
修改density(屏幕密度),scaleDensity(一般用于字体,通常情况下与density相等),densityDpi(屏幕上每一英寸的像素点)的值,直接更改系统内部对于目标尺寸而言的像素密度。
android中的dp在渲染前会将dp转为px,计算公式:
- px = density * dp;
- density = dpi / 160;
- px = dp * (dpi / 160);
而dpi是根据屏幕真实的分辨率和尺寸来计算的,每个设备都可能不一样的。
具体工具类
就是根据设计稿去修改目标Activity的Density等值
* 设计稿宽度(dp)
DEFAULT_WIDTH
appDensity
appScaleDensity
application activity
displayMetrics application
appDensity
appDensity displayMetricsdensity
appScaleDensity displayMetricsscaledDensity
application
newConfig
newConfig newConfigfontScale
appScaleDensity applicationscaledDensity
targetDensity displayMetricswidthPixels DEFAULT_WIDTH
targetScaleDensity targetDensity appScaleDensity appDensity
targetDensityDpi targetDensity
dm activity
dmdensity targetDensity
dmscaledDensity targetScaleDensity
dmdensityDpi targetDensityDpi
工具类具体调用在Activity的onCreate()的setContentView()方法之前。可以在每个Activity单独设置,也可以在BaseActivity中设置,或者在Application实现Activity的监听。
Application的onCreate()中监听,自己实现一个BaseApplication继承Application。
activity savedInstanceState
activity
activity
activity
activity
activity
activity outState
activity
布局文件,我们适配稿给的360dp的宽度,所以我们分别对两个控件设置180dp,实际适配之后结果应该是平分屏幕宽度。
xmlnsandroid
xmlnstools
xmlnsapp
androidlayout_width
androidlayout_height
androidid
androidlayout_width
androidlayout_height
androidbackground
androidtextSize
androidtext
applayout_constraintTop_toTopOf
applayout_constraintStart_toStartOf
androidgravity
androidid
androidlayout_width
androidlayout_height
androidbackground
androidtextSize
androidtext
applayout_constraintTop_toBottomOf
applayout_constraintStart_toEndOf
androidgravity
效果对比,适配前和适配后:
最后,各种适配方式没有什么是最好的,合适的才是最好的,有句话怎么说的(不适配就是最好的适配),哈哈。平时的适配根据具体项目具体选择采用什么样的适配方式。还有我们可以选择对整体界面适配也可以对单独的某一块进行适配。
四、刘海屏适配
Android 9.0官方适配方式
- 如果非全屏模式(有状态栏),则APP不受刘海屏影响,刘海屏的高度就是状态栏的高度。
- 如果是全屏模式,APP未适配刘海屏,系统会对界面做特殊处理,竖屏向下移动,横屏向右移动。
其他手机厂商的适配:
- 华为:https://devcenter-test.huawei.com/consumer/cn/devservice/doc/50114,打不开。华为开放平台也没有找到相应的文档。
- 小米:https://dev.mi.com/console/doc/detail?pId=1293
- OPPO:https://open.oppomobile.com/wiki/doc\#id=10159
- VIVO:https://dev.vivo.com.cn/documentCenter/doc/103
官方适配方案
日常适配刘海屏,我们需要对厂商的版本单独处理,很多厂商都有他们自己的适配方式。比如华为,小米,OPPO等。
1、取消标题栏,设置全屏
2、判断手机厂商
3、判断手机是否有刘海
4、设置是否让内容区域延伸进刘海
5、设置控件是否避开刘海区域
6、获取刘海的高度(一般就是状态栏的高度)
设置全屏,设置内容延伸至刘海等操作都是在setContentView()方法之前
override fun savedInstanceState
savedInstanceState
FEATURE_NO_TITLE
window
FLAG_FULLSCREEN
FLAG_FULLSCREEN
window
val attributes windowattributes
* LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 全屏模式,内容下移,非全屏不受影响
* LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 允许内容去延伸进刘海区
* LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 不允许内容延伸进刘海区
VERSIONSDK_INT
attributeslayoutInDisplayCutoutMode
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
windowattributes attributes
val flags
SYSTEM_UI_FLAG_FULLSCREEN or SYSTEM_UI_FLAG_HIDE_NAVIGATION or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
systemUiVisibility windowdecorViewsystemUiVisibility
systemUiVisibility systemUiVisibilityflags
windowdecorViewsystemUiVisibility systemUiVisibility
layoutactivity_shaped_screen
val layoutParams
tvShapelayoutParams as
layoutParamstopMargin
tvShapelayoutParams layoutParams
* 判断是否是刘海屏
fun window
val displayCutout
val decorView windowdecorView
VERSIONSDK_INT
val rootWindowInsets decorViewrootWindowInsets
VERSIONSDK_INT rootWindowInsets
displayCutout rootWindowInsetsdisplayCutout
displayCutout
displayCutoutboundingRectssize displayCutoutsafeInsetBottom
* 获取状态栏高度(通常情况下,刘海的高度等于状态栏的高度)
fun
val resId resources
resId
resourcesresId
五、单独适配
对需要适配的布局进行单独适配,相对来说更麻烦,但是效果也是最好的。
跟屏幕相关的缩放比,水平,竖直高度的工具类
instance
DEFAULT_WIDTH
DEFAULT_HEIGHT
displayMetricsWidth
displayMetricsHeight
mStatusBarHeight
context
wm contextWINDOW_SERVICE
wm
displayMetrics
displayMetricsWidth displayMetricsHeight
wmdisplayMetrics
mStatusBarHeight context
displayMetricswidthPixels displayMetricsheightPixels
displayMetricsWidth displayMetricsheightPixels
displayMetricsHeight displayMetricswidthPixels
displayMetricsWidth displayMetricswidthPixels
displayMetricsHeight displayMetricsheightPixels
context
instance
instance
instance context
instance
instance
instance
* 水平缩放比
*
displayMetricsWidth DEFAULT_WIDTH
* 竖直缩放比
*
displayMetricsHeight DEFAULT_HEIGHT
* 获取宽度
width
width displayMetricsWidth DEFAULT_WIDTH
* 获取高度
height
height displayMetricsHeight DEFAULT_HEIGHT
* 用于得到状态框的高度
context
resourceId context
height contextresourceId
height
height
context
context dimeClass system_bar_height defaultValue
clazz dimeClass
object clazz
field clazzsystem_bar_height
obj fieldobject
obj defaultValue
id obj
contextid
e
e
defaultValue
对单独布局进行适配的工具类
* 设置字体大小
*
textView size
textViewCOMPLEX_UNIT_PX size
view width height lefMargin topMargin
rightMargin bottomMargin asWidth
layoutParams view
layoutParams
width MATCH_PARENT
width WRAP_CONTENT
layoutParamswidth width
layoutParamswidth width
height MATCH_PARENT
height WRAP_CONTENT
layoutParamsheight asWidth height height
layoutParamsheight height
layoutParamstopMargin asWidth topMargin topMargin
layoutParamsbottomMargin asWidth bottomMargin bottomMargin
layoutParamsleftMargin lefMargin
layoutParamsrightMargin rightMargin
viewlayoutParams
* 设置view的内边距
*
view topPadding bottomPadding leftPadding rightPadding
viewleftPadding
topPadding
rightPadding
bottomPadding
view width height topMargin bottomMargin lefMargin
rightMargin asWidth
layoutParams view
width MATCH_PARENT
width WRAP_CONTENT
layoutParamswidth width
layoutParamswidth width
height MATCH_PARENT
height WRAP_CONTENT
layoutParamsheight asWidth height height
layoutParamsheight height
layoutParamstopMargin asWidth topMargin topMargin
layoutParamsbottomMargin asWidth bottomMargin bottomMargin
layoutParamsleftMargin lefMargin
layoutParamsrightMargin rightMargin
viewlayoutParams
view width height topMargin bottomMargin lefMargin
rightMargin asWidth
layoutParams view
width MATCH_PARENT width WRAP_CONTENT
layoutParamswidth width
layoutParamswidth width
height MATCH_PARENT height WRAP_CONTENT
layoutParamsheight asWidth height height
layoutParamsheight height
layoutParamstopMargin asWidth topMargin topMargin
layoutParamsbottomMargin asWidth bottomMargin bottomMargin
layoutParamsleftMargin lefMargin
layoutParamsrightMargin rightMargin
viewlayoutParams
view width height asWidth
layoutParams view
width MATCH_PARENT width WRAP_CONTENT width FILL_PARENT
layoutParamswidth width
layoutParamswidth width
height MATCH_PARENT height WRAP_CONTENT height FILL_PARENT
layoutParamsheight asWidth height height
layoutParamsheight height
viewlayoutParams