Android 系統中,那些能大幅提高工作效率的 API 彙總(持續更新中...)

亦楓發表於2017-03-11

前言


“條條大路通羅馬。”工作中,實現某個需求的方式往往不是唯一的,這些不同實現方式不僅表現在程式碼質量上,還影響著我們的工作效率。就像,在 Android 系統中,總有那麼一些鮮為人知的 API 能夠減少我們很多零碎的工作量。於是,就想憑著一些經驗,整理一些常用的,找個地方歸納總結,也供日後翻閱。

getResources().getIdentifier(String name, String defType, String defPackage)


根據資源名稱獲取資源 id。正常情況下,我們會在程式碼中直接根據資源ID獲取資源,比如:

getResources().getDrawable(R.drawable.ic_launcher);複製程式碼

但有時候,資源並不是固定的,需要根據使用情況從多個同類資源中動態選擇,比如根據伺服器傳遞給客戶端的介面資料動態設定,怎麼辦呢?設定一個硬編碼的對映關係陣列?太繁瑣!不妨用上這個方法。

一個完整的資源名為package:type/entry,對應該方法的三個引數:資源名稱、資源型別、應用包名。舉幾個例子:

imageView.setImageResource(getResources().getIdentifier("ic_launcher", "drawable", getPackageName()));

radioGroup.check(getResources().getIdentifier("rb_indicator_" + position, "id", getPackageName()));複製程式碼

實際使用過程中,第一個引數 name,也就是資源名稱,根據需要動態設定,這就需要多個資源在命名時保持一定的規範格式。另外,需要注意的是,直接通過 id 獲取資源比通過名字的方式效率更高,所以,如果沒有這般特殊需求的話,不提倡使用這個方法獲取資源。

TextUtils.isEmpty(CharSequence str)


使用頻率超高的字串判空方法,返回一個 boolean 值,內部實現的判斷條件為:str == null || str.length() == 0。備受開發人員喜愛的一個 if 字串判斷,系統已經幫我們封裝過。

Html.fromHtml()


解析 Html 格式的富文字內容,並返回一個帶樣式的字串,供 TextView 等控制元件顯示。可以解決一些含超連結、圖文混排等格式的富文字內容的顯示問題。

DateUtils.formatDateTime()


利用 Android SDK 提供的這個日期工具類可以將 long 型別的毫秒級時間資料格式化成特定顯示格式的字串。通常我們使用 Java SDK 中的 SimpleDateFormat 格式化日期資料,比如 new SimpleDateFormat("yyyy-MM-dd HH:mm").format()DateUtils 的作用就是替我們封裝了這個過程。格式化結果與當前裝置的本地語言環境有關。這裡列舉幾個常用 format 格式(中文環境下):

  • FORMAT_SHOW_DATE:3月3日
  • FORMAT_SHOW_TIME:10:37
  • FORMAT_SHOW_WEEKDAY:星期五
  • FORMAT_SHOW_YEAR:2017年3月3日
  • FORMAT_NUMERIC_DATE:3/3
  • FORMAT_NO_MONTH_DAY:三月

Formatter.formatFileSize(Context context, long sizeBytes)


格式化檔案大小,將位元組資料格式化為 B、KB、M 等單位的相應資料。context 引數用於判斷返回結果的字串順序,right-to-left 還是 left-to-right 形式的。這個工具類免去我們自己轉化計算的過程,非常方便,特別適用於應用內檔案下載的類似場景。

TypedValue.applyDimension(int unit, float value, DisplayMetrics metrics)


將指定單位的尺寸資料按照當前裝置螢幕資訊轉化為相應的畫素值。其中,TypedValue 為第一個引數提供了常用的單位值,比如:

  • COMPLEX_UNIT_PX
  • COMPLEX_UNIT_DIP
  • COMPLEX_UNIT_PT
  • COMPLEX_UNIT_SP

原始碼如下:

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;
}複製程式碼

其實在實際使用過程中,畫素值都是 int 整數型別,而該方法返回的是 float 型別,如果直接強制轉換的話,會自動捨去小數部分。所以,如果不用該方法的話,通常我們會這麼轉換:

public static int convertDipToPx(Context context, int dip) {
    float scale = context.getResources().getDisplayMetrics().density;
    // 0.5f 用於向上取整
    return (int) (dip * scale + 0.5f * (dip >= 0 ? 1 : -1));
}複製程式碼

Space


官方註釋如下:

Space is a lightweight View subclass that may be used to create gaps between components in general purpose layouts.

Space 是一個用於建立檢視之間空隙的輕量級 View。在 onDraw() 方法中不執行任何繪製,所以 android:background 屬性對他來說不起作用。通常我們使用 View 建立檢視間的空隙,在不考慮背景色的情況下,Space 其實效率更高。注意,由於是 API 14 引入的控制元件,如果需要向前相容的話,需要使用到 support v4 包。

view.performClick()


自動呼叫 View 點選事件。通常按鈕等控制元件只有在使用者點選時才能觸發其點選事件,該方法可以由某些特殊條件觸發模擬使用者點選行為。類似的還有 performLongClick() 方法。

Log.getStackTraceString(Throwable tr)


Log 類提供的一個公共靜態方法,與常見的 Log.i() 等方法列印日誌到 logcat 控制檯不同的是,該方法從 Throwable 物件中獲取錯誤資訊,並以字串的形式返回。當你需要做錯誤資訊的資料持久化,比如儲存至本地儲存卡中或者上傳至伺服器時,利用這個方法就非常方便。

Linkify.addLinks()


我們知道對於 TextView 文字控制元件中的內容,通過 android:autoLink 屬性可以為其新增諸如 web、phone 等固定模版的超連結點選事件。但畢竟系統模版有限,而利用 Linkify.addLinks() 方法可以新增一些應用內自定義模版,比如新浪微博中的 "@XXX" 格式的超連結跳轉等,都可以通過自定義正規表示式來匹配處理。

getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE)


設定安全視窗,禁用系統截圖。防止 App 中的一些介面被截圖,並顯示在其他裝置中造成資訊洩漏。(常見手機裝置系統截圖操作方式為:同時按下電源鍵和音量鍵。)

比如支付寶 App 的“向商家付款”的包含付款二維碼的介面。(補充說明一點,微信付款介面不是這麼做的,採用的是在 onResume() 生命週期方法中實時重新整理付款二維碼,與支付寶在安全方法採取的手段不同。)

攔截 Back 鍵,使 App 進入後臺而不是關閉


  @Override
  public void onBackPressed() {
    Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
    launcherIntent.addCategory(Intent.CATEGORY_HOME);
    startActivity(launcherIntent);
  }複製程式碼

使用 Back 鍵返回桌面,但不關閉當前應用,而是使之進入後臺,就像按下 Home 鍵一樣。

這個技巧厲害了。通常為了防止出現使用者誤按 Back 鍵退出 App 的情況,我們會在應用首頁的 Activity 中監聽返回鍵操作,使用 Toast 弱提示甚至 Dialog 強提示的方式給到使用者一個再次確認的操作,但無法阻止使用者通過返回鍵逐步關閉應用。

然而,如果用這個方法攔截 App 最後一個 Activity(常見為首頁介面),既沒有阻礙使用者操作(回到桌面),又沒有關閉掉我們的應用(後臺執行中),間接提高 App 的存活時間,真乃暗度陳倉。並且據我實驗,微信、支付寶、微博等 App 都是這麼做的,大家不妨一試。

ThumbnailUtils


縮圖工具類,可以根據本地視訊檔案源、Bitmap 物件生成縮圖,常用的公共靜態方法為:

  • createVideoThumbnail(String filePath, int kind)
  • extractThumbnail(Bitmap source, int width, int height)

bitmap.extractAlpha()


從源 bitmap 中根據 alpha 獲取一個新的 bitmap 物件。比較繞口,通常 App 中的 Icon 多數是純色透明畫素背景組成,利用這個方法可以對該圖的非透明區域著色,有多種使用場景,常見如 Button 的 pressed 狀態,View 的陰影狀態等。舉個例子:

private static Bitmap getDropShadow(ImageView iv, Bitmap src, float radius, int color) {

    final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(color);

    final int width = src.getWidth(), height = src.getHeight();
    final Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(dest);
    final Bitmap alpha = src.extractAlpha();
    canvas.drawBitmap(alpha, 0, 0, paint);

    final BlurMaskFilter filter = new BlurMaskFilter(radius, BlurMaskFilter.Blur.OUTER);
    paint.setMaskFilter(filter);
    canvas.drawBitmap(alpha, 0, 0, paint);
    iv.setImageBitmap(dest);

    return dest;
}複製程式碼

ArgbEvaluator


系統提供的一個 TypeEvaluator ,我們只需要提供兩個起始顏色值和一個分值,系統會通過特定的演算法計算得出一個新的顏色中間值。利用這個類,我們至少可以做兩件事情。

第一,用於屬性動畫中。由於其實現了 TypeEvaluator 介面,可以用來做自定義屬性動畫的求值器,改變 View 的顯示狀態。比如:

int colorStart = ContextCompat.getColor(this, R.color.black);
int colorEnd = ContextCompat.getColor(this, R.color.green);
ValueAnimator valueAnimator = ValueAnimator
    .ofObject(new ArgbEvaluator(), colorStart, colorEnd)
    .setDuration(3000);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        textView.setTextColor((Integer) animation.getAnimatedValue());
    }
});
valueAnimator.start();複製程式碼

第二,利用該類提供的顏色求值演算法,配合 ViewPager 提供的滑動偏離值使用。這種場景常見於使用 ViewPager 實現的引導頁,其背景色隨著滑動距離動態改變;使用 ViewPager 實現的 Tab 樣式選單頁面,Tab 中文字內容隨著滑動距離動態改變字型顏色(可以參考安卓版微信)。這兩種使用都使得 ViewPager 頁面切換過渡得很自然,體驗極佳。如:

viewPager.addOnPageChangeListener(new OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        new ArgbEvaluator().evaluate(positionOffset, startColor, endColor);
    }

    @Override
    public void onPageSelected(int position) {

    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
});複製程式碼

另外,關於顏色差值的計算,Google Sample 裡有另一種演算法,可參考 SlidingTabStrip.java 檔案原始碼,核心方法內容如下:


/**
* Blend {@code color1} and {@code color2} using the given ratio.
*
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
*              0.0 will return {@code color2}.
*/
private static int blendColors(int color1, int color2, float ratio) {
    final float inverseRation = 1f - ratio;
    float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
    float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
    float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
    return Color.rgb((int) r, (int) g, (int) b);
}複製程式碼

android:weightSum


用於 LinearLayout 中,用於設定 Children weight 的總比重。在 LinearLayout 的 children 中,我們經常會使用 android:layout_weight 按比例分配容器佈局的空間,但有時候不一定會分完。以往,有些朋友可能會使用一個空 放在最後來達到末尾佔位效果。如果你知道這個屬性的話,就能少寫一些程式碼。

android:descendantFocusability


用於 ViewGroup 中,解決作為 Parent 的 ViewGroup 與 Children View 之間的焦點佔用問題。最最常見的使用場景就是 list item 中含有一些點選效果的控制元件,比如 Button、CheckBox 等,相信大家都遇到過。取值有三種,含義就不用再多說了:

  • afterDescendants
  • beforeDescendants
  • blocksDescendants

android:duplicateParentState


是否將 View 自身的 drawable state 交給直接 parent ViewGroup 控制,值為 boolean 型別。比如有一個 item 佈局,item 中有一個 button,如果點選 item layout 時,需要 button 呈現對應的點選效果,就可以在 button 中用到這個屬性。不過,從設計的角度來講,這種場景還是比較少見的。知道有這個屬性就好,不推薦這種互動設計。

android:fillViewport


ScrollView 的一個屬性,用於設定內容部分是否填滿螢幕,主要針對內容不足以填滿螢幕的情況。這裡推薦一個使用技巧,參考我之前寫的文章:Android 日常開發中,兩個非常實用的佈局技巧

android:adjustViewBounds


使用 ImageView 時,你可能會用 android:scaleType 屬性設定圖片縮放方式。殊不知,android:adjustViewBounds 屬性也能起到類似的效果。但要注意的是,後者需要至少指定 ImageView 寬高中的一個屬性,或者 maxHeight 之類的,然後另一個屬性隨之適配。這個屬性用在列表中較為合適,比如 App 中的活動列表頁面,圖片寬度設定為 match_parent,然後高度設為 wrap_content 使其自適應,這樣便能保證從服務獲取的高解析度圖片在不同的螢幕中不被拉伸變形。(備註:最好在專案資原始檔中放置一個與網路圖片相同尺寸的預設圖,起到 placeholder 作用,避免圖片顯示前高度為 0 的較差體驗。)

持續更新中...


本文先記這麼多吧,日積月累,後續持續更新中,敬請關注...(當然,你有什麼高效而又少見的 API,歡迎一起分享交流哦~)

歡迎關注我的微信公眾號


安卓筆記俠:專注於 Android 開發,和程式設計師之路。

Android 系統中,那些能大幅提高工作效率的 API 彙總(持續更新中...)

相關文章