android 配置aspect_Android APP全面屏适配技术要点
全面屏的概念
為什么先要解釋一下全面屏,因為這個詞在現(xiàn)在來講就是一個偽命題。全面屏字面意思就是手機(jī)的正面全部都是屏幕,100%的屏占比。但是現(xiàn)在推出所謂“全面屏”手機(jī)的廠商沒有一個能達(dá)到全面的。
那么下面來說一下Android開發(fā)領(lǐng)域?qū)θ嫫恋睦斫夂投x吧。
一般手機(jī)的屏幕縱橫比為16:9,如1080x1920、1440x2560等,其比值為1.77,在全面屏手機(jī)出現(xiàn)之前,Android中默認(rèn)的最大屏幕縱橫比(maximum aspect ratio)為1.86,即能夠兼容16:9的屏幕。
一些手機(jī)廠商為了追求更大的屏幕空間以及更極致的用戶體驗,于是提高了屏幕縱橫比,17:9、19:10、18:9、18.5:9的手機(jī)開始進(jìn)入市場,這些手機(jī)的屏幕縱橫比大大超過了1.86,這些手機(jī)被稱為全面屏手機(jī)。
為何需要適配
我們將targetSdkVersion的值改為小于等于23,運行程序,我們會發(fā)現(xiàn)屏幕底部出現(xiàn)一個黑條。
image
如何適配
targetSdkVersion<=23,更大的屏幕縱橫比
在Galaxy S8發(fā)布之后,Android官方提供了適配方案,即提高App所支持的最大屏幕縱橫比,實現(xiàn)很簡單,在AndroidManifest.xml中可做如下配置:
android:value="ratio_float"/>
其中ratio_float為浮點數(shù),官方建議為2.1或更大,因為18.5:9=2.055555555……,如果日后出現(xiàn)縱橫比更大的手機(jī),此值將會更大。
android:value="2.1" />
max_aspect值也可以在Java代碼中動態(tài)地設(shè)置,通過下面的方法即可實現(xiàn):
public void setMaxAspect() {
ApplicationInfo applicationInfo = null;
try {
applicationInfo = getPackageManager().getApplicationInfo(getPackageName(),
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
if(applicationInfo == null){
throw new IllegalArgumentException(" get application info = null ");
}
applicationInfo.metaData.putString("android.max_aspect", "2.1");
}
如果targetSdkVersion的值的值大于23,那么應(yīng)該不用設(shè)置max_aspect即可。
查看適配之后的截圖:
image
圖片資源適配
我們看一下啟動頁,在16:9屏幕中適配的圖片,到了18:9的屏幕中就會被拉伸了。
16:9屏幕中顯示
18:9屏幕中顯示
image
image
解決這個問題無非就是兩種方法,換圖片或者是換布局
換圖片
不能依賴單一廠商的解決方案,只能從Android系統(tǒng)屬性出發(fā)。考慮到目前大部分全面屏手機(jī)只是在高度上拉長,且大多為6.0英寸左右,像素密度對比xxhdpi并沒有多大區(qū)別,那我們可以在項目中增加一組資源drawable-xxhdpi-2160x1080 、drawable-long 這樣解決圖片的拉伸問題,當(dāng)然這樣的方法肯定是不太好的,會增加app的容量。這里就不演示了。
優(yōu)化布局
當(dāng)然最好的方法還是用相對布局采用XML的方式,或者.9圖的解決方案。
我總結(jié)的就是少量多切,盡量減少尺寸對布局的影響。比如這里,使用正方形的切圖,讓他居中顯示,無論屏幕縱橫比如何,都不會拉伸這個圖片,拉伸的只是背景而已。
image
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/bz002"/>
適配前
適配后
image
image
全面屏高度問題適配
首先解釋一下window,decorview,rootview這幾個概念
image
Window官方文檔:Window
public abstract class Window. Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.
The only existing implementation of this abstract class is android.view.PhoneWindow, which you should instantiate when needing a Window.
翻譯一下:每一個 Activity 都持有一個 Window 對象,但是 Window 是一個抽象類,這里 Android 為 Window 提供了唯一的實現(xiàn)類 PhoneWindow。也就是說 Activity 中的 window 實例就是一個 PhoneWindow 對象。
但是 PhoneWindow 終究是 Window,它并不具備多少 View 相關(guān)的能力。不過 PhoneWindow 中持有一個 Android 中非常重要的一個 View 對象 DecorView.
現(xiàn)在的關(guān)系就很明確了,每一個 Activity 持有一個 PhoneWindow 的對象,而一個 PhoneWindow 對象持有一個 DecorView 的實例,所以 Activity 中 View 相關(guān)的操作其實大都是通過 DecorView 來完成。
DecorView就可以理解為手機(jī)的內(nèi)屏,就是那塊玻璃,可以發(fā)光的屏幕。
這里通過代碼,打印出我們頁面中的高度的各項數(shù)據(jù)
int decorviewHeight = decorView.getHeight();
int screenHeight = FullScreenManager.getScreenHeight();
int nativeBarHeight = FullScreenManager.getNativeBarHeight();
int contentViewHeight = rootView.getHeight();
int navigationBarHeight1 = FullScreenManager.getNavigationBarHeight();
Log.d("shijiacheng","=======================================");
Log.d("shijiacheng","DecorView height: " + decorviewHeight + " px");
Log.d("shijiacheng","Screen height: " + screenHeight + " px");
Log.d("shijiacheng","NativeBar height: " + nativeBarHeight + " px");
Log.d("shijiacheng","ContentView height: " + contentViewHeight + " px");
Log.d("shijiacheng","NavigationBar height: " + navigationBarHeight + " px");
Log.d("shijiacheng","---------------------------------------");
獲取decorView的高度
final View decorView = getWindow().getDecorView();
int decorviewHeight = decorView.getHeight();
獲得屏幕高度
/**
* 獲得屏幕高度
* @return
*/
public static int getScreenHeight() {
Resources resource = AppContext.getInstance().getResources();
DisplayMetrics displayMetrics = resource.getDisplayMetrics();
return displayMetrics.heightPixels;
}
獲取狀態(tài)欄的高度
/**
* 獲取狀態(tài)欄的高度
*
* @return
*/
public static int getNativeBarHeight() {
Resources resource = AppContext.getInstance().getResources();
int result = 0;
int resourceId = resource.getIdentifier("status_bar_height",
"dimen", "android");
if (resourceId > 0) {
result = resource.getDimensionPixelSize(resourceId);
}
return result;
}
獲取contentView的高度
LinearLayout contentView = findViewById(R.id.root);
int contentViewHeight = contentView.getHeight();
獲取NavigationBar的高度
public static int getNavigationBarHeight() {
Resources resources = AppContext.getInstance().getResources();
int resourceId = resources.getIdentifier("navigation_bar_height","dimen", "android");
int height = resources.getDimensionPixelSize(resourceId);
return height;
}
為了更加直觀的展示各個數(shù)據(jù),這里我們使用布局的方式將各個數(shù)據(jù)展示出來,布局代碼比較簡單,這里就不展示了。
image
先展示一下正常的屏幕高度的各項數(shù)據(jù)
10-08 09:52:03.636 23818-23818/? D/shijiacheng: =========================
10-08 09:52:03.637 23818-23818/? D/shijiacheng: DecorView height: 1280 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: Screen height: 1280 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: NativeBar height: 50 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: ContentView height: 1230 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: NavigationBar height: 96 px
10-08 09:52:03.637 23818-23818/? D/shijiacheng: -------------------------
image
DecorView = Screen height = NativeBar height + ContentView height
看一下小米mix全面屏的情況
2018-10-08 09:54:15.640 /? D/shijiacheng: =========================
2018-10-08 09:54:15.640 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 09:54:15.641 /? D/shijiacheng: RootView height: 2094 px
2018-10-08 09:54:15.641 /? D/shijiacheng: Screen height: 2030 px
2018-10-08 09:54:15.641 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 09:54:15.641 /? D/shijiacheng: ContentView height: 2094 px
2018-10-08 09:54:15.641 /? D/shijiacheng: NavigationBar height: 130 px
2018-10-08 09:54:15.641 /? D/shijiacheng: -------------------------
image
問題出現(xiàn)了,可以發(fā)現(xiàn)contentView的高度比screen屏幕的高度還要大,不禁要懷疑,我們的獲取屏幕高度的方法在全面屏下計算錯誤了。
問題1:獲取屏幕高度方法計算不準(zhǔn)確
我們一直都是使用如下方法進(jìn)行屏幕高度測量的:
public static int getScreenHeight() {
Resources resource = AppContext.getInstance().getResources();
DisplayMetrics displayMetrics = resource.getDisplayMetrics();
return displayMetrics.heightPixels;
}
但是這個方法卻是一個十分古老的方法,沒有與時俱進(jìn),雖然說在普通屏幕上這種方法沒有問題,但是在全面屏手機(jī)上來說,這種方法就不靈了。
下面我們就來研究一下獲取屏幕尺寸的方法的演進(jìn)。
獲取屏幕寬高
獲取屏幕的寬高是我們開發(fā)中經(jīng)常遇到的問題,而且相信大家都已經(jīng)非常熟悉,最常用的為以下兩種:
public static int getScreenHeight1(Activity activity) {
return activity.getWindowManager().getDefaultDisplay().getHeight();
}
public static int getScreenHeight2(Activity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.heightPixels;
}
其實以上兩種方式是一樣的,只不過第二種是把信息封裝到 DesplayMetrics中,再從DesplayMetrics得到數(shù)據(jù)。
在 Android 3.2(Api 13) 之后又提供了如下的一個方法,將數(shù)據(jù)封裝到Point中,然后返回寬度高度信息。
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public static int getScreenHeight3(Activity activity) {
Point point = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(point);
return point.y;
}
在 Android 4.2(Api17) 之后提供了如下方法,與第三種類似也是將數(shù)據(jù)封裝到Point中,然后返回款高度信息。
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static int getScreenHeight4(Activity activity) {
Point realSize = new Point();
activity.getWindowManager().getDefaultDisplay().getRealSize(realSize);
return realSize.y;
}
其實getRealSize這個方法在Android Api15的時候就已經(jīng)加入了,不過是被隱藏了,通過查閱源碼我們可以看到。
image
Android Api15 Display.java源碼中g(shù)etRealSize()方法被標(biāo)記為@hide
image
因此,我們可以重寫獲取高度的方法,適配所有機(jī)型,所有系統(tǒng)。
適配所有屏幕的獲取屏幕尺寸的方法
public static int[] getScreenSize(Context context) {
int[] size = new int[2];
WindowManager w = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display d = w.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
d.getMetrics(metrics);
// since SDK_INT = 1;
int widthPixels = metrics.widthPixels;
int heightPixels = metrics.heightPixels;
// includes window decorations (statusbar bar/menu bar)
if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17)
try {
widthPixels = (Integer) Display.class.getMethod("getRawWidth").invoke(d);
heightPixels = (Integer) Display.class.getMethod("getRawHeight").invoke(d);
} catch (Exception ignored) {
}
// includes window decorations (statusbar bar/menu bar)
if (Build.VERSION.SDK_INT >= 17)
try {
Point realSize = new Point();
Display.class.getMethod("getRealSize", Point.class).invoke(d, realSize);
widthPixels = realSize.x;
heightPixels = realSize.y;
} catch (Exception ignored) {
}
size[0] = widthPixels;
size[1] = heightPixels;
return size;
}
使用新的獲取高度的方法,重新運行程序,運行結(jié)果已經(jīng)正常顯示了。
2018-10-08 13:19:32.389 /? D/shijiacheng: ==========================
2018-10-08 13:19:32.390 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: Screen height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 13:19:32.390 /? D/shijiacheng: ContentView height: 2094 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NavigationBar height: 130 px
2018-10-08 13:19:32.390 /? D/shijiacheng: --------------------------
image
問題2:小米mix切為經(jīng)典導(dǎo)航鍵模式下的計算問題
我們在MIUI設(shè)置中將全面屏導(dǎo)航樣式修改為“經(jīng)典導(dǎo)航鍵”樣式。
image
重新運行程序,運行結(jié)果如下:
image
可以發(fā)現(xiàn)又出問題了,DecorView = Screen height > NativeBar height + ContentView height
這里不難發(fā)現(xiàn),Screen height將底部虛擬導(dǎo)航欄的高度也算進(jìn)里面了。
很多情況下,我們都用如下方法獲取導(dǎo)航欄的高度:
public static int getNavigationBarHeight() {
Resources resources = AppContext.getInstance().getResources();
int resourceId = resources.getIdentifier("navigation_bar_height",
"dimen", "android");
int height = resources.getDimensionPixelSize(resourceId);
return height;
}
這種方法得到的導(dǎo)航欄的高度數(shù)值是沒問題的,但是在全面屏的手機(jī)上,即使隱藏了導(dǎo)航欄,也是可以獲取到導(dǎo)航欄的高度的。通過上面的logcat日志可以看到,即使沒有導(dǎo)航欄,導(dǎo)航欄的高度的計算也是有值的。
適配小米mix虛擬導(dǎo)航欄
小米mix的機(jī)型中,我們可以“force_fsg_nav_bar”來判斷小米手機(jī)是否開啟了全面屏手勢。
public static int getHeightOfNavigationBar(Context context) {
//如果小米手機(jī)開啟了全面屏手勢隱藏了導(dǎo)航欄則返回 0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
if (Settings.Global.getInt(context.getContentResolver(),
"force_fsg_nav_bar", 0) != 0) {
return 0;
}
}
int realHeight = getScreenSize(context)[1];
Display d = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
d.getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;
return realHeight - displayHeight;
}
因此可以通過這個方法來判斷是否顯示了底部導(dǎo)航欄,并且可以計算導(dǎo)航欄的高度。
int navigationBarHeight = FullScreenManager.getHeightOfNavigationBar(MainActivity.this);
if (navigationBarHeight > 0){
container_navigationview.setVisibility(View.VISIBLE);
}else {
container_navigationview.setVisibility(View.GONE);
}
正常的顯示效果如下:
有虛擬導(dǎo)航欄
沒有虛擬導(dǎo)航欄
image
image
沒有虛擬導(dǎo)航欄Log
2018-10-08 13:19:32.389 /? D/shijiacheng: ==========================
2018-10-08 13:19:32.390 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: Screen height: 2160 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 13:19:32.390 /? D/shijiacheng: ContentView height: 2094 px
2018-10-08 13:19:32.390 /? D/shijiacheng: NavigationBar height: 0 px
2018-10-08 13:19:32.390 /? D/shijiacheng: --------------------------
有虛擬導(dǎo)航欄Log
2018-10-08 13:38:03.229 /? D/shijiacheng: ==========================
2018-10-08 13:38:03.230 /? D/shijiacheng: DecorView height: 2160 px
2018-10-08 13:38:03.230 /? D/shijiacheng: Screen height: 2160 px
2018-10-08 13:38:03.230 /? D/shijiacheng: NativeBar height: 66 px
2018-10-08 13:38:03.230 /? D/shijiacheng: ContentView height: 1964 px
2018-10-08 13:38:03.230 /? D/shijiacheng: NavigationBar height: 130 px
2018-10-08 13:38:03.230 /? D/shijiacheng: --------------------------
總結(jié)
以上是生活随笔為你收集整理的android 配置aspect_Android APP全面屏适配技术要点的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python计算平方用map函数_pyt
- 下一篇: 开关电源怎么测试文波_示波器测试开关电源