【Android】getDimension()、getDimensionPixelOffset()和getDimensionPixelSize()區別詳解

在自定義控件中使用自定義屬性時,經常需要使用java代碼獲取在xml中定義的尺寸,相關有以下三個函數

  • getDimension()
  • getDimensionPixelOffset()
  • getDimensionPixelSize()

(在類TypedArray和類Resources中都有這三個函數,功能類似,TypedArray中的函數是獲取自定義屬性的,Resources中的函數是獲取android預置屬性的)

 
通常初學者(尤其是洋文不大好的朋友們)看到這三個函數的名稱時會有點不知所云。反正在我仔細研究前是這樣,getDimensionPixelSize()函數看名稱是獲取像素,那getDimensionPixelOffset()這玩意兒的offset是啥(通常API裏不都是 begin, offset, len麼)? getDimension()這個函數又是幹啥的,和getDimensionPixelSize()有什麼區別嗎,是獲取原始的dp值嗎(答案是否定的)?

   高手請無視本帖,不太明白的初學者可以往下仔細看看哦~

帶着這些疑惑,看看API reference裏的解釋:

  • getDimension()是基於當前DisplayMetrics進行轉換,獲取指定資源id對應的尺寸。文檔裏並沒說這裏返回的就是像素,要注意這個函數的返回值是float,像素肯定是int。
  • getDimensionPixelSize()與getDimension()功能類似,不同的是將結果轉換爲int,並且小數部分四捨五入。
  • getDimensionPixelOffset()與getDimension()功能類似,不同的是將結果轉換爲int,並且偏移轉換(offset conversion,函數命名中的offset是這個意思)是直接截斷小數位,即取整(其實就是把float強制轉化爲int,注意不是四捨五入哦)。

由此可見,這三個函數返回的都是絕對尺寸,而不是相對尺寸(dp/sp等)。如果getDimension()返回結果是20.5f,那麼getDimensionPixelSize()返回結果就是21,getDimensionPixelOffset()返回結果就是20。

 
到這裏本帖就可以結束了,但如果想知道的多一點,還可以看看android的源代碼,來印證上述解釋。

 
深入源碼,我們可以發現其實這三個函數實現都很像,以Resources類的getDimension()爲例

public float getDimension(int id) throws NotFoundException {
        synchronized (mTmpValue) {
            TypedValue value = mTmpValue;
            getValue(id, value, true);
            if (value.type == TypedValue.TYPE_DIMENSION) {
                return TypedValue.complexToDimension(value.data, mMetrics);
            }
            throw new NotFoundException(
                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
                    + Integer.toHexString(value.type) + " is not valid");
        }
    }

類TypedValue是動態類型數據的容器,主要用於盛放resource的值。上述代碼第4行就是根據resId獲取TypedValue的值,getDimension()、getDimensionPixelOffset()和getDimensionPixelSize()函數體唯一的不同就是第4行:

  • getDimension()調用的是TypedValue的complexToDimension方法
  • getDimensionPixelSize調用的是TypedValue的complexToDimensionPixelSize方法
  • getDimensionPixelOffset調用的是TypedValue的complexToDimensionPixelOffset方法

我們再深入類TypedValue,查看complexToDimension()、complexToDimensionPixelSize()和complexToDimensionPixelOffset()函數的區別,會發現這三個函數體內容還是差不多,以complexToDimension()爲例:

public static float complexToDimension(int data, DisplayMetrics metrics)
    {
        return applyDimension(
            (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
            complexToFloat(data),
            metrics);
    }

complexToDimensionPixelOffset()與complexToDimension()不同的是將結果進行了強制轉換,相當於直接截斷小數部分;
complexToDimensionPixelSize()是將結果進行四捨五入,四捨五入的代碼就是把結果加上0.5f再進行強制轉換(因爲java的float強制轉換爲int都是直接捨去小數的;如果大於等於0.5則加上0.5進位,強制轉換後捨去小數相當於五入;如果小於0.5則加上0.5後整數部分不變,強制轉換捨去小數後相當於四舍,java基礎,第一次接觸的新手普及下~)

 
ok了,簡單的源碼分析完成了。 通過源碼分析,進一步驗證了getDimension()、getDimensionPixelOffset()和getDimensionPixelSize()的區別,大家以後再用到這三個函數的時候就不用發矇了。在java代碼裏很多setWidth(),setHeight()的參數都是像素,即整形,大家根據實際情況,看看如果是四捨五入就調用getDimensionPixelSize(),如果是取整就調用getDimensionPixelOffset()。千萬不要setWidth((int)getDimension()) 這麼寫哦!

 
後記: android並沒有在java代碼中直接獲取xml中定義的dp/sp的值的API,可能是因爲google認爲沒有必要。但如果實在想得到xml中咱們自己寫的dp或sp的值(例如想在日誌裏輸出dp/sp什麼的),請參見我的另一個帖子Java代碼獲取xml中定義的dp/sp值的方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章