安卓螢幕適配方案(根據今日頭條方案,升級版)
前言
螢幕適配方案有很多,比如原生的dp,鴻洋大神的AutoLayout,寬高限定符,今天我用缺點比較小的今日頭條方案
使用效果
測試後可以適配我身邊的所有機型(其他的應該是都可以)
- 設定Activity後Activity可以適配
- 設定Activity後Activity內的Fragment可以適配
- 設定Activity後Activity內的RecyclerView可以適配
- 設定Activity後Activity彈出的Dialog可以適配
優缺點
優點:適配簡單,無侵入,呼叫簡單,未使用非官方api,不影響效能
缺點:基本可以通過騷操作來避免
使用方式
複製工具類
- import android.app.Activity;
- import android.util.DisplayMetrics;
- /**
- * 創 建: lt 2018/8/15--14:45
- * 作 用: 使用並優化今日頭條的適配方案的工具類
- * 注意事項: 在Activity的onCreate裡,並在setContextView之上呼叫,可以直接放在Base裡
- */
- public class FitUtil {
- private static float width = 750;//todo 手動設定為設計圖的寬,適配將根據寬為基準,也可以設定高,但是推薦設定寬,如果不需要px=dp則不設定也行
- private static int dpi = 375
;//todo 手動設定設計圖的dpi,不知道可以設計圖的寬除2測試一下 - private static float nativeWidth = 0;//真實螢幕的寬,不需要手動改
- /**
- * 在Activity的onCreate中呼叫,修改該Activity的density,即可完成適配,使用寬高直接使用設計圖上px相等的dp值
- *
- * @param activity 需要改變的Activity
- * @param isPxEqualsDp 是否需要設定為設計圖上的px直接在xml上寫dp值(意思就是不需要自己計算dp值,直接寫設計圖上的px值,並改單位為dp),但開啟後可能需要手動去設定ToolBar的大小,如果不用可以忽略
- */
- public static void autoFit(Activity activity, boolean isPxEqualsDp) {
- if (nativeWidth == 0) {
- nativeWidth = activity.getWindowManager().getDefaultDisplay().getWidth();
- }
- DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
- displayMetrics.density = isPxEqualsDp ? nativeWidth / dpi / (width / dpi) : nativeWidth / dpi;
- displayMetrics.densityDpi = (int) (displayMetrics.density * 160);
- }
- }
使用
在Activity的onCreate裡,並在setContextView之上呼叫,可以直接放在Base裡,比如:
- public class BaseActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- FitUtil.autoFit(this,true);//this
- }
- }
手動設定好UI給你的設計圖的寬和dp(請檢視註釋自行計算)
單位用dp,字型單位也用dp,就可以自動適配了
需要注意方法的第二個引數
如果UI給你的圖紙是按照dp為單位,就設定為false,然後頁面上直接寫相應的dp值就ok
如果UI給你的圖紙是按照px為單位,就設定為true,然後頁面上寫對應的px值,但是單位寫成dp,相當於直接從UI設計圖上抄下來,很方便
但是推薦設定為false,如果設計圖紙是px為單位則自行計算,因為設定為true會使其他的三方View變得偏小
某些可以避免的坑
- 字型單位的坑,sp可以通過修改scaledDensity屬性來修改,不過為了防止使用者調節字型導致字型顯示不全,所以推薦使用dp,而不用去修改scaledDensity屬性
- 聽說8.0手機無效,不過我在自己的8.0手機上可以(華為)
- 本方案對純豎屏應用支援較好,若是純橫屏或橫豎屏相交叉的話需要自行修改方法即可
擴充套件
由於px=dp方案留有一些坑(三方View框架也會用到dp值,而部分無法手動去修改),所以該px=dp的方案並不推薦使用,但是若小夥伴又想如此適配,又想使用px=dp方案的話,接下來我會提供一下騷操作供參考
檢視原始碼可知,系統在使用距離值的時候會把所有的單位值轉換為px值來應用,而轉換方法如下:
- TypedValue.java下
- 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;
- }
可以看到系統提供了六種單位,上面就是修改了dp的計算係數來適配螢幕,而一些三方框架可能會使用dp,sp甚至是px來給View設定寬高,所以可以將目光放在基本不用的單位上
騷操作來了
我們可以使用in這個單位(英寸),修改方法:
- public static void autoFit2(Activity activity, boolean isPxEqualsDp) {
- if (nativeWidth == 0) {
- nativeWidth = activity.getWindowManager().getDefaultDisplay().getWidth();
- }
- DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
- displayMetrics.xdpi = isPxEqualsDp ? nativeWidth / dpi / (width / dpi) : nativeWidth / dpi;
- }
然後所有值抄設計圖上的,單位用in,則可以解決上述問題
親測可以使用,並且適配三方框架,也不會影響到系統控制元件;但,該方案缺點也有,所以該方案僅供參考
- 預覽慘不忍睹,因為in單位是英寸,手機才幾英寸,而View的值動不動就是幾十幾百的,預覽時基本上就是一個控制元件佔滿螢幕
- 由於本人只是測試了該方案(n多種情況下),但是並沒有正式用在專案上,所以會不會出現奇葩情況也不得而知
- 由於修改了xdpi,測試過程中並沒有什麼其他變化,但目前不清楚修改後會不會有其他的影響(檢視原始碼看到引用該值的地方並不多)
- 由於該操作屬於比較騷的操作,所以可能不會得到認可(哈哈)
所以上面的幾種方案需要自行選擇判斷