Andorid 屏幕适配
本文链接:https://blog.csdn.net/feather_wch/article/details/131672378
参考文章:Android 适配归纳总结
1、dpi是什么?
- 屏幕像素密度
- 每英寸内160个像素点,160dpi
- dpi = px / in
2、dp和px转换
- px = dp * (dpi / 160)
- density: dpi / 160
3、适配策略(宽度百分比,高度长宽比)
- 控件宽度按照UI设计的大小
- 控件高度按照样张百分比算出来
头条适配方案
1、让dp具有百分比的效果
- px = dp * density
- 修改density(存储在DisplayMetrics)
- Activity启动时调用相关适配方法,传入activity、application(应对两者的DisplayMetrics不同的情况)
- 非常轻量级
2、DisplayMetrics有哪些重要的属性和方法?
- widthPixels:显示设备的宽度,以像素为单位。
- heightPixels:显示设备的高度,以像素为单位。
- density:显示设备的逻辑密度,即每英寸点数(dpi)除以160的比值。
- densityDpi:显示设备的物理密度,即每英寸点数(dpi)。
- scaledDensity:显示设备的缩放密度,用于文字缩放,通常与density相同,除非用户修改了字体大小。
3、需要重新计算density,就以样张360dp为标准
- 计算出density、densityDpi、scaledDensity
- 分配设置给Application和Activity的DisplayMetrics
公式:px = dp * density, density = px / dp
新density = DisplayMetrics.widthPixels / 360
公式:density = dpi / 160,dpi = density * 160
新densityDpi = 160 * 新density
// 设置DisplayMetrics
displayDensity.density = 新density
displayDensity.densityDpi = 新densityDpi
4、字体缩放有问题,要么偏大要么偏小,如何处理?scaledDensity
- scaledDensity需要将density密度按照等比例进行缩放
- 系统修改字体大小,会触发接口,接口触发后要重新算字体的scaledDensity
百分比方案
5、百分比方案如何实现的?
- 算出生成xml文件,不同分辨率不同xml文件,
- dp、px用x1、y1等参数,内部会自己换算
DisplayMetrics源码
1、DisplayMetrics什么时候设置的?
- DisplayMetrics是在WindowManagerService类中设置的。
【SystemServer.java】
private void startOtherServices() {
final Context context = mSystemContext;
WindowManagerService wm = null;
InputManagerService inputManager = null;
...
//创建WindowManagerService对象
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
//添加到服务管理器
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
...
//调用displayReady方法
wm.displayReady();
...
}
【WMS】
public void displayReady() {
synchronized (mWindowMap) {
//初始化显示设备
for (Display display : mDisplays) {
DisplayContent displayContent = mRoot.getDisplayContentOrCreate(display.getDisplayId());
//配置显示策略
configureDisplayPolicyLocked(displayContent);
//设置布局标志
displayContent.layoutNeeded = true;
//更新方向
boolean configChanged = updateOrientationFromAppTokensLocked(false);
//设置临时配置
mTempConfiguration.setToDefaults();
mTempConfiguration.fontScale = 1;
//更新资源配置
if (configChanged) {
mWaitingForConfig = true;
startFreezingDisplayLocked(false, 0, 0, displayContent);
//发送配置变化广播
sendNewConfiguration(display.getDisplayId());
}
}
//初始化输入设备
mInputMonitor.setUpdateInputWindowsNeededLw();
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
}
【PhoneWindowManager】- WindowManagerPolicy的实现类
private void configureDisplayPolicyLocked(DisplayContent displayContent) {
//设置显示设备的初始尺寸和密度
mPolicy.setInitialDisplaySize(displayContent.getDisplay(),
displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight,
displayContent.mBaseDisplayDensity);
//设置显示设备的overscan区域
DisplayInfo displayInfo = displayContent.getDisplayInfo();
mPolicy.setDisplayOverscan(displayContent.getDisplay(),
displayInfo.overscanLeft, displayInfo.overscanTop,
displayInfo.overscanRight, displayInfo.overscanBottom);
}
【PhoneWindowManager】- WindowManagerPolicy的实现类
public void setInitialDisplaySize(Display display, int width, int height, int density) {
if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
synchronized (mLock) {
//初始化真实屏幕大小
mOverscanScreenWidth = mOverscanScreenHeight = 0;
mUnrestrictedScreenWidth = width;
mUnrestrictedScreenHeight = height;
//初始化DisplayMetrics
mDisplayWidth = width;
mDisplayHeight = height;
mDisplayDensity = density;
mSystemGestures.screenWidth = width;
mSystemGestures.screenHeight = height;
}
//更新显示区域
updateOrientationListenerLp();
updateRotation(true);
}
}