Android自訂控制項

來源:互聯網
上載者:User

標籤:sdn   顯示   attr   縮放圖片   type   自己的   over   多少   開始   

我們在開發的過程中,有時會遇到一些Android系統內建的控制項解決不了我們的需求,比如說我們在開發項目時顯示的圖片輪播,當我們展示的時候不希望圖片變形,還要保證圖片能夠完整的顯示出來,我們如何做呢?如果只是一個簡單的ImageView控制項恐怕很難實現吧!有人會說ImageView的ScaleType屬性就能夠解決圖片填充不滿的問題,但是那樣的話圖片很容易失真,達不到產品原先的需求。首先我們來認識一下ImageView的ScaleType屬性的一些值。當我們查看ImageView的ScaleType的源碼時會發現ScaleType的一些枚舉值,也就是我們所用的屬性值

public static enum ScaleType {        CENTER,        CENTER_CROP,        CENTER_INSIDE,        FIT_CENTER,        FIT_END,        FIT_START,        FIT_XY,        MATRIX;        private ScaleType() {        }    }

那麼我們就按照枚舉值的順序來認識一下這些值如何使用。
android:scaleType=”center”
center就是置中,按圖片的原來大小置中顯示,當圖片長/寬超過View的長/寬,則截取圖片的置中部分顯示,目的是讓圖片置中。
android:scaleType=”centerCrop”
這個就是縮放圖片的一種,它的作用就是讓圖片等比例縮放,但是唯一美中不足的就是這個圖片的縮放會對圖片造成一定的裁剪(當設定的View的寬高和圖片的寬高比例不同的時候)。圖片依舊是置中顯示
android:scaleType=”centerInside”
將圖片的內容完整置中顯示,通過按比例縮小或原來的大小使得圖片長/寬等於或小於View的長/寬
android:scaleType=”fitCenter”
把圖片按比例擴大/縮小到View的寬度,置中顯示
android:scaleType=”fitEnd”
把圖片按比例擴大/縮小到View的寬度,顯示在View的下部分位置
android:scaleType=”fitStart”
把圖片按比例擴大/縮小到View的寬度,顯示在View的上部分位置
android:scaleType=”fitXY”
把圖片不按比例擴大/縮小到跟View的大小一致,這一種也是最容易產生圖片失真的
android:scaleType=”matrix”
用矩陣來繪製,動態縮小放大圖片來顯示
以上如下

介紹了上面的ScaleType以後下面就開始進入我們的正題,上面的屬性值恐怕有時候是滿足不了我們的需求吧,多多少少還是達不到圖片既不失真又要等比例顯示的效果吧。有人會說,我可以根據圖片的寬高然後設定控制項的寬高啊,這一種是可以解決圖片的失真和等比例顯示的效果,你直接用
`android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
這樣不是更好嗎?但是你想想這樣的話圖片會達到輪播圖的圖片效果嗎?輪播圖的圖片一般是寬度填充整個螢幕的,但是高度怎麼辦呢?有人說我可以根據自己手機的螢幕寬度/圖片的寬度得到的比值去乘以圖片的高度得到的值設定為控制項的高度就可以解決了。但是這又有一個問題,你這樣的話你的手機螢幕顯示的是非常完美的,但是如果到其它手機你能確保也一樣嗎?恐怕不行吧!那麼我們如何解決呢?我們如果想要完美的顯示在所有的手機上面就需要自訂一個控制項了。
Android自訂控制項需要一些自訂的屬性,我們需要在app\src\main\res\values下建立一個xml檔案名稱字為attrs也就是屬性檔案,在檔案裡面我們要使用declare-styleable來聲明一個我們自己的格式的一些屬性

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CustomLayout">        <attr name="test" format="string"></attr>    </declare-styleable></resources>

其中declare-styleable name=”CustomLayout”裡面的name值最好跟我們自訂控制項名相同,name=”test” format=”string”,name是根據自己的習慣來定但是要做到見名知意,就像layout_width讓人一看就知道就布局的寬一樣。format就是資料的格式,它有string 、float、boolean、color、dimension、enum、flag、fraction、integer、reference這10種格式,本次只用到float這種格式。因為要實現圖片等比例縮放需要用到一個比例內容如下

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CustomLayout">        <attr name="ratio" format="float"></attr>    </declare-styleable></resources>

自訂控制項的時候需要重寫onMeasure方法,onMeasure是用來計算控制項的寬高的,當我們知道控制項的寬度或高度想要根據比例設定高度或寬度的時候,就需要在此方法中進行測量計算。在onMeasure方法中,我們會看到兩個參數onMeasure(int widthMeasureSpec, int heightMeasureSpec),onMeasure傳入的兩個參數是由上一層控制項傳入的大小,有多種情況,重寫該方法時需要對計算控制項的實際大小,然後調用setMeasuredDimension(int, int)設定實際大小。widthMeasureSpec其實就是width的寬度值與寬度的模式,說到模式大家都應該很熟悉的,有三種模式分別是:MeasureSpec.AT_MOST、MeasureSpec.EXACTLY、MeasureSpec.UNSPECIFIED。當模式為MeasureSpec.AT_MOST時則意味著控制項大小一般隨著控制項的子空間或內容進行變化,此時控制項尺寸只要不超過父控制項允許的最大尺寸即可,我們經常使用的wrap_content就是這種模式。當模式為MeasureSpec.EXACTLY時,說明控制項的大小為精確值,是我們已知的大小的時候可以設定此模式,我們經常使用的match_parent就是這種模式。當模式為MeasureSpec.UNSPECIFIED時,說明控制項的大小不確定,控制項的大小是可以隨便變化的,我們通常使用的父控制項為Adapter的就是通過傳入的大小來確定的就是這種模式。那麼widthMeasureSpec裡面的值又是什麼情況呢,下面是我列印的一個當控制項的寬度為match_parent也就是模式為MeasureSpec.EXACTLY時的widthMeasureSpec的值

10-28 12:55:50.785 5350-5350/custom.lyxrobert.com.customcontrols I/System.out: widthMeasureSpec------>1073742904

那麼1073742904是什麼意思呢?

其中的10000111000?轉換成10進位就是1080也就是My Phone的屏寬,那麼?01000000000000000000010000111000?前面的又是什麼情況呢?

    public static final int AT_MOST = -2147483648;    public static final int EXACTLY = 1073741824;    public static final int UNSPECIFIED = 0;

1073741824的二進位則為?01000000000000000000000000000000?看到這裡相比大家也就知道怎麼回事了吧。
那麼我們就可以根據widthMeasureSpec來擷取我們想要的寬度方法則是MeasureSpec.getSize(widthMeasureSpec),高度也是如此不過傳入的參數就是heightMeasureSpec。
如果我們知道寬度和高度也是不行的,如果使用者想要給圖片設定一定的padding值怎麼辦?比如說padding為10,那麼圖片的內邊距都會為10,圖片以前的比例就很難保持。所以我們為防止這種情況的發生還是通過擷取相應的padding值來進行處理。圖片的寬度則為int img_width=width - getPaddingLeft() - getPaddingRight();通過寬度/計算出的圖片的寬高比得到圖片的高度int img_height = (int) (imageWidth / ratio + 0.5f);但是這樣還是不行,因為圖片寬度左右各減少10,而高度雖然也等比例縮小了,但是顯示出來的效果恐怕只有圖片的左右的間距與上下的會有不同的效果

圖片沒有設定padding時

圖片設定padding為20dp時

我們發現圖片的上下的邊距並不是我們所設定的,那麼如果達到這種效果呢,就是需要根據我們擷取的img_height 的值再加上得到top和bottom的padding值height = img_height + getPaddingTop() + getPaddingBottom();設定之後的圖片顯示為

package custom.lyxrobert.com.customcontrols;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.widget.FrameLayout;/** * Created by ytx on 2016/10/28. */public class CustomLayout extends FrameLayout {    private float ratio;    public CustomLayout(Context context) {        super(context);    }    public CustomLayout(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.CustomLayout);        ratio = ta.getFloat(R.styleable.CustomLayout_rario, -1);        ta.recycle();    }    public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int width = MeasureSpec.getSize(widthMeasureSpec);// 擷取寬度值        int widthMode = MeasureSpec.getMode(widthMeasureSpec);// 擷取寬度模式        int height;        int heightMode = MeasureSpec.getMode(heightMeasureSpec);// 擷取高度模式        if (widthMode == MeasureSpec.EXACTLY                && heightMode != MeasureSpec.EXACTLY && ratio > 0) {            int img_width  = width - getPaddingLeft() - getPaddingRight();            int img_height  = (int) (img_width / ratio + 0.5f);            height = img_height  + getPaddingTop() + getPaddingBottom();            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,                    MeasureSpec.EXACTLY);        }        // 按照最新的高度測量控制項        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }}

布局檔案為,如果想給給圖片添加邊距請在custom.lyxrobert.com.customcontrols.CustomLayout裡面添加,不要在ImageView裡面設定,否則將不會起作用,因為我們的計算是在CustomLayout裡面的onMeasure方法裡面實現的。lyxrobert:rario=”1.7”裡面的1.7是圖片的寬和高的像素比

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:lyxrobert="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    >    <custom.lyxrobert.com.customcontrols.CustomLayout    android:layout_width="match_parent"    android:layout_height="wrap_content"    lyxrobert:rario="1.7">    <ImageView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:src="@drawable/test"         /></custom.lyxrobert.com.customcontrols.CustomLayout></LinearLayout>

點擊免費下載源碼
如有疑問請留言

Android自訂控制項

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.