1. 程式人生 > >Android相容性小總結(一)

Android相容性小總結(一)

前言

在完美完成過年增肥任務之後,新的一年又得投入到工作當中了,今天是新年的第一篇部落格,我們來討論一下Android開發經久不變的相容性問題。

國內有很多的廠商都定製了自己的安卓系統,誰也不知道會他們私底下都做了哪些操蛋的修改,再加上安卓版本的更新週期目前還是比較穩定,幾乎每一年都會發布新的版本,例如去年的8.0。目前國內的Android系統主要是5.0和6.0,少量的4.x和7.0,掰掰手指頭就5、6個,所以如何做好相容性問題,一直是開發者,也是老闆最關心的問題。

今天我準備簡單介紹幾個比較細節的例子,大家一起學習討論下。

正文

我把相容性問題主要分為兩大類:

1、功能相容問題,主要是系統版本的新特性,api的變化,或者是sdk定製導致部分api的執行結果出現差異導致的,一般需要重寫方法,或者判斷系統版本特殊處理。

2、佈局相容問題,主要是不同ViewGroup的特性和某些屬性不能同時使用的問題,也可能是由不同Android系統版本自帶的Theme或者其他特性導致的。

功能相容問題

獲得從相簿選擇的圖片的路徑

從相簿選擇圖片,系統會返回給我們Uri,但是個別情況我們也需要得到路徑,其實這就有一個相容性問題,在不同系統版本的手機你會發現有的能得到路徑,有的不能。

照相機拍照以及呼叫系統安裝

從7.0以後,為了提高app安全,Android不允許app與其他app通訊時,傳遞明文的檔案路徑,比較典型的例子就是app下載更新,安裝的時候需要通過自己的ContentProvider對apk的路徑進行加密,然後呼叫系統安裝。

有的時候需要呼叫系統相機拍照,並且指定照片的儲存路徑。這個路徑我們也必須加密,否則會直接崩潰,具體的解決辦法我之前有寫過:

適配android7.0:獲取檔案的Uri

許可權

在Android 6.0以及國內部分手機在Android 5.0開啟了許可權申請,部分敏感許可權需要手動申請,所以如果你需要使用某些許可權,一定要記得申請,網上這部分資料非常多,這裡就不多說了。

ScrollView滑動到底部的問題

這個問題是最近才發現的,看來測試機多了還是有好處的,一般我們監聽到ScrollView是否滑動到底部通過重寫

onOverScrolled方法,判斷clampedY是否是true,如果是true,表示滑動到了底部。

protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) 

但是在部分手機上clampedY始終返回false,例如我發現的錘子手機。

解決辦法:不僅要判斷clampedY是否等於true,還要判斷ScrollView的scrollY + getHeight + paddingTop + paddingBottom是否等於第一個子View的高度,下面貼出程式碼:

@Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        if (scrollY != 0 && null != onScrollToBottom) {
            // 如果是false,需要額外判斷,解決部分手機的相容問題,例如錘子
            if (!clampedY) {
                // 解決在錘子
                if (getScrollY() + getHeight() - getPaddingTop() - getPaddingBottom() == getChildAt(0).getHeight()) {
                    onScrollToBottom.onScrollBottomListener(true);
                    return;
                }
            }
            onScrollToBottom.onScrollBottomListener(clampedY);
        }
    }

Theme相容問題

使用Theme,推薦使用Theme.AppCompat下的主題,這樣避免相容性問題,如果你看到了你的錯誤提示:

You need to use a Theme.AppCompat theme (or descendant) with this activity

說明你也是一個有故事的人了,我遇到多發生在Android 8.0 和 華為手機。

佈局相容問題

RelativeLayout子View的padding、margin和center屬性的衝突

首先我們看一段程式碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="#000000"
    android:paddingRight="30dp"
    android:paddingBottom="10dp">

    <TextView
        android:layout_marginTop="50dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/app_name"
        android:textColor="#ffffff" />

</RelativeLayout>
在RelativeLayout設定paddingBottom=20dp,但是TextView設定了centerInParent並且設定了marginTop=50dp,會顯示出什麼效果呢?很明顯paddingBottom和marginTop都沒有生效,同樣的道理,子View的centerInHorizontal,centerInVertical屬性也是和父View的padding和自身的margin是不能同時生效的。Button的預設大小問題
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00ff00"
        android:drawablePadding="5dp"
        android:text="hello button" />

    <TextView
        android:layout_marginTop="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00ff00"
        android:textSize="14sp"
        android:textColor="#000000"
        android:drawablePadding="5dp"
        android:text="hello button" />

</LinearLayout>

這裡我特意設定了text="hello button", 因為英文會讓問題更明顯,Button和TextView都是wrap_content,理論上應該是一樣的效果,看一下展示圖:


咦?很明顯結果並不和我們預料的一樣,強調一下我使用的sdk版本是26,如果你的展示效果和我的不一樣,那就說明了一個問題:Button在不同的sdk中有不同的預設樣式

接下來我們看一看預設樣式是什麼:

// 首先我們檢視Button的構造方法,看到了Button使用了buttonStyle樣式
// 用過style切換主題的朋友對這種用法一定很熟悉,接下來我們要看看style檔案裡怎麼定義的buttonStyle
public Button(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.buttonStyle);
}
<!-- Button styles -->
// 順著主題的繼承關係,我們跟蹤到了Base.Widget.AppCompat.Button主題
<item name="buttonStyle">@style/Widget.AppCompat.Button</item>
<style name="Widget.AppCompat.Button" parent="Base.Widget.AppCompat.Button"/>

當我們繼續跟蹤程式碼的時候出現了一個選擇框:


這個主題在sdk 21以下 和sdk以上分別使用了兩種主題,我們先看看sdk 21以下的style:

<style name="Base.Widget.AppCompat.Button" parent="android:Widget">
    <item name="android:background">@drawable/abc_btn_default_mtrl_shape</item>
    <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
    <item name="android:minHeight">48dip</item>
    <item name="android:minWidth">88dip</item>
    <item name="android:focusable">true</item>
    <item name="android:clickable">true</item>
    <item name="android:gravity">center_vertical|center_horizontal</item>
</style>

再看看sdk 21以上的style:

<style name="Base.Widget.AppCompat.Button" parent="android:Widget.Material.Button"/>
<!-- Bordered ink button -->
<style name="Widget.Material.Button">
    <item name="background">@drawable/btn_default_material</item>
    <item name="textAppearance">?attr/textAppearanceButton</item>
    <item name="minHeight">48dip</item>
    <item name="minWidth">88dip</item>
    <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
    <item name="focusable">true</item>
    <item name="clickable">true</item>
    <item name="gravity">center_vertical|center_horizontal</item>
</style>

通過對比,我們發現兩種主題的背景,字型樣式是不同的,同時發現了有minWidth和minHeight,所以Button會有預設的大小,字母會全大寫是textAppearance中定義的,感興趣的朋友可以自己去看看裡面還定義了哪些樣式。

解決辦法:設定Button的minWidth和minHeight等於0,你也可以按照你的需要修改其他的屬性。

總結

上面的幾點是我臨時整理的,還有很多的內容都沒有寫出來,以後會慢慢補充,最後祝大家在新的一年裡技術薪資雙提升!!!