2018-08-29

今日頭條適配方案?


一、屏幕適配原理

1、Android中的dp、pxdpi、desity關(guān)系

px = density * dp;

density = dpi / 160;

px = dp * (dpi / 160);

其中dpi是根據(jù)屏幕的真實(shí)分辨率和尺寸計(jì)算的,每個(gè)設(shè)備可能都不一樣

2、為什么要算出 density,這和屏幕適配有什么關(guān)系呢?

public static float applyDimension(int unit, float value,

?????????????????????????????????????? DisplayMetrics metrics)

??? {

??????? switch (unit) {

??????? case COMPLEX_UNIT_PX:

??????????? return value;

??????? case COMPLEX_UNIT_DIP:

??????????? return value * metrics.density;

??????? case COMPLEX_UNIT_SP:

??????????? return value * metrics.scaledDensity;

??????? case COMPLEX_UNIT_PT:

??????????? return value * metrics.xdpi * (1.0f/72);

??????? case COMPLEX_UNIT_IN:

??????????? return value * metrics.xdpi;

??????? case COMPLEX_UNIT_MM:

??????????? return value * metrics.xdpi * (1.0f/25.4f);

??????? }

??????? return 0;

??? }


不管你在布局文件中填寫(xiě)的是什么單位,最后都會(huì)被轉(zhuǎn)化為px,系統(tǒng)就是通過(guò)上面的方法,將你在項(xiàng)目中任何地方填寫(xiě)的單位都轉(zhuǎn)換為px 的,所以我們常用的px轉(zhuǎn)dp的公式dp = px / density,就是根據(jù)上面的方法得來(lái)的

3、修改density的大小,保證在所有的設(shè)備上計(jì)算出來(lái)的px 的值正好是屏幕寬度(解決方案)


(1)、Denstiy是DisplayMetrics?中的成員變量,DisplayMetrics 實(shí)例通過(guò)?Resources#getDisplayMetrics()?可以獲得,而Resouces通過(guò)Activity或者Application的Context獲得

DisplayMetrics#density?就是上述的density

DisplayMetrics#densityDpi?就是上述的dpi

DisplayMetrics#scaledDensity?字體的縮放因子


正常情況下和density相等,但是調(diào)節(jié)系統(tǒng)字體大小后會(huì)改變這個(gè)值


(2)假設(shè)按照設(shè)計(jì)圖是320dp,依據(jù)寬度來(lái)適配


注:今日頭條的適配方式,今日頭條適配方案默認(rèn)項(xiàng)目中只能以高或?qū)捴械囊粋€(gè)作為基準(zhǔn),進(jìn)行適配


那么適配后的 density = 設(shè)備真實(shí)寬(單位px) / 320,接下來(lái)只需要把我們計(jì)算好的 density 在系統(tǒng)中修改下即可,代碼實(shí)現(xiàn)如下


// 獲取原始密度大小

private static float sRoncompatDennsity;

// 縮放比例因子

private static float sRoncompatScaledDensity;

private void setCustomDensity(@NonNull Activity activity,final @NonNull Application application) {

??? //application


final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();

??? if (sRoncompatDennsity == 0) {

??????? sRoncompatDennsity = appDisplayMetrics.density;

??????? sRoncompatScaledDensity = appDisplayMetrics.scaledDensity;

??????? //

當(dāng)應(yīng)用程序運(yùn)行中,系統(tǒng)配置發(fā)生改變時(shí),系統(tǒng)回調(diào)用此方法

??????? application.registerComponentCallbacks(new ComponentCallbacks() {

??????????? @Override

??????????? public void onConfigurationChanged(Configuration newConfig) {

??????????????? if (newConfig !=null && newConfig.fontScale > 0) {

???????????????? // 當(dāng)系統(tǒng)配置發(fā)生改變,縮放因子也隨之改變

??????????????????? sRoncompatScaledDensity = application.getResources()

??????????????????????????? .getDisplayMetrics().scaledDensity;

??????????????? }

??????????? }

??????? });

??? }

??? //

計(jì)算寬為320dp

??? final float targetDensity = appDisplayMetrics.widthPixels / 320;

??? final float targetScaledDensity = targetDensity * (sRoncompatScaledDensity /sRoncompatDennsity);

??? final int targetDensityDpi = (int) (targetDensity * 160);

??? appDisplayMetrics.density = targetDensity;

??? appDisplayMetrics.densityDpi = targetDensityDpi;

??? appDisplayMetrics.scaledDensity = targetScaledDensity;

??? //activity


final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();

??? activityDisplayMetrics.density = targetDensity;

??? activityDisplayMetrics.densityDpi = targetDensityDpi;

??? activityDisplayMetrics.scaledDensity = targetScaledDensity;

}




AndroidAutoSize 使用注意事項(xiàng)和原理

項(xiàng)目地址:https://github.com/JessYanCoding/AndroidAutoSize

1、AndroidAutosize使用注意事項(xiàng)

????????????? 我們還是依照寬度為320dp為基準(zhǔn)說(shuō)明

[if !supportLists]a、? [endif]當(dāng)我們的設(shè)計(jì)圖寬度為填充整個(gè)屏幕的寬度時(shí),我們的寬度寫(xiě)成layout_width="320dp"和layout_width="match_parent"都可以


[if !supportLists]b、? [endif]對(duì)于固定尺寸的圖片或者其他控件,如果mark圖上面明確標(biāo)注了寬、高,我就按照設(shè)計(jì)圖寬和高的1\2設(shè)置對(duì)應(yīng)的寬、高(單位為:dp)

2、AndroidAutosize原理

?????? AndroidAntuosize 第三方庫(kù)的實(shí)現(xiàn)基本原理和今日頭條適配原理一樣,其實(shí)就是對(duì)今日頭條適配方案的封裝

1、通過(guò)聲明{@link

InitProvider} 自動(dòng)啟動(dòng)初始化

Provider是由ActivityThread負(fù)責(zé)啟動(dòng)的,ActivityThread對(duì)應(yīng)應(yīng)用進(jìn)程的主線(xiàn)程,即在應(yīng)用進(jìn)程啟動(dòng)時(shí),會(huì)將ContentProvider啟動(dòng)起來(lái)。


@Override

public boolean onCreate() {

??? AutoSizeConfig.getInstance()

??????????? .setLog(true)

??????????? .init((Application) getContext().getApplicationContext())

??????????? .setUseDeviceSize(false);

??? return true;

}



2、配置AutosizeConfig中完成具體的初始化工作

?? 注意:初始化方法只能調(diào)用一次, 否則報(bào)錯(cuò),對(duì)activity和fragment的生命周期注冊(cè)監(jiān)聽(tīng),初始化默認(rèn)的設(shè)配策略(在manifest 中配置)如果對(duì)某個(gè)activiyh 進(jìn)行了自定義策略,則使用自定義策略

/**

?*框架會(huì)在APP 啟動(dòng)時(shí)自動(dòng)調(diào)用此方法進(jìn)行初始化, 使用者無(wú)需手動(dòng)初始化, 初始化方法只能調(diào)用一次, 否則報(bào)錯(cuò)

?*

?* @param application?? {@link Application}

?* @param isBaseOnWidth詳情請(qǐng)查看{@link #isBaseOnWidth} 的注釋

?* @param strategy????? {@link AutoAdaptStrategy},{@code null} 則使用{@link DefaultAutoAdaptStrategy}

?*/

AutoSizeConfig init(final Application application, boolean isBaseOnWidth, AutoAdaptStrategy strategy) {

??? Preconditions.checkArgument(mInitDensity == -1, "AutoSizeConfig#init() can only be called once");

??? Preconditions.checkNotNull(application, "application == null");

??? this.mApplication = application;

??? this.isBaseOnWidth = isBaseOnWidth;

??? final DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();

??? //manifest中獲取配置的寬或者高

??? getMetaData(application);

//獲取屏幕的真實(shí)寬度和高度

??? int[] screenSize = ScreenUtils.getScreenSize(application);

??? mScreenWidth = screenSize[0];

?? ?mScreenHeight = screenSize[1];

??? mInitDensity = displayMetrics.density;

??? mInitDensityDpi = displayMetrics.densityDpi;

??? mInitScaledDensity = displayMetrics.scaledDensity;

??? application.registerComponentCallbacks(new ComponentCallbacks() {

??????? @Override

??????? public void onConfigurationChanged(Configuration newConfig) {

??????????? if (newConfig != null) {

??????????????? if (newConfig.fontScale > 0) {

??????????????????? mInitScaledDensity =

??????????????????????????? Resources.getSystem().getDisplayMetrics().scaledDensity;

??????????????? int[] screenSize = ScreenUtils.getScreenSize(application);

??????????????? mScreenWidth = screenSize[0];

??????????????? mScreenHeight = screenSize[1];

??????????? }

??????? }

??????? @Override

??????? public void onLowMemory() {

???? ???}

??? });

??? mActivityLifecycleCallbacks = new ActivityLifecycleCallbacksImpl(strategy == null ? new DefaultAutoAdaptStrategy() : strategy);

??? //對(duì)Activity的生命周期事件進(jìn)行監(jiān)聽(tīng)

??? application.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);

? ??return this;

}


3、獲取使用者在Androidmanifest中填寫(xiě)的meta信息


?? ????????android:value="320"/>

?????????? android:value="568"/>


private void getMetaData(final Context context) {

??? new Thread(new Runnable() {

??????? @Override

??????? public void run() {

??????????? PackageManager packageManager = context.getPackageManager();

??????????? ApplicationInfo applicationInfo;

??????????? try {

??????????????? applicationInfo = packageManager.getApplicationInfo(context

??????????????????????? .getPackageName(), PackageManager.GET_META_DATA);

??????????????? if (applicationInfo != null && applicationInfo.metaData != null) {

??????????????????? if (applicationInfo.metaData.containsKey(KEY_DESIGN_WIDTH_IN_DP)) {

??????????????????????? mDesignWidthInDp = (int) applicationInfo.metaData.get(KEY_DESIGN_WIDTH_IN_DP);

??????????????????? }

??????????????????? if (applicationInfo.metaData.containsKey(KEY_DESIGN_HEIGHT_IN_DP)) {

??????? ????????????????mDesignHeightInDp = (int) applicationInfo.metaData.get(KEY_DESIGN_HEIGHT_IN_DP);

??????????????????? }

??????????????? }

??????????? } catch (PackageManager.NameNotFoundException e) {

??????????????? e.printStackTrace();

??????????? }

???? ???}

??? }).start();

}


4、默認(rèn)配置策略


這里是今日頭條適配方案的核心代碼, 核心在于根據(jù)當(dāng)前設(shè)備的實(shí)際情況做自動(dòng)計(jì)算并轉(zhuǎn)換DisplayMetrics#density、DisplayMetrics#scaledDensity、DisplayMetrics#densityDpi這三個(gè)值, 有興趣請(qǐng)看下面的鏈接設(shè)計(jì)圖上的設(shè)計(jì)尺寸, sizeInDp設(shè)計(jì)圖上的設(shè)計(jì)尺寸,單位dp, 如果isBaseOnWidth設(shè)置為true,則應(yīng)該填寫(xiě)設(shè)計(jì)圖的總寬度, 如果isBaseOnWidth 設(shè)置為false, sizeInDp 則應(yīng)該填寫(xiě)設(shè)計(jì)圖的總高度,isBaseOnWidth 是否按照寬度進(jìn)行等比例適配, true為以寬度進(jìn)行等比例適配, false 為以高度進(jìn)行等比例適配


public static void autoConvertDensity(Activity activity, float sizeInDp, boolean isBaseOnWidth) {

??? Preconditions.checkNotNull(activity, "activity == null");

??? int screenSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getScreenWidth()

??????????? : AutoSizeConfig.getInstance().getScreenHeight();

??? String key = sizeInDp + "|" + isBaseOnWidth + "|"

??????????? + AutoSizeConfig.getInstance().isUseDeviceSize() + "|"

??????????? + AutoSizeConfig.getInstance().getInitScaledDensity() + "|"

??????????? + screenSize;

??? DisplayMetricsInfo displayMetricsInfo = mCache.get(key);

??? float targetDensity = 0;

??? int targetDensityDpi = 0;

??? float targetScaledDensity = 0;

??? if (displayMetricsInfo == null) {

??????? if (isBaseOnWidth) {

??????????? targetDensity = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / sizeInDp;

??????? } else {

????? ??????targetDensity = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / sizeInDp;

??????? }

??????? targetScaledDensity = targetDensity * (AutoSizeConfig.getInstance().

??????????????? getInitScaledDensity() * 1.0f / AutoSizeConfig.getInstance().getInitDensity());

??????? targetDensityDpi = (int) (targetDensity * 160);

??????? mCache.put(key, new DisplayMetricsInfo(targetDensity, targetDensityDpi, targetScaledDensity));

??? } else {

??????? targetDensity = displayMetricsInfo.density;

??????? targetDensityDpi = displayMetricsInfo.densityDpi;

??????? targetScaledDensity = displayMetricsInfo.scaledDensity;

??? }

?? //對(duì)activity applicationDisplayMetrics設(shè)置參數(shù)

???? setDensity(activity, targetDensity, targetDensityDpi, targetScaledDensity);

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容