1. 程式人生 > >詳解onMeasure()(二)--利用onMeasure測量來實現圖片拉伸永不變形,解決螢幕適配問題

詳解onMeasure()(二)--利用onMeasure測量來實現圖片拉伸永不變形,解決螢幕適配問題

原文地址 :http://blog.csdn.net/cyp331203/article/details/45027641  

上一篇文章詳細講解了一下onMeasure/measure方法在Android自定義控制元件時的原理和作用,參看博文:Android自定義控制元件系列七:詳解onMeasure()方法中如何測量一個控制元件尺寸(一),今天就來真正實踐一下,讓這兩個方法大顯神威來幫我們搞定圖片的螢幕適配問題。

使用ImageView會遇到的問題

        在Android應用中,都少不了圖片的顯示,ImageView,輪播圖,ViewPager等等,很多都是來顯示圖片的,比如一個廣告條的輪播效果,參看部落格:

廣告條效果實現----ViewPager載入大圖片(LruCache)以及定時重新整理,很多時候,我們都希望圖片能夠在寬度上填充父窗體,這樣比較符合人的審美觀點,但是問題就隨之而來了,那就是高度如何定義??先來看一個普通的ImageView的 Xml佈局檔案的定義:

  1. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     android:orientation="vertical">
  6.     <ImageView
  7.         android:layout_width="fill_parent"
  8.         android:layout_height="wrap_content"
  9.         android:src="@drawable/recommend_39"/>
  10.     <TextView
  11.         android:layout_width="wrap_content"
  12.         android:layout_height="wrap_content"
  13.         android:text="描述文字資訊........................."/>
  14. </LinearLayout>

為了方便檢視,我在ImageView下面又加上了一句描述的資訊的TextView,這時,父控制元件都是填充父窗體,而ImageView則是:橫向填充父窗體,縱向包裹內容;text都是包裹內容;那麼來看看顯示效果:


上面那個藍色的小框就是ImageView的範圍,這種效果一般都不會是我們想要的,那麼如果想要ImageView中的圖片能夠填滿ImageView的整個窗體怎麼辦?新增一個屬性:scaleType,如下:

  1. <ImageView
  2.     android:layout_width="fill_parent"
  3.     android:layout_height="wrap_content"
  4.     android:scaleType="fitXY"
  5.     android:src="@drawable/recommend_39"/>

效果如圖:


可以看到,填是填滿了,但是也由於縱向拉伸而使圖片變形了。那要怎麼做呢?

我們仔細觀察一下,不難發現ImageView的縱向高度是包裹內容:wrapcontent,有些同學可能想到,能夠直接在這裡給ImageView一個特定的dip值,讓這個ImageView符合圖片的寬高比呢?這樣做無疑是可以的,但是卻不具有通用性。。。下面還是用上面的例子來講解:

我們先來實現一下,制定ImageView的高度,來達到讓這張圖片在這個特定模擬器下顯示比例正常的過程:

上面的圖片實際畫素的尺寸是:828*314,寬度和高度的比例大約是2.43,而我們這裡使用的模擬器的尺寸是480*800(單位是px,也就是畫素),也就是說寬度上的畫素是480,那麼我們要設定這個ImageView的高度為多少dip才能夠讓其正好符合2.43的比例呢。

Android裝置上px(畫素)、dpi(畫素密度)、dip(密度無關畫素)之間的關係:

這裡又要牽扯出另外一塊知識點,dip和dpi的聯絡:

解析度(Resolution):表示裝置螢幕上畫素點的總數,比如上面的模擬器,螢幕畫素尺寸是480(px)*800(px)

dpi(畫素密度):是指每英寸的畫素,所以同分辨率的兩個裝置,它們的dpi很可能不一樣;如果一個手機解析度5寸是1080*1920,而一個平板9.7寸解析度也是1080*1920,那麼 手機的dpi會比平板高出很多。

dp/dip:全稱是Density-independent pixel ,中文名是 “密度無關畫素”,也就是我們經常在xml檔案中寫的長度單位dp。為什麼叫做密度無關畫素呢,這其實是為了解決不同解析度裝置顯示效果統一的一個解決方案,試想,如果一個兩個手機螢幕都是一樣大小,比如5寸,A手機的解析度是 720*1280,而B手機的解析度是1080*1920;那麼如果我們想在上面顯示一個圖片解析度為:200*200的圖片,就會發現,在A手機上顯示的圖片,比B手機上顯示的圖片要小了很多;直觀的來看,A手機的寬度是720,顯示200*200的圖片差不多要佔據將近1/3的寬度,而反觀B手機,寬度是1080,顯示200*200的圖片,則只需要佔據1/5不到的寬度,而兩個手機的尺寸又都是5寸,所以就會在顯示同樣解析度的圖片時,產生大小的差異。這種差異明顯不是我們想要的。

所以dip/dp,密度無關畫素就應運而生;它是這樣規定的,dip與一個dpi(畫素密度)為160dpi的裝置的px(畫素)值是相等的,而對於其他畫素密度的裝置,則依據轉換公式來計算對應的dip值,這個公式是根據dpi(相當於比例),來轉換px(畫素)和dip(密度無關畫素)的:

  1. px = dip * (dpi / 160)  
  2. dip = px / (dpi / 160)  

經過上面的轉換之後,由於dip和px的轉換是按照比例來的,而這個比例又是dpi/160,而dpi又是根據各個裝置的解析度和尺寸的比例得來的,所以使用相同的dip來設定的尺寸的控制元件,在相同尺寸大小的裝置上,不論裝置的解析度是多少,它們顯示的大小都會是一樣的。

計算ImageView的合適高度的方法

有了上面的知識之後,我們就可以來轉換一下我們的ImageView的大小了:

首先,圖片的寬高比是2.43,而模擬器螢幕的寬度是480px,於是計算得到圖片應該顯示的高度是:480/2.43=197.53px(畫素),但是一般時候,我們在xml檔案中,設定的高度單位都是dip,所以這裡要需要使用上面的轉換公式:dip = px / (dpi / 160),而在這個模擬器的引數上可以查詢到,它的dpi是240,所以計算得到高度應該是:

197.53/(240/160)=131.68(dip),約等於132dp,於是我們將上面的ImageView的高度設定成132dp:

  1. <ImageView
  2.     android:layout_width="fill_parent"
  3.     android:layout_height="132dp"
  4.     android:scaleType="fitXY"
  5.     android:src="@drawable/recommend_39"/>

再來看一下顯示效果:


這樣顯示比例就完全正常了。

但是這樣問題就解決了嗎?答案是沒有,我們不妨來換一個模擬器來顯示,這次選用Nexus7的模擬器,解析度為1200*1920,dpi為320,ImageView的引數不變,再來看看效果:


會發現圖片被拉長了,這是為什麼 呢,我們可以簡單的再算一下:

nexus7 寬度為1200px,而dpi為320,圖片比例為2.43,那麼應該設定ImageView的高度dp值是:1200/2.43/(320/160)=246.91dp,而我們現在的高度卻還是之前的132dp,當然會發現被拉伸了。

那怎麼辦,有點讓人抓狂!!!

重寫onMeasure方法,重新測量控制元件高度,實現多種螢幕下自適應圖片顯示

其實辦法是有的,思路就是讓控制元件(ImageView)自己根據不同的裝置幫我們來計算這個高度,而不需要我們自己去計算,那要怎麼做呢?就得用到上一篇博文中說到的onMeasure方法了,對measure/onMeasure方法不熟悉的同學,可以再去看一下Android自定義控制元件系列七:詳解onMeasure()方法中如何測量一個控制元件尺寸(一)和下面就開始:

首先要明確的一點就是,自定的view在呼叫view.measure()之前,是得不到控制元件的寬和高的,下面就一步步來寫:

思路是首先寫一個SmartImageView來繼承自ImageView,並且新增相應的構造:

  1. package com.example.imageviewdemo;  
  2. import android.content.Context;  
  3. import android.util.AttributeSet;  
  4. import android.widget.ImageView;  
  5. /** 
  6.  * @author : 苦咖啡 
  7.  *  
  8.  * @version : 1.0 
  9.  *  
  10.  * @date :2015年4月14日 
  11.  *  
  12.  * @blog : http://blog.csdn.net/cyp331203 
  13.  *  
  14.  * @desc : SmartImageView,能根據給定的圖片比例,自動調整寬高,解決拉伸變形的螢幕適配問題 
  15.  */
  16. publicclass SmartImageView extends ImageView {  
  17.     public SmartImageView(Context context, AttributeSet attrs,  
  18.             int defStyleAttr, int defStyleRes) {  
  19.         super(context, attrs, defStyleAttr, defStyleRes);  
  20.     }  
  21.     public SmartImageView(Context context, AttributeSet attrs, int defStyle) {  
  22.         super(context, attrs, defStyle);  
  23.     }  
  24.     public SmartImageView(Context context, AttributeSet attrs) {  
  25.         super(context, attrs);  
  26.     }  
  27.     public SmartImageView(Context context) {  
  28.         super(context);  
  29.     }  
  30. }  


然後在SmartImageView中,新增一個float型別的成員變數ratio作為圖片的比例值,並且給它暴露一個setter方法,以便於設定圖片比例。

  1. /** 圖片寬和高的比例 */
  2. privatefloat ratio = 2.43f;  
  3. publicvoid setRatio(float ratio) {  
  4.     this.ratio = ratio;  
  5. }  


然後我們來重寫onMeausre方法,如下:

  1. @Override
  2. protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.     // 父容器傳過來的寬度方向上的模式
  4.     int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  5.     // 父容器傳過來的高度方向上的模式
  6.     int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  7.     // 父容器傳過來的寬度的值
  8.     int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft()  
  9.             - getPaddingRight();  
  10.     // 父容器傳過來的高度的值
  11.     int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingLeft()  
  12.             - getPaddingRight();  
  13.     if (widthMode == MeasureSpec.EXACTLY  
  14. 相關推薦

    onMeasure()()--利用onMeasure測量實現圖片永不變形解決螢幕問題

    原文地址 :http://blog.csdn.net/cyp331203/article/details/45027641   上一篇文章詳細講解了一下onMeasure/measure方法在Android自定義控制元件時的原理和作用,參看博文:Androi

    Android自定義控制元件系列八:onMeasure()()--利用onMeasure測量實現圖片永不變形解決螢幕問題

            上一篇文章詳細講解了一下onMeasure/measure方法在Android自定義控制元件時的原理和作用,參看博文:Android自定義控制元件系列七:詳解onMeasure()方法中如何測量一個控制元件尺寸(一),今天就來真正實踐一下,讓這兩個方法大顯神威來幫我們搞定圖片的螢幕適配問題。

    Java——IO流()使用位元組流實現圖片加密、拷貝檔案的功能

    1.實現圖片加密功能 2.拷貝檔案(在控制檯錄入檔案的路徑,將檔案拷貝到當前專案下)  3.實現錄入資料拷貝到檔案的功能(將鍵盤錄入的資料拷貝到當前專案下的te.txt檔案中,錄入資料遇到quit時退出) package pra_16; import java.i

    PullScrollView(四)——完全使用listview實現回彈(方法一)

    在前面三篇中,我為大家展示了使用ScrollView實現下拉回彈的效果。但如果ScrollView裡如果巢狀使用ListView就可能會出現問題,因為兩者都會有滑動監聽。操作起來可能會起衝突,然後解決了衝突問題,到後面頁面效能也會很差強人意。即然如此,那我們就直接使用

    java.util包)——Connection接口

    操作 相同 元素 叠代 cat roo soft true nbsp Connection接口介紹   Connection接口是java集合的root接口,沒有實現類,只有子接口和實現子接口的各種容器。主要用來表示java集合這一大的抽象概念。   Connection接

    Javascript設計模式與開發實踐:策略模式) http://www.jianshu.com/p/ef53781f6ef2

    的人 思想 ram gis pan pro msg have 改變 上一章我們介紹了單例模式及JavaScript惰性單例模式應用這一次我主要介紹策略模式策略模式是定義一系列的算法,把它們一個個封裝起來,並且讓他們可以互相替換。比方說在現實中很多時候也有很多途徑到達同一個

    C++ 模板)(轉)

    創建 規則 error ++ 例如 public err iostream () 四、類模板的默認模板類型形參   1、可以為類模板的類型形參提供默認值,但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。   2、類模板的類型形

    mybatis )------入門實例(基於XML)

    ssi 開發模式 文件中 Coding import 拼接 upd baidu actor   通過上一小節,mybatis 和 jdbc 的區別:http://www.cnblogs.com/ysocean/p/7271600.html,我們對 mybatis有了一個大致

    OSI七層 數據鏈路層(DataLinkLayer)

    必須 sdl -s 通過 鏈路層 family ram style ora 一、簡介   在物理層提供比特流服務的基礎上,建立相鄰結點之間的數據鏈路,通過差錯控制提供數據幀(Frame)在信道上無差錯的傳輸,並進行各電路上的動作系列。    數據鏈路層在不可靠的物理介質上提

    Spring)------IOC控制反轉

    tsp name 調試 的人 好的 turn 同時 eth 時機   我相信提到 Spring,很多人會脫口而出IOC(控制反轉)、DI(依賴註入)、AOP等等概念,這些概念也是面試官經常問到的知識點。那麽這篇博客我們就來詳細的講解 IOC控制反轉。   ps:本篇博客源

    .Net AppDomain

    onf urn attach msdn 允許 cut isolation cal pst AppDomain 類 表示應用程序域,它是一個應用程序在其中執行的獨立環境。 此類不能被繼承。 命名空間: System程序集: mscorlib(位於 mscorlib.

    Ansible

    latest load 遠程 即使 centos fine oct syn srv Ansible系列命令 Ansible系列命令有如下: ansible:這個命令是日常工作中使用率非常高的命令之一,主要用於臨時一次性操作; ansible-doc:是Ansible模塊文

    Zookeeper):Zookeeper安裝

    zookeeper安裝安裝環境:CentOS 7 內存1GBJDK版本:1.8.0_112為JDK配置如下環境變量:編輯/etc/profile.d/jdk.sh#!/bin/bash JAVA_HOME=/usr/local/jdk1.8.0_112 export PATH=$JAVA_HOME/bi

    dns

    dns 子域授權 主從架構 bind 轉發區域 bind view dns的另外一種實現方式:dnsmasq較為簡單請自行理解一.主從架構 二.子域授權 三.轉發區域 四.bind中的安全相關配置 五.bind view視圖一.主從架構從s擁有和主s一樣的解析庫 註意:從服務器是

    Linux防火墻

    iptables netfilter linux防火墻詳解 nat dnat 一、iptables命令基本語法 二、iptables語法進階 三、iptables顯示擴展 四、iptables簡單案例 五、iptables之forward 六、iptables之NAT一、iptables命

    Redis

    redis sentinal redis集群 redis主從 redis事務 一、redis認證二、redis事務三、Connection&server相關命令四、Redis的發布與訂閱//充當mq的功能五、Redis持久化六、redis復制七、master-slave實現八、se

    05-Linux中DNS

    相關 proc gen lin 四種方法 .... rate 我的網站 四種 接“04-Linux中DNS詳解(一)” 六、在Linux上測試域名解析1、先檢查DNS是否設置正確 cat /etc/resolv.conf [resolv.conf] # Generate

    LAMP

    cgi www perl gre tar.gz emd init.d .html product 編譯安裝LAMP一、安裝mariadb二、安裝httpd三、php編譯四、apache的代理指令五、其他LAMP結合的方式 c---httpd[modules]

    RGB格式)--索引格式

    存儲 簡介 以及 關於 需要 色彩 顏色 針對 表現 本節針對RGB索引格式做簡單介紹,這些格式是比較老的格式,比較節省空間,在計算機發展的初期存儲的成本還是很高的,但是表現的色彩很有限,而隨著存儲成本的不斷降低,以及用戶越來越高的視覺體驗需求,這些格式也就基本被拋棄,不再

    編碼原理)---變換編碼

    mar 關於 詳解 tex 描述 原理 學習 編碼原理 watermark 本篇介紹編碼原理中的變換編碼。 一、變換的目的和作用: 變換編碼的作用是將空間域描述的圖像信號變換到頻率域,然後對變換後的系數進行編碼處理。一般來說,圖像在空間上具有較強的相關性,變換到頻率