Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義控件:圖片比例適配,解決圖片白邊(詳解View中onMeasure方法)

Android自定義控件:圖片比例適配,解決圖片白邊(詳解View中onMeasure方法)

編輯:關於Android編程

這裡寫圖片描述

當App中涉及到布局需要展示大量圖片時,你就應該考慮到“圖片比例適配“的問題。當圖片的寬高規格不同時,你設置展示的ImageView<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPsrHt/G/ydLUPHN0cm9uZz7N6rrDtdjVucq+PC9zdHJvbmc+o6w8c3Ryb25nPszus+TC+jwvc3Ryb25nPqO/0rK+zcrHy7VJbWFnVmlld7XEscjA/brNzbzGrLXEscjA/bK7xqXF5KOssrvIu7XEu7C74bW81sLNvMasxdS74cH009A8c3Ryb25nPr/VsNc8L3N0cm9uZz6jrNXi0fnSu8+1wdC1xNfpzbzEo7/pz8LAraOs09C1xNPQPHN0cm9uZz6w17HfPC9zdHJvbmc+o6zT0LXEw7vT0KOst8ezo9Owz+zDwLnboaM8c3Ryb25nPr3Tz8LAtLXE19S2qNLlv9i8/r2rv8nS1M/7s/0g1bnKvs28xqzT0LDXsd+1xM7KzOKjrNTasru21M28xqy9+NDQyM66zrLDvPShosCtyey1xMewzOHPwqOs1+6088/etsizys/Ws/bNvMas0fnKvaOhPC9zdHJvbmc+PGJyIC8+DQo8aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160916/20160916093613777.png" title="\" />





一. 自定義 按比例展示高度 的控件

以上圖的組圖頁面為例,為了達到更好地顯示圖片的效果,我們來自定義一個控件,這個控件的寬度填充屏幕,但是高度不確定,根據圖片比例具體情況動態的設置高度值

這裡寫圖片描述
比如說組圖頁面展現的圖片樣式都是這種:圖片尺寸為 444 x 183 ,所以比例444/183 =2.43 (具體比例根據你APP中需要展示的圖片類型而定!)有了比例之後,自定義控件中的 高度 就根據這個 比例 來動態設置。


這裡寫圖片描述


1.自定義控件 繼承幀布局

有了以上規劃後,我們可以用一個 幀布局,以它來作為一個容器,讓它的寬高嚴格按照圖片的寬高來設置,使得圖片填充這個幀布沮喎?/kf/yidong/wp/" target="_blank" class="keylink">WPC9zdHJvbmc+o6zNvMasSW1hZ2VWaWV3zt7Q6Nf2yM66zrjEtq+jrNDeuMTWobK8vta8tL/JoaOjqNXiwO+yu9Do0qq/vMLH19S2qNLlv9i8/silvMyz0EltYWdlVmlld6OszKu5/bi01NOjrNahsry+1rj8yN3S18q1z9ajrLb4x9LU2tfUtqjS5b/YvP66zbavzKzM7rPk0rPD5tXit73D5sq508O3x7OjueOjqTwvcD4NCjxwcmUgY2xhc3M9"brush:java;"> /** * 自定義控件, 按照比例來決定布局高度 * Created by gym on 2016/9/15. */ public class RatioLayout extends FrameLayout { private float ratio; public RatioLayout(Context context) { super(context); } public RatioLayout(Context context, AttributeSet attrs) { super(context, attrs); } public RatioLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }


2.自定義屬性,布局編寫

xml文件中部分代碼
        
        
        


attrs.xml  自定義屬性


    
        
    

如以上布局文件中代碼所示,幀布局RatioLayout的寬度填充屏幕,高度隨比例而定,也就是會讓圖片來填充滿這個幀布局,所以原先設定的ImageView的屬性要修改!
android:layout_height="wrap_content"
android:scaleType="fitXY"


3. 在構造方法中獲取 屬性值

 public RatioLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 獲取屬性值
        // attrs.getAttributeFloatValue("", "ratio", -1);
        // 當自定義屬性時, 系統會自動生成屬性相關id, 此id通過R.styleable來引用
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.RatioLayout);
        // id = 屬性名_具體屬性字段名稱 (此id系統自動生成)
        ratio = typedArray.getFloat(R.styleable.RatioLayout_ratio, -1);
        typedArray.recycle();// 回收typearray, 提高性能

        System.out.println("ratio:" + ratio);
    }

獲取屬性值的兩種方法:
一.
一般谷歌最常用的是attrs.getAttributeFloatValue("", "ratio", -1);

二.
(1)首先使用 context 的方法獲取屬性的集合數組。
這裡寫圖片描述
這裡的RatioLayout是在自定義屬性裡定義的,底層已經將它編譯成一個R文件了,就是一個int數組
這裡寫圖片描述

(2)調用完方法後,它會返回一個TypedArray類的數組集合,再去get到自定義的屬性字段即可。(注意:typedArray.getFloat(R.styleable.RatioLayout_ratio, -1); 中的RatioLayout_ratio id 是系統自動生成:”屬性名稱_具體字段“)
這裡寫圖片描述

(3) typeArray 回收,提高性能。

分析完後,明顯發現第一種取屬性值的方法要簡單,所以僅作了解即可。






二. 重寫 onMeasure 方法(詳解onMeasure )

經過以上步驟,我們已經拿到了 比例值,寬度也確定下拉,現在要根據比例值來設置 自定義控件高度。這就涉及到 尺寸的調整,一個布局,它有三個核心的方法:Measure測量調整它的大小;layout設置它的位置;draw具體繪制。

而我們需要在測量—–Measure裡重新修改它的寬高,重寫onMeasure方法。方法中需要做的步驟:
a. 獲取寬度
b. 根據寬度和比例ratio, 計算控件的高度
c.. 重新測量控件

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 1. 獲取寬度
        // 2. 根據寬度和比例ratio, 計算控件的高度
        // 3. 重新測量控件

        //1000000000000000000000111001110
        System.out.println("widthMeasureSpec:" + widthMeasureSpec)

        int width = MeasureSpec.getSize(widthMeasureSpec);// 獲取寬度值
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);// 獲取寬度模式
        int height = MeasureSpec.getSize(heightMeasureSpec);// 獲取高度值
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);// 獲取高度模式

        // 寬度確定, 高度不確定, ratio合法, 才計算高度值
        if (widthMode == MeasureSpec.EXACTLY
                && heightMode != MeasureSpec.EXACTLY && ratio > 0) {
            // 圖片寬度 = 控件寬度 - 左側內邊距 - 右側內邊距
            int imageWidth = width - getPaddingLeft() - getPaddingRight();

            // 圖片高度 = 圖片寬度/寬高比例
            int imageHeight = (int) (imageWidth / ratio + 0.5f);

            // 控件高度 = 圖片高度 + 上側內邊距 + 下側內邊距
            height = imageHeight + getPaddingTop() + getPaddingBottom();

            // 根據最新的高度來重新生成heightMeasureSpec(高度模式是確定模式)
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                    MeasureSpec.EXACTLY);
        }

        // 按照最新的高度測量控件
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

1. onMeasure 方法中的參數分析
void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
方法中兩個參數widthMeasureSpecheightMeasureSpec並非是真正的寬高,而是帶了一種模式的數值表示。

MeasureSpec類的兩個方法,一個是getSize,一個是getMode。首先打印日志查看這個widthMeasureSpec具體值
System.out.println("widthMeasureSpec:" + widthMeasureSpec)
這裡寫圖片描述

1073742286這麼大的值,怎麼可能是它的寬度呢?將它轉為二進制
這裡寫圖片描述

最前面的 ”1“代表的就是模式,後面的數值才是真正的寬度。將111001110 轉換成 十進制,是462個像素(如下圖藍線所示)。我的模擬器是 480x800 ,所以這個值是合理的

這裡寫圖片描述

所以這個widthMeasureSpec值表示的是:模式+寬度值



2. MeasureSpec中模式分析

而MeasureSpec的Mode 模式類型 有三種:
MeasureSpec.AT_MOST:至多模式, 控件有多大顯示多大,類似於wrap_content
MeasureSpec.EXACTLY:確定模式, 類似寬高寫死成dip,類似 match_parent
MeasureSpec.UNSPECIFIED:未指定模式,不確定寬高,動態計算測量。(舉例scrollView,高度決定於它的孩子數量所占的高度)

這裡寫圖片描述

所以之前轉換成那麼大的數,最前頭的1代表模式 EXACTLY,而根據源碼定義 1 << 30,widthMeasureSpec數值之所以那麼大,是因為它左移了30位。
所以真正的寬高值是 將widthMeasureSpec 轉換為二進制後的後面幾位數,再轉換為十進制即可,當然方法中毋須那麼復雜,MeasureSpec.getSize(widthMeasureSpec);即可。




3. 計算控件高度
詳解完 onMeasure方法後,則可以開始重新測量布局,步驟a已完成,進行步驟b ,首先 if 判斷 當寬度確定(這裡的寬度一定要是一個准確值), 高度不確定, ratio合法, 才計算高度值。

 if (widthMode == MeasureSpec.EXACTLY
                && heightMode != MeasureSpec.EXACTLY && ratio > 0)

注意:在計算imageView時,一定要考慮 Padding 值,圖片寬度 = 控件寬度 - 左側內邊距 - 右側內邊距
根據圖片真正的寬度,使用比例計算它的高度,這時我們自定義控件的高度也出來了
控件高度 = 圖片高度 + 上側內邊距 + 下側內邊距,最後我們有了真正高度數值後,再獲取當前Mode類型,封裝好heightMeasureSpec,讓父類執行測量的最新高度值即可。

        // 按照最新的高度測量控件
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);



三.展示效果

總結就是我們就是希望有一個布局,讓它完全按照圖片的比例來展現原樣式,所以我們自定義一個幀布局FrameLayout,讓圖片去填充這個幀布局,幀布局有多寬多高,展示的圖片就有多寬多高。

更改前
這裡寫圖片描述


更改後
這裡寫圖片描述


可以明顯看出,前一張小白邊左右多出來的,後者是完全無貼合的,而且並沒有裁剪、拉伸圖片,按照完美比例展現圖片。以下動圖就是最後成果,徹底消滅圖片留有白邊的情況

這裡寫圖片描述





如果你還是認為這個 圖片比例適配的自定義控件沒起到什麼作用,那我現在把 比例改成1 ,呈現如下,寬高值相同,但是圖片的左右兩邊仍然是無縫銜接,未留有白邊!

這裡寫圖片描述






希望對你有幫助:)

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved