其實網上已經有不少人總結了Andorid 屏幕適配的知識. 這裏總結了適配的主流方案, 經過分析思考適配的本質, 再來思考各個適配方案的優劣. 弄清楚爲何有適配問題.android
這裏說的屏幕適配就是在Android衆多機型上,能有一個相對一致的顯示錶現.Android機型分辨率,尺寸,寬高比太多了,若是不作適配後果就是顯示效果差別比較大. 舉個栗子 xml佈局:git
<TextView
android:layout_width="300px"
android:layout_height="50px"
android:background="@color/colorAccent"
android:gravity="center_vertical"
android:text="文本"/>
複製代碼
左側是nexus4 右側是nexus5
屏幕都是等分十等分的, 便於咱們觀察差別. github
剛纔能夠看到是用px作單位的. 其實谷歌有默認的適配方案,就是採用dp作單位來適配. 咱們來看看使用dp作單位的狀況是什麼樣的. bash
從圖中能夠看到其實使用dp作適配,基本解決了問題, 只有一些小差別,若是公司要求不高,使用dp作適配其實也能夠的. 沒有問題, 甚至還有些優點 這個後面再分析. 如今主要分析爲何dp適配產生了一些差別, 谷歌這麼牛x的公司爲何設計的方案是這這個樣子 ,好像並無徹底解決問題, 還須要咱們繼續操心適配問題. 要了解這些,就先須要瞭解一下基本概念.相信各位大佬確定猜到我要說什麼了. 老生長談的幾個概念, 可是我仍是說下,便於以後的分析理解app
咱們看看nexus5 nexus5x 手機參數 ide
實際運行效果 以下圖: 這個顯示效果和上面表格計算出來的一致 工具
能夠看出使用dp作單位影響因素最大的就是dpi這個值了佈局
適配問題的產生核心本質就一句話:字體
dp適配是谷歌原始的適配方案, 上文也作了分析, 你們再來思考一下以前提出的問題 爲何谷歌這樣設計. 設個一個dpi的概念, 而不使用真實像素密度的? 其實dpi是爲了給手機廠商靈活配置顯示效果的. 有兩個手機一個是5寸 一個是6寸 , 若是直接採用真實像素密度那麼6寸手機就是5寸手機的徹底放大版本.不少時候咱們想着的是大屏手機能夠看到更多的內容. 而不是單純的等比例放大.
缺點: 在不一樣的手機上表現不一樣, 不符合公司設計圖標準.ui
咱們在佈局中寫的單位能夠引用dimens資源文件. 配置不一樣的分辨率而後手機運行的時候能夠根據不一樣分辨率去對應不一樣的單位. 參考連接: blog.csdn.net/lmj62356579… 優勢: 能夠很好的控制不一樣分辨率的顯示效果. 缺點: Andorid手機分辨率愈來愈多, 維護這套多分辨率的文件十分繁瑣. 更糟糕的是, 若是沒有對應的分辨率 不會去自動匹配類似的分辨率. 這種方案不作推薦
和方案一相似, 可是不是指定分辨率的,而是指定屏幕寬度的, 這樣一來文件就要少不少了 目標屏幕的最小寬度都大於480dp時,屏幕就會自動到帶sw480dp後綴的資源文件裏去尋找相關資源文件,這裏的最小寬度是指屏幕寬高的較小值,每一個屏幕都是固定的,不會隨着屏幕橫向縱向改變而改變。
參考使用連接: www.jianshu.com/p/759375113… 優勢: 能夠很好的控制不一樣分辨率的顯示效果. 缺乏對應寬度的引用資源文件 ,能夠默認去匹配相近的文件. 缺點: 仍是要去維護較多的文件. 略繁瑣.這個庫提供了PercentRelativeLayut percentFrameLayout 支持經常使用的屬性. 使用的時候設置百分比就能夠用了. 實現原理: 經過Layoutparams 獲取child設置的百分比. 經過計算獲取這百分比應該是多少. 測量出控件的大小. 缺點: 使用起來比較麻煩. 覺得設計圖標註的是px.每次設置百分比的時候須要去計算. 設置百分比仍是依賴父容器的.致使scrollView ListView 內的高度沒法使用百分比. 這種方案用的人比較少 不推薦使用.
使用px單位並非像素.內部通過處理.變成相應的百分比. 寬和高都是百分比. 寬的1px和高的1px不相同; github.com/hongyangAnd… 鴻洋大神的這套方案用的不少 . 雖然最後中止維護了, 優勢: 配置簡單, 使用起來能夠直接用px對着設計稿直接寫. 缺點: 在一些複雜佈局, 帶一些自定義控件的佈局時候容易出現適配問題. 並且在實際項目使用的時候 有偶發失效的狀況. 失效後, 界面因爲px作單位. 界面元素變的很小. 這方案能夠考慮使用. 畢竟在項目中運用普遍. 須要注意的是一些自定義佈局的處理, 在列表中itemview須要經過工具類額外處理一下.
其實以上幾種方案都不是很好, 在項目中有各自缺點. 目前最推薦的仍是頭條的方案; 參考連接: mp.weixin.qq.com/s/d9QCoBP6k…
工具類以下:
/**
* @param activity
* @param application
* @param isLandscape 是不是橫屏
*/
public class ScreenUtils {
/** 設計稿標準 */
private static final float width = 750f;
private static final float high = 1334f;
private static float textDensity = 0;
private static float textScaledDensity = 0;
/**
* 今日頭條的屏幕適配方案
* 根據當前設備物理尺寸和分辨率去動態計算density、densityDpi、scaledDensity
* 同時也解決了用戶修改系統字體的狀況
* @param activity
*/
public static void setCustomDensity(@NonNull Activity activity) {
setCustomDensity(activity, false);
}
/**
* @param activity
* @param isLandscape 是不是橫屏
*/
public static void setCustomDensity(@NonNull final Activity activity, boolean isLandscape) {
final Application application = activity.getApplication();
final DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
if (textDensity == 0) {
textDensity = displayMetrics.density;
textScaledDensity = displayMetrics.scaledDensity;
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration configuration) {
if (configuration != null && configuration.fontScale > 0) {
textScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
final float targetDensity;
if (isLandscape) {//橫屏
targetDensity = displayMetrics.widthPixels / (high / 2); //當前UI標準750*1334
} else {
targetDensity = displayMetrics.widthPixels / (width / 2); //當前UI標準750*1334
}
final float targetScaledDensity = targetDensity * (textScaledDensity / textDensity);
final int targetDpi = (int) (160 * targetDensity);
displayMetrics.density = targetDensity;
displayMetrics.scaledDensity = targetScaledDensity;
displayMetrics.densityDpi = targetDpi;
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDpi;
}
}
複製代碼
使用以下:
public class MainActivity extends AppCompatActivity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在設置佈局以前調用工具類方法
ScreenUtils.setCustomDensity(this);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
int densityDpi = getResources().getDisplayMetrics().densityDpi;
tv.setText("邏輯像素密度 dpi " + densityDpi);
}
}
複製代碼
看看實際運行效果: 實際效果中能夠看到很好的完成了適配
以前咱們分析了dp不太適配的緣由. 因此今日頭條方案簡答暴力. 既然rom中設置的dpi不是實際像素密度. 那麼我直接把dpi給你改爲實際像素密度. 果真夠簡單暴力的... 工具類比較簡單,複製就能夠用. 就不上傳github了.
實際開發中咱們直接使用今日頭條方案就可好了, 能夠知足需求.
彷佛咱們已經找到了完美的適配方案了. 可是? 可是了. 可是這個所謂的適配的真的就是谷歌的個各個手機廠商想看到的麼? 廠商定義dpi沒有按照實際的來定義 而是作了調整, 調整以後的好處就是就是但願在大屏手機上能夠看更多的內容. 在圖三中咱們發現nexus-5x 比nexus-5 屏幕大一些 dpi值廠商設置的也diy低一些, 更具公式px=dpi/160*dp 得知這樣px就變小, 內容佔的地方就小, 能夠容納更多內容. 這就是爲了讓大屏手機看到更多的內容. 爲了驗證這個猜測咱們再看看更大手機的顯示狀況
圖六中的表格看到越是大屏手機, 廠商對dpi的標定就比實際越偏小. 這樣就可讓屏幕顯示更多的內容.特別是看電子書之類的內容時候體驗更明顯, 大屏手機一頁能夠顯示更多的內容. 大屏幕的優點就凸顯出來了.
可是咱們開發的普通應用爲了在大屏小屏上一致的體驗. 就不得不去修改dpi爲實際值了. 以上是個人對Android中適配的一些思考和總結, 但願對你們有幫助~