編輯:關於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.自定義屬性,布局編寫 如以上布局文件中代碼所示,幀布局RatioLayout的寬度填充屏幕,高度隨比例而定,也就是會讓圖片來填充滿這個幀布局,所以原先設定的ImageView的屬性要修改!
xml文件中部分代碼
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)
方法中兩個參數widthMeasureSpec、heightMeasureSpec並非是真正的寬高,而是帶了一種模式的數值表示。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 ,呈現如下,寬高值相同,但是圖片的左右兩邊仍然是無縫銜接,未留有白邊!
希望對你有幫助:)
AnDroidDraw 是一個與 DroidDraw 集成的 Android 應用程序,它允許你從 DroidDraw 應用 程序下載你的 GUIs, 也允許你在一個 A
Android 組件ContentProviderAndroid的數據存儲有五種方式Shared Preferences、網絡存儲、文件存儲、外儲存儲、SQLite,一般
最近老板要求在launcher界面做個自動定位,並獲取當地天氣的功能,中間走了不少彎路,我在這裡都寫下來,希望看到這篇文章的人,能少走點彎路。1、接到任務後,我首先想的是
最近學到用AsyncTask來處理有關網絡的操作。雖然代碼看上去不是很復雜,但仍有很多地方有疑惑。所以研讀了一下API文檔,在這裡把我學到的和練習的代碼展示出來。如有錯誤