Android螢幕適配方案
阿新 • • 發佈:2018-12-09
目錄介紹
- 1.螢幕適配定義
- 2.相關重要的概念
- 2.1 螢幕尺寸[物理尺寸]
- 2.2 螢幕解析度[px]
- 2.3 螢幕畫素密度[dpi]
- 2.4 dp、dip、dpi、sp、px
- 2.5 mdpi、hdpi、xdpi、xxdpi
- 2.6 獲取螢幕解析度[寬高]
- 3.Android螢幕適配出現的原因
- 3.1 什麼是畫素點
- 3.2 dp與百分比
- 4.Android適配問題及本質
- 4.1 尺寸適配
- 4.2 程式碼適配
- 4.3 佈局適配
- 4.4 權重適配
- 4.5 圖片適配
- 4.6 百分比適配
- 5.存在問題和困境
- 5.1 萬用字元適配困境
- 5.2 傳統dp適配困境
- 6.常用解決方案
- 6.1 今日頭條適配方案
- 6.2 鴻洋大神庫
- 6.3 AndroidAutoSize
1.螢幕適配定義
- 使得某一元素在Android不同尺寸、不同解析度的手機上具備相同的顯示效果
2.相關重要的概念
2.1 螢幕尺寸[物理尺寸]
- 含義:手機對角線的物理尺寸
- 單位:英寸(inch),1英寸=2.54cm
- Android手機常見的尺寸有5寸、5.5寸、6寸等等
2.2 螢幕解析度[px]
- 含義:手機在橫向、縱向上的畫素點數總和
- 一般描述成螢幕的”寬x高”=AxB
- 含義:螢幕在橫向方向(寬度)上有A個畫素點,在縱向方向 (高)有B個畫素點
- 例子:1080x1920,即寬度方向上有1080個畫素點,在高度方向上有1920個畫素點
- 單位:px(pixel),1px=1畫素點
- Android手機常見的解析度:720x1280、1080x1920等等
2.3 螢幕畫素密度[dpi]
- 含義:每英寸的畫素點數
- 單位:dpi(dots per ich)
- 假設裝置內每英寸有160個畫素,那麼該裝置的螢幕畫素密度=160dpi
- 螢幕畫素密度與螢幕尺寸和螢幕解析度有關,在單一變化條件下,螢幕尺寸越小、解析度越高,畫素密度越大,反之越小。
- dpi計算公式:舉個例子:螢幕解析度為:1920*1080,螢幕尺寸為5吋的話,那麼dpi為440
- 注意:dpi是根據螢幕真實的解析度和尺寸來計算的,每個裝置都可能不一樣的
2.4 dp[dip]、dpi、sp、px
- dp(dip):px = dp * 密度比,都是Density Independent Pixels的縮寫,即密度無關畫素
- dpi:開方(寬度平方 + 高度平方) / 手機的尺寸;dpi是螢幕畫素密度,假如一英寸裡面有160個畫素,這個螢幕的畫素密度就是160dpi
- sp: 可以根據文字大小首選項進行放縮,是設定字型大小的御用單位。
- px: 畫素,px是比較熟悉,前面的解析度就是用的畫素為單位,大多數情況下,比如UI設計、Android原生API都會以px作為統一的計量單位,畫素是獲取螢幕寬高等。
- 問題: dp和px如何換算呢?
px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
2.5 mdpi、hdpi、xdpi、xxdpi
- 2.5.1 作用: mdpi、hdpi、xdpi、xxdpi用來修飾Android中的drawable資料夾及values資料夾,用來區分不同畫素密度下的圖片和dimen值。
名稱像 素密度範圍
ldpi 0dpi~120dpi
mdpi 120dpi~160dpi
hdpi 120dpi~160dpi
xdpi 160dpi~240dpi
xxdpi 240dpi~320dpi
xxxdpi 480dpi~640dpi
Android專案後應該可以看到很多drawable資料夾,分別對應不同的dpi
drawable-ldpi (dpi=120, density=0.75)
drawable-mdpi (dpi=160, density=1)
drawable-hdpi (dpi=240, density=1.5)
drawable-xhdpi (dpi=320, density=2)
drawable-xxhdpi (dpi=480, density=3)
對於五種主流的畫素密度(MDPI、HDPI、XHDPI、XXHDPI 和 XXXHDPI)應按照 2:3:4:6:8 的比例進行縮放。
- 2.5.2 在進行開發的時候,我們需要把合適大小的圖片放在合適的資料夾裡面。下面以圖示設計為例進行介紹
- 2.5.3 在設計圖示時,對於五種主流的畫素密度(MDPI、HDPI、XHDPI、XXHDPI 和 XXXHDPI)應按照 2:3:4:6:8 的比例進行縮放。
- 例如,一個啟動圖示的尺寸為48x48 dp,這表示在 MDPI 的螢幕上其實際尺寸應為 48x48 px,在 HDPI 的螢幕上其實際大小是 MDPI 的 1.5 倍 (72x72 px),在 XDPI 的螢幕上其實際大小是 MDPI 的 2 倍 (96x96 px),依此類推。
- 2.5.4 下圖為圖示的各個螢幕密度的對應尺寸:
螢幕密度 圖示尺寸
mdpi 48X48px
hdpi 72X72px
xdpi 96X96px
xxdpi 144X144px
xxxdpi 192X192px
2.6 DisplayMetrics解析
- DisplayMetrics解析
- 獲取螢幕解析度資訊的三種方法:
//第一種
DisplayMetrics metrics = new DisplayMetrics();
Display display = activity.getWindowManager().getDefaultDisplay();
display.getMetrics(metrics);
//第二種
DisplayMetrics metrics= activity.getResources().getDisplayMetrics();
//第三種
Resources.getSystem().getDisplayMetrics();
3.Android螢幕適配出現的原因
3.1 什麼是畫素點
- 螢幕解析度是指在橫縱向上的畫素點數,單位是px,1px=1個畫素點。
- 一般以縱向畫素*橫向畫素,如1960*1080。 由於Android系統的開放性,任何使用者、開發者、OEM廠商、運營商都可以對Android進行定製,修改成他們想要的樣子。 螢幕尺寸這麼多,為了讓我們開發的程式能夠比較美觀的顯示在不同尺寸、解析度、畫素密度(這些概念我會在下面詳細講解)的裝置上,那就要在開發的過程中進行處理,至於如何去進行處理,這就是我們今天的主題。
3.2 dp與百分比 (網頁前端提供百分比,所以無需適配)
- 只要記住一點dp是與畫素無關的,在實際使用中1dp大約等於1/160inch
那麼dp究竟解決了適配上的什麼問題?可以看出1dp = 1/160inch;那麼它至少能解決一個問題,就是你在佈局檔案寫某個View的寬和高為160dp*160dp,這個View在任何解析度的螢幕中,顯示的尺寸大小是大約是一致的(可能不精確),大概是 1 inch * 1 inch。
- 1.呈現效果仍舊會有差異,僅僅是相近而已
- 當裝置的物理尺寸存在差異的時候,dp就顯得無能為力了。為4.3寸螢幕準備的UI,執行在5.0寸的螢幕上,很可能在右側和下側存在大量的空白。而5.0寸的UI執行到4.3寸的裝置上,很可能顯示不下。
一句話,總結下,dp能夠讓同一數值在不同的解析度展示出大致相同的尺寸大小。但是當裝置的尺寸差異較大的時候,就無能為力了。
4.Android螢幕適配常見方法
4.1 適配常見方法
- 尺寸適配
- dimen適配
- 佈局適配
- 程式碼適配
- 圖片適配
4.2 尺寸適配
- 4.2.1 佈局檔案設定寬高
- 寬高設定引數:有的時候用dp,有的時候用px,大多數用dp
- dp:dp(dip):px = dp * 密度比,與螢幕畫素有對應關係,設定成dp後,在不同解析度的手機上有可能尺寸會不一樣 px:畫素,比如小解析度手機上一畫素和大解析度手機上一畫素,所顯示的影象是不一樣的 理解dp和px之間對應的關係,不同解析度的手機用不同的dp值來適配
- 4.2.2 密度比
- 密度比是固定的,可以查詢文件Develop—>API Guides—>Best Practices—>Supporting Multiple mdpi手機:160dpi 是基準線,1px = 1dp * 1,其他手機的密度比 = 自己的dpi/160
- 程式碼獲取密度比:getResources().getDisplayMetrics().density
ldip:120px = 160dp * 0.75 mdpi:160px = 160dp * 1 hdpi:240px = 160dp * 1.5 xhdpi:360px = 180dp * 2
- 4.2.3 dimen適配
1.在預設的values中的dimens檔案下宣告(類似於Strings.xml)
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="harlWidth">160dp</dimen>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- values-hdpi 480X800 -->
<dimen name="imagewidth">120dip</dimen>
</resources>
<resources>
<!-- values-hdpi-1280x800 -->
<dimen name="imagewidth">220dip</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- values-hdpi 480X320 -->
<dimen name="imagewidth">80dip</dimen>
</resources>
2.在佈局檔案中引用
<TextView
android:layout_width="@dimen/harlWidth"
android:layout_height="wrap_content"
android:background="#0ff"
android:text="@string/hello_world" />
3.新建需要適配的values-XXX(比如values-1280x720,注意規範大值在前)
4.在新建values-1280x720中的dimens.xml檔案中
* <dimen name="harlWidth">180dp</dimen>
5.所有手機適配找對應的預設的dimens
* 思考:如何計算dpi?如何計算手機密度比?能夠用dp適配所有手機嗎?
* dp不能適配所有手機;
* 舉個例子:按鈕佔螢幕寬度一半,把寬度設定成160dp,120px和160px和240px可以佔螢幕一半,但是360px則小於螢幕一半;
* 如果把寬度設定成180dp,那麼360dp可以佔螢幕一半,但其他幾個又不行。
* 如果要適配所有手機的控制元件寬度為螢幕寬度的一半,該怎麼做呢?用dimen
4.2 程式碼適配
4.3 佈局適配,有可能在不同的手機佈局中,控制元件排列的位置不一樣
- 1.位置不一樣
- 不同的手機在執行的時候選擇不同的佈局(佈局名稱一樣,類似於dimens),比如:
- 2.控制元件不一樣
- 不能用佈局適配了;為什麼?
- 佈局能夠實現介面效果,但是完成佈局後在程式碼中,由於控制元件都不一樣,所以會找這兩套佈局的id,還要做判斷,根據不同的佈局做兩套程式碼(如果頁面複雜,給控制元件設定引數等十分繁瑣)
- 3.適用場景
- 不同的手機的控制元件的位置不一樣,發生了位置變化才會用到佈局適配,實際開發中用的很少
4.4 權重適配
4.5 圖片適配
- 1.圖片的查詢順序
- 注意:一般手機 ldpi
5.存在問題和困境
5.1 萬用字元適配困境
5.2 傳統dp適配困境
- [摘自頭條]一般我們設計圖都是以固定的尺寸來設計的。比如以解析度750px * 1334px來設計,以density為3來標註,也就是螢幕其實是350dp * 667dp。如果想在所有裝置上顯示完全一致,其實是不現實的,因為螢幕高寬比不是固定的,各種寬高比層出不窮,寬高比不同,顯示完全一致就不可能了。但是通常下,我們只需要以寬或高一個維度去適配,比如我們Feed是上下滑動的,只需要保證在所有裝置中寬的維度上顯示一致即可,再比如一個不支援上下滑動的頁面,那麼需要保證在高這個維度上都顯示一致,尤其不能存在某些裝置上顯示不全的情況。同時考慮到現在基本都是以dp為單位去做的適配,如果新的方案不支援dp,那麼遷移成本也非常高。
- 因此,總結下大致需求如下:
- 支援以寬或者高一個維度去適配,保持該維度上和設計圖一致;注意是某一個維度
- 支援dp和sp單位,控制遷移成本到最小。
6.常用適配框架
6.1 今日頭條適配方案
- 6.1.1 相容突破口
- 從dp和px的轉換公式 :px = dp * density
- 可以看出,如果設計圖寬為360dp,想要保證在所有裝置計算得出的px值都正好是螢幕寬度的話,我們只能修改 density 的值。
//在xml中使用何種尺寸單位(dp、sp、pt、in、mm),最後在繪製時都會給我們轉成px!
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;
}
- 6.1.2 頭條適配方案核心程式碼
public static void setCustomDensity(Activity activity, Application application) {
DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
if (sNoncompatDensity == 0) {
// 系統的Density
sNoncompatDensity = displayMetrics.density;
// 系統的ScaledDensity
sNoncompatScaledDensity = displayMetrics.scaledDensity;
// 監聽在系統設定中切換字型
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig != null && newConfig.fontScale > 0) {
sNoncompatScaledDensity=application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
// 公司UI尺寸是750px-1334px,此處以375dp的設計圖作為例子
float targetDensity=displayMetrics.widthPixels/375;
float targetScaledDensity=targetDensity*(sNoncompatScaledDensity/sNoncompatDensity);
int targetDensityDpi= (int) (160 * targetDensity);
displayMetrics.density = targetDensity;
displayMetrics.scaledDensity = targetScaledDensity;
displayMetrics.densityDpi = targetDensityDpi;
DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
- 6.1.3 頭條適配方案注意事項
- 寬度適配就已經完成啦,只需要在Activity中呼叫就行了,必須在setContentView()之前!
- 如果需要適配高度,頭條指出只要按照同樣的方法做高度適配就可以了!
- 實現思路:假設設計圖寬度是360dp,以寬維度來適配,那麼適配後的 density = 裝置真實寬(單位px) / 360,接下來只需要把我們計算好的 density 在系統中修改下即可
遇到的問題:
- 1.如果某個頁面不想適配該方案,該如何處理
- 2.滾動頁面以寬為維度適配,而某些頁面則是以高為維度適配,這種情況怎麼辦?
- 3.針對第三方庫有何更好的方案,比如支付寶支付彈窗,或者第三方客服聊天頁面如何處理適配
6.1.4 頭條適配工具類,暫時只是用作測試專案
public class ScreenDensityUtils {
/*
* 1.先在application中使用setup()方法初始化一下
* 2.手動在Activity中呼叫match()方法做適配,必須在setContentView()之前
* 3.建議使用dp做寬度適配,大多數時候寬度適配才是主流需要
* 4.個人覺得在寫佈局的時候,可以多用dp,如果是使用px,建議轉化成dp
* 5.入侵性很低,不需要改動原來的程式碼
*/
/**
* 螢幕適配的基準
*/
private static final int MATCH_BASE_WIDTH = 0;
private static final int MATCH_BASE_HEIGHT = 1;
/**
* 適配單位
*/
private static final int MATCH_UNIT_DP = 0;
private static final int MATCH_UNIT_PT = 1;
// 適配資訊
private static MatchInfo sMatchInfo;
// Activity 的生命週期監測
private static Application.ActivityLifecycleCallbacks mActivityLifecycleCallback;
private ScreenDensityUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 初始化
* @param application 需要在application中初始化
*/
public static void setup(@NonNull final Application application) {
/*
//獲取螢幕解析度資訊的三種方法
//第一種
DisplayMetrics metrics = new DisplayMetrics();
Display display = activity.getWindowManager().getDefaultDisplay();
display.getMetrics(metrics);
//第二種
DisplayMetrics metrics= activity.getResources().getDisplayMetrics();
//第三種
Resources.getSystem().getDisplayMetrics();
*/
//注意這個是獲取系統的displayMetrics
final DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
if (sMatchInfo == null) {
// 記錄系統的原始值
sMatchInfo = new MatchInfo();
sMatchInfo.setScreenWidth(displayMetrics.widthPixels);
sMatchInfo.setScreenHeight(displayMetrics.heightPixels);
sMatchInfo.setAppDensity(displayMetrics.density);
sMatchInfo.setAppDensityDpi(displayMetrics.densityDpi);
sMatchInfo.setAppScaledDensity(displayMetrics.scaledDensity);
sMatchInfo.setAppXdpi(displayMetrics.xdpi);
}
// 新增字型變化的監聽
// 呼叫 Application#registerComponentCallbacks 註冊下 onConfigurationChanged 監聽即可。
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
// 字型改變後,將 appScaledDensity 重新賦值
if (newConfig != null && newConfig.fontScale > 0) {
float scaledDensity = displayMetrics.scaledDensity;
sMatchInfo.setAppScaledDensity(scaledDensity);
}
}
@Override
public void onLowMemory() {
}
});
}
/**
* 在 application 中全域性啟用適配(也可單獨使用 match() 方法在指定頁面中配置適配)
*/
@RequiresApi(api = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static void register(@NonNull final Application application, final float designSize, final int matchBase, final int matchUnit) {
if (mActivityLifecycleCallback == null) {
mActivityLifecycleCallback = new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity != null) {
match(activity, designSize, matchBase, matchUnit);
}
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
};
application.registerActivityLifecycleCallbacks(mActivityLifecycleCallback);
}
}
/**
* 全域性取消所有的適配
*/
@RequiresApi(api = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static void unregister(@NonNull final Application application, @NonNull int... matchUnit) {
if (mActivityLifecycleCallback != null) {
application.unregisterActivityLifecycleCallbacks(mActivityLifecycleCallback);
mActivityLifecycleCallback = null;
}
for (int unit : matchUnit) {
cancelMatch(application, unit);
}
}
/**
* 適配螢幕(放在 Activity 的 setContentView() 之前執行)
*
* @param context 上下文
* @param designSize 設計圖的尺寸
*/
public static void match(@NonNull final Context context, final float designSize) {
match(context, designSize, MATCH_BASE_WIDTH, MATCH_UNIT_DP);
}
/**
* 適配螢幕(放在 Activity 的 setContentView() 之前執行)
*
* @param context 上下文
* @param designSize 設計圖的尺寸
* @param matchBase 適配基準
*/
public static void match(@NonNull final Context context, final float designSize, int matchBase) {
match(context, designSize, matchBase, MATCH_UNIT_DP);
}
/**
* 適配螢幕(放在 Activity 的 setContentView() 之前執行)
*
* @param context 上下文
* @param designSize 設計圖的尺寸
* @param matchBase 適配基準
* @param matchUnit 使用的適配單位
*/
private static void match(@NonNull final Context context, final float designSize, int matchBase, int matchUnit) {
if (designSize == 0) {
throw new UnsupportedOperationException("The designSize cannot be equal to 0");
}
if (matchUnit == MATCH_UNIT_DP) {
matchByDP(context, designSize, matchBase);
} else if (matchUnit == MATCH_UNIT_PT) {
matchByPT(context, designSize, matchBase);
}
}
/**
* 重置適配資訊,取消適配
*/
public static void cancelMatch(@NonNull final Context context) {
cancelMatch(context, MATCH_UNIT_DP);
cancelMatch(context, MATCH_UNIT_PT);
}
/**
* 重置適配資訊,取消適配
*
* @param context 上下文
* @param matchUnit 需要取消適配的單位
*/
private static void cancelMatch(@NonNull final Context context, int matchUnit) {
if (sMatchInfo != null) {
final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
if (matchUnit == MATCH_UNIT_DP) {
if (displayMetrics.density != sMatchInfo.getAppDensity()) {
displayMetrics.density = sMatchInfo.getAppDensity();
}
if (displayMetrics.densityDpi != sMatchInfo.getAppDensityDpi()) {
displayMetrics.densityDpi = (int) sMatchInfo.getAppDensityDpi();
}
if (displayMetrics.scaledDensity != sMatchInfo.getAppScaledDensity()) {
displayMetrics.scaledDensity = sMatchInfo.getAppScaledDensity();
}
} else if (matchUnit == MATCH_UNIT_PT) {
if (displayMetrics.xdpi != sMatchInfo.getAppXdpi()) {
displayMetrics.xdpi = sMatchInfo.getAppXdpi();
}
}
}
}
public static MatchInfo getMatchInfo() {
return sMatchInfo;
}
/**
* 使用 dp 作為適配單位(適合在新專案中使用,在老專案中使用會對原來既有的 dp 值產生影響)
* <br>
* <ul>
* dp 與 px 之間的換算:
* <li> px = density * dp </li>
* <li> density = dpi / 160 </li>
* <li> px = dp * (dpi / 160) </li>
* </ul>
*
* @param context 上下文
* @param designSize 設計圖的寬/高(單位: dp)
* @param base 適配基準
*/
private static void matchByDP(@NonNull final Context context, final float designSize, int base) {
final float targetDensity;
if (base == MATCH_BASE_WIDTH) {
targetDensity = sMatchInfo.getScreenWidth() * 1f / designSize;
} else if (base == MATCH_BASE_HEIGHT) {
targetDensity = sMatchInfo.getScreenHeight() * 1f / designSize;
} else {
targetDensity = sMatchInfo.getScreenWidth() * 1f / designSize;
}
final int targetDensityDpi = (int) (targetDensity * 160);
final float targetScaledDensity = targetDensity * (sMatchInfo.getAppScaledDensity() / sMatchInfo.getAppDensity());
final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
displayMetrics.density = targetDensity;
displayMetrics.densityDpi = targetDensityDpi;
displayMetrics.scaledDensity = targetScaledDensity;
}
/**
* 使用 pt 作為適配單位(因為 pt 比較冷門,新老專案皆適合使用;也可作為 dp 適配的補充,
* 在需要同時適配寬度和高度時,使用 pt 來適配 dp 未適配的寬度或高度)
* <br/>
* <p> pt 轉 px 演算法: pt * metrics.xdpi * (1.0f/72) </p>
*
* @param context 上下文
* @param designSize 設計圖的寬/高(單位: pt)
* @param base 適配基準
*/
private static void matchByPT(@NonNull final Context context, final float designSize, int base) {
final float targetXdpi;
if (base == MATCH_BASE_WIDTH) {
targetXdpi = sMatchInfo.getScreenWidth() * 72f / designSize;
} else if (base == MATCH_BASE_HEIGHT) {
targetXdpi = sMatchInfo.getScreenHeight() * 72f / designSize;
} else {
targetXdpi = sMatchInfo.getScreenWidth() * 72f / designSize;
}
final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
displayMetrics.xdpi = targetXdpi;
}
/**
* 適配資訊
*/
private static class MatchInfo {
private int screenWidth;
private int screenHeight;
private float appDensity;
private float appDensityDpi;
private float appScaledDensity;
private float appXdpi;
int getScreenWidth() {
return screenWidth;
}
void setScreenWidth(int screenWidth) {
this.screenWidth = screenWidth;
}
int getScreenHeight() {
return screenHeight;
}
void setScreenHeight(int screenHeight) {
this.screenHeight = screenHeight;
}
float getAppDensity() {
return appDensity;
}
void setAppDensity(float appDensity) {
this.appDensity = appDensity;
}
float getAppDensityDpi() {
return appDensityDpi;
}
void setAppDensityDpi(float appDensityDpi) {
this.appDensityDpi = appDensityDpi;
}
float getAppScaledDensity() {
return appScaledDensity;
}
void setAppScaledDensity(float appScaledDensity) {
this.appScaledDensity = appScaledDensity;
}
float getAppXdpi() {
return appXdpi;
}
void setAppXdpi(float appXdpi) {
this.appXdpi = appXdpi;
}
}
}
6.2 鴻洋大AutoLayout框架
- 我記得上上一個公司的專案投資界就是用的這個螢幕適配庫……哈哈
- 該庫的想法非常好:對照設計圖,使用px編寫佈局,不影響預覽;繪製階段將對應設計圖的px數值計算轉換為當前螢幕下適配的大小;為簡化接入,inflate時自動將各Layout轉換為對應的AutoLayout,從而不需要在所有的xml中更改。但是同時該庫也存在以下等問題:
- 擴充套件性較差。對於每一種ViewGroup都要對應編寫對應的AutoLayout進行擴充套件,對於各View的每個需要適配的屬性都要編寫程式碼進行適配擴充套件;
- 在onMeasure階段進行數值計算。消耗效能,並且這對於非LayoutParams中的屬性存在較多不合理之處。比如在onMeasure時對TextView的textSize進行換算並setTextSize,那麼玩家在程式碼中動態設定的textSize都會失效,因為在每次onMesasure時都會重新被AutoLayout重新設定覆蓋。
- issue較多並且作者已不再維護。
- 個人覺得AutoLayout的設計思想非常優秀,但是將LayoutParams與屬性作為切入口在mesure過程中進行轉換計算的方案存在效率與擴充套件性等方面的問題。那麼Android計算長度的收口在哪裡,能不能在Android計算長度時進行換算呢?如果能在Android計算長度時進行換算,那麼就不需要一系列多餘的計算以及適配,一切問題就都迎刃而解了
6.3 AndroidAutoSize
- 已經用於現在正式庫,程式碼量多,且註釋也比較專案,作者更新很頻繁,極力維護並解決bug,非常不錯!