解讀ImageView的wrap_content和adjustViewBounds的工作原理
可以看到在圖片的上下留了很大的空白空間,有人可能想設定fitXY來解決這個問題。結果是ImageView區域未變,但是圖片變形了,如圖:
上面這種情況出現在圖片實際尺寸要大於螢幕尺寸(或為ImageView設定的尺寸)。那如果圖片比較小,情況會改善麼?
我們同樣觀察設定fitXY前後的情況,圖片如下:
可以看到當圖片比較小的時候,會左右留出空白,而設定fitXY後則ImageView區域依然未改變,所以圖片變形了。
1、wrap_content
那麼設定了wrap_content的ImageView的區域為什麼無法貼合圖片內容的大小呢?我們知道View的onMeasure函式是計算一個view的大小的,那麼讓我們來看看ImageView的onMeasure函式,程式碼如下:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { resolveUri(); int w; int h; // Desired aspect ratio of the view's contents (not including padding) float desiredAspect = 0.0f; // We are allowed to change the view's width boolean resizeWidth = false; // We are allowed to change the view's height boolean resizeHeight = false; final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); if (mDrawable == null) { // If no drawable, its intrinsic size is 0. mDrawableWidth = -1; mDrawableHeight = -1; w = h = 0; } else { w = mDrawableWidth; h = mDrawableHeight; if (w <= 0) w = 1; if (h <= 0) h = 1; // We are supposed to adjust view bounds to match the aspect // ratio of our drawable. See if that is possible. if (mAdjustViewBounds) { resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; desiredAspect = (float) w / (float) h; } } final int pleft = mPaddingLeft; final int pright = mPaddingRight; final int ptop = mPaddingTop; final int pbottom = mPaddingBottom; int widthSize; int heightSize; if (resizeWidth || resizeHeight) { // Get the max possible width given our constraints widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec); // Get the max possible height given our constraints heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec); if (desiredAspect != 0.0f) { // See what our actual aspect ratio is final float actualAspect = (float)(widthSize - pleft - pright) / (heightSize - ptop - pbottom); if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { boolean done = false; // Try adjusting width to be proportional to height if (resizeWidth) { int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; // Allow the width to outgrow its original estimate if height is fixed. if (!resizeHeight && !sCompatAdjustViewBounds) { widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec); } if (newWidth <= widthSize) { widthSize = newWidth; done = true; } } // Try adjusting height to be proportional to width if (!done && resizeHeight) { int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; // Allow the height to outgrow its original estimate if width is fixed. if (!resizeWidth && !sCompatAdjustViewBounds) { heightSize = resolveAdjustedSize(newHeight, mMaxHeight, heightMeasureSpec); } if (newHeight <= heightSize) { heightSize = newHeight; } } } } } else { ... widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); } setMeasuredDimension(widthSize, heightSize); }
我們一步步來看,首先看這個函式一開始呼叫了resolveUri這個函式,這個函式程式碼如下:
private void resolveUri() { ... if (mResource != 0) { try { d = mContext.getDrawable(mResource); } catch (Exception e) { ... } } else if (mUri != null) { d = getDrawableFromUri(mUri); ... } else { return; } updateDrawable(d); } private void updateDrawable(Drawable d) { ... if (d != null) { ... mDrawableWidth = d.getIntrinsicWidth(); mDrawableHeight = d.getIntrinsicHeight(); ... } else { mDrawableWidth = mDrawableHeight = -1; } }
通過上面程式碼可以看到resolveUri函式會先得到drawable物件(從resource或uri中),然後通過updateDrawable將
mDrawableWidth和mDrawableHeight這兩個變數設定為drawable的寬高。
我們回到onMeasure函式繼續往下看,第一個if-else,因為我們討論的是有圖片的情況,所以mDrawable一定不為null,那麼走進了else語句,
將剛才的mDrawableWidth和mDrawableHeight兩個變數的值賦給了w和h這兩個區域性變數。
同時這裡如果mAdjustViewBounds為ture,則改變resizeWidth和resizeHeight。而他們的預設值是false。這裡我們先討論mAdjustViewBounds為false的情況。
再繼續,第二個if-else,判斷是否resize,由於mAdjustViewBounds為false,所以resizeWidth和resizeHeight都為false,走進else語句塊。
在else中則用resolveSizeAndState這個函式來計算寬高,程式碼如下:
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
final int result;
switch (specMode) {
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
result = size;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
首先從measureSpec中獲取mode和size。
這裡簡單解釋一下measureSpec,它是一個int值,前兩位(32和31位)儲存標誌位,即specMode;後面則儲存著size即限制大小。而measureSpec是父View傳給子View的,也就是說父View會根據自身的情況限制子View的大小。
這裡還涉及到specMode的三種模式:
①UNSPECIFIED:父View沒有對子View施加任何約束。它可以是任何它想要的大小。
②EXACTLY:父View已經確定了子View的確切尺寸。子View將被限制在給定的界限內,而忽略其本身的大小。
③AT_MOST:子View的大小不能超過指定的大小
這部分也值得一說,以後專門開一篇新文章來仔細講講。
基於上面的解釋,我們回頭在來看resolveAdjustedSize的程式碼就比較好理解了。
在從measureSpec中的到了mode和size後,根據mode不同會有不同的處理。
這裡我們有一個隱藏的前提暴露出來了,就是ImageView不能超出它的父view的顯示區域。即mode只能為EXACTLY或AT_MOST。因為view的寬度是match_parent,所以mode是EXACTLY,直接是父view的寬度,我們就不再考慮了。
那麼重點來看高度,因為是wrap_content,所以mode應該是AT_MOST,則最終的高度是desiredSize、specSize和maxSize的最小值,desiredSize是前面獲取的圖片的高度,specSize是父view限制的大小。而最終高度則取他們兩個的最小值。
這樣我們就有一個結論,在我們設定的前提下,ImageView的寬度是父View的限制寬度,而高度是圖片高度與父View限制高度的較小值。兩者並無關聯,所以並不會按照圖片的比例計算自己的寬高。所以在這種情況下,wrap_content無法達到讓ImageView按圖片的比例顯示,這樣就會出現文章開頭的情況。
2、adjustViewBounds
上面我們發現為ImageView設定了wrap_content無法達到效果。但是ImageView還有一個adjustViewBounds引數,當設定了這個引數,如下:android:adjustViewBounds="true"
ImageView就可以按照圖片的比例來顯示了。
這是怎麼實現的?
接下來我們回過頭看看之前的mAdjustViewBounds,我們上面討論的是它為false的情況。當我們設定了adjustViewBounds,它就為ture了,這時就執行if語句中的程式碼:
resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
desiredAspect = (float) w / (float) h;
當ImageView的寬高沒有都是設定為固定值或match_parent時,resizeWidth和resizeHeight一定有一個為ture。而desiredAspect則是寬高比。
繼續看onMeasure中接下來的程式碼,在第二個if-else時,由於resizeWidth和resizeHeight一定有一個為ture,所以走進if語句塊。
首先呼叫了resolveAdjustedSize這個函式來計算寬高。我們先來看看resolveAdjustedSize的程式碼:
private int resolveAdjustedSize(int desiredSize, int maxSize,
int measureSpec) {
int result = desiredSize;
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
/* Parent says we can be as big as we want. Just don't be larger
than max size imposed on ourselves.
*/
result = Math.min(desiredSize, maxSize);
break;
case MeasureSpec.AT_MOST:
// Parent says we can be as big as we want, up to specSize.
// Don't be larger than specSize, and don't be larger than
// the max size imposed on ourselves.
result = Math.min(Math.min(desiredSize, specSize), maxSize);
break;
case MeasureSpec.EXACTLY:
// No choice. Do what we are told.
result = specSize;
break;
}
return result;
}
與上面resolveSizeAndState方法很類似,當mode為AT_MOST時,
result = Math.min(Math.min(desiredSize, specSize), maxSize);
是取圖片size、specSize和maxSize的最小值。
到目前為止沒什麼不同,下面才是重點,繼續看onMeasure下面的程式碼:
final float actualAspect = (float)(widthSize - pleft - pright) /
(heightSize - ptop - pbottom);
if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {
boolean done = false;
// Try adjusting width to be proportional to height
if (resizeWidth) {
int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
pleft + pright;
// Allow the width to outgrow its original estimate if height is fixed.
if (!resizeHeight && !sCompatAdjustViewBounds) {
widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);
}
if (newWidth <= widthSize) {
widthSize = newWidth;
done = true;
}
}
// Try adjusting height to be proportional to width
if (!done && resizeHeight) {
int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
ptop + pbottom;
// Allow the height to outgrow its original estimate if width is fixed.
if (!resizeWidth && !sCompatAdjustViewBounds) {
heightSize = resolveAdjustedSize(newHeight, mMaxHeight,
heightMeasureSpec);
}
if (newHeight <= heightSize) {
heightSize = newHeight;
}
}
}
當計算後的寬高比與圖片寬高比不同時,會根據之前resizeWidth和resizeHeight,用固定的那個值和圖片寬高比取計算另外一個值。
這樣ImageView的寬高比例就完全符合了圖片的實際寬高比,不會出現文章前面的留白的情況了。
3、其他元件
本文討論的是ImageView保持固定的寬高比,那麼其他元件也可以麼?相關推薦
(轉)計算機原理學習(1)-- 馮諾依曼體系和CPU工作原理
原文:https://blog.csdn.net/cc_net/article/details/10419645 對於我們80後來說,最早接觸計算機應該是在95年左右,那個時候最流行的一個詞語是多媒體。 依舊記得當時在同學家看同學輸入幾個DOS命令就成功的打開了一個遊戲,當時實在是佩服的五體投地。因為對我來
乾貨!!c++new和delete工作原理 以及 針對連結串列節點過載operator new 和operator delete 實現連結串列節點使用記憶體池申請和釋放空間
第一部分: new和delete的實現原理 開始談之前我們應該瞭解另一個概念“operator new”和“operator delete”: new操作符呼叫一個函式來完畢必需的記憶體分配,你可以重寫或過載這個函式來改變它的行為。new操
HTTP Session和Cookie工作原理
session的工作原理 術語session在我的經驗裡,session這個詞被濫用的程度大概僅次於transaction,更加有趣的是transaction與session在某些語境下的含義是相同的。 session,中文經常翻譯為會話,其本來的含義是指有始有終的一系列動作/訊息,比如打電話時從拿起電話撥號
記憶體模型(堆和棧工作原理,String詳解)
JVM主要管理兩種型別記憶體:堆和非堆。 1.堆是執行時資料區域,所有類例項和陣列的記憶體均從此處分配,這些物件通過new、newarray、 anewarray和multianewarray等
Servlet、Filter 和Listener 工作原理
Servlet工作原理 一、Servlet生命週期分為三個階段: 1、初始化階段,呼叫init()方法 2、響應客戶請求階段,呼叫service()方法 3、終止階段,呼叫destroy()方法 二、Servlet初始化階段,在下列時刻Servlet容器裝載Servlet:
計算機原理學習(1)-- 馮諾依曼體系和CPU工作原理
前言 對於我們80後來說,最早接觸計算機應該是在95年左右,那個時候最流行的一個詞語是多媒體。 依舊記得當時在同學家看同學輸入幾個DOS命令就成功的打開了一個遊戲,當時實在是佩服的五體投地。因為對我來說,螢幕上的東西簡直就是天書。有了計算機我們生活發生了巨大的變化,打遊
HSRP和VRRP工作原理
2.1.2 HSRP工作原理 多數IP主機有一個以單一路由器作為預設閘道器的IP地址。當使用HSRP時,IP主機的預設閘道器將以HSRP組的虛擬IP地址替代具體物理路由器介面的IP地址。HSRP通過為網路中的主機提供冗餘的IP通訊路由來實現網路的高可用性。1. HSRP組中路由器的兩種角色
路由和交換機工作原理
打包 否則 溢出 流量 .網絡 限制 入口 repeater 連接服務器 路由器與交換機的工作原理 計算機網絡往往由許多種不同類型的網絡互連連接而成。如果幾個計算機網絡只是在物理上連接在一起,它們之間並不能進行通信,那麽這種“互連&rdquo
「MoreThanJava」一文了解二進位制和CPU工作原理
![](https://imgkr.cn-bj.ufileos.com/def6144e-d6a2-4f06-9d6c-7f2c0acf6cc9.png) - **「MoreThanJava」** 宣揚的是 **「學習,不止 CODE」**,本系列 Java 基礎教程是自己在結合各方面的知識之後,對 Jav
解讀ImageView的wrap_content和adjustViewBounds的工作原理
ImageView是android開發過程中經常會使用的一種元件,由於android螢幕碎片化的問題,有時候我們無法設定一個具體的寬高。比如說width是match_parent的,這時候我們還想讓圖片在寬度完全填充並能正常顯示,我們直接會想到將height設定為wrap_content。但是用過的同學都知道
Android 基於Netty的消息推送方案之概念和工作原理(二)
img b2c 決定 watermark net nios 通道 感覺 art 上一篇文章中我講述了關於消息推送的方案以及一個基於Netty實現的一個簡單的Hello World。為了更好的理解Hello World中的代碼,今天我來解說一下關於Netty中一些概念和工
springMVC 的工作原理和機制、配置
spring mvc+my batis kafka dubbo+zookeerper restful redis分布式緩存 工作原理下面的是springMVC的工作原理圖:1、客戶端發出一個http請求給web服務器,web服務器對http請求進行解析,如果匹配DispatcherServle
深入解析瀏覽器的幕後工作原理(三) 呈現樹和 DOM 樹的關系
文本 一行 出現 src 格式 關於 放置 顯示 關系 呈現樹和 DOM 樹的關系 呈現器是和 DOM 元素相對應的,但並非一一對應。非可視化的 DOM 元素不會插入呈現樹中,例如“head”元素。如果元素的 display 屬性值為“none”,那麽也不會顯示在呈現
lvs和keeplived的工作原理詳解
lvs+keeplived的工作原理一、lvs的工作原理 使用集群的技術和liunx的操作系統實現一個高性能、高可用的服務器。可伸縮性、可靠性、很好的管理性。 特點:可伸縮網絡服務的幾種結構,它們都需要一個前端的負載調度器(或者多個進行主從備份)。我們先分析實現虛擬網絡服務的主要技術,指出IP負載均衡技術
Struts2工作原理和執行流程圖
過濾器 map filters play servle 同時 cati 通過 spa 在struts2的應用中,從用戶請求到服務器返回相應響應給用戶端的過程中,包含了許多組件如:Controller、ActionProxy、ActionMapping、Configurati
(轉)Java 詳解 JVM 工作原理和流程
移植 獲得 代碼 適配 調用 tac 階段 main方法 等待 作為一名Java使用者,掌握JVM的體系結構也是必須的。說起Java,人們首先想到的是Java編程語言,然而事實上,Java是一種技術,它由四方面組成:Java編程語言、Java類文件格式、Java虛擬機和Ja
Servlet生命周期和工作原理
所有 equal web容器 protoc xml文件 body ror 動態網頁 servlet容器 Servlet生命周期分為三個階段: 1,初始化階段 調用init()方法 2,響應客戶請求階段 調用service()方法 3,終止階段 調用dest
CSS布局模型 之 浮動模型(浮動的工作原理和清除浮動技巧?)
浮動 浮動模型 工作原理 浮動的工作原理浮動是讓某元素脫離文檔流,在浮動框之前和之後的非定位元素會當它不存在一樣,可能沿著它的另一側垂直流動,但都為其騰出空間,塊級元素也不例外(被浮動元素占據了部分行空間的塊級元素,仍然被看作是占據了一整行,只不過是被浮動元素占據的那部分空間無法利用罷了)。浮動的
strust2的核心和工作原理
如何工作 java語言 creates 幫助 multipart bject null -a throws 在學習strust2之前,我們要明白使用struts2的目的是什麽?它能給我們帶來什麽樣的好處? 設計目標 Strust設計的第一目標就是使MVC模式應用於we
負載均衡器部署方式和工作原理
硬件負載均衡 f5設備概述負載均衡(Load Balance)由於目前現有網絡的各個核心部分隨著業務量的提高,訪問量和數據流量的快速增長,其處理能力和計算強度也相應地增大,使得單一的服務器設備根本無法承擔。在此情況下,如果扔掉現有設備去做大量的硬件升級,這樣將造成現有資源的浪費,而且如果再面臨下一次業務量的提