ImageView之ScaleType詳解及擴充

大頭呆發表於2017-12-06

ImageView中有個很重要也很常用的屬性android:scaleType,相信大家都應該不陌生,主要用於控制圖片在ImageView中顯示的樣式,比如顯示大小、顯示位置、顯示內容區域。當然也可以在程式碼中設定: setScaleType(ImageView.ScaleType.xxx); ScaleType的取值一共有8種:fitCenterfitEndcentercenterCropcenterInsidematrixfitXYfitStart。其中預設值是fitCenter。 我們先來看比較簡單的center吧,一個60dp*40dp的ImageView(為了顯示效果,背景設成藍色)裡面放了張20dp*20dp的圖片。上圖看看效果:

ImageView之ScaleType詳解及擴充

center的含義是:保持原圖的大小,顯示在ImageView的中心。當原圖的size大於ImageView的size,超過部分裁剪處理。 再來看比較“暴力”的fitXY

ImageView之ScaleType詳解及擴充

fitXY的含義:拉伸顯示圖片,不保持原比例,填滿ImageView.嗯,果然很暴力。

嗯,接下來我們...什麼?難道你以為我要把剩下的6種樣式都貼張圖出來,然後愉快地水一篇部落格嗎?

ImageView之ScaleType詳解及擴充

所以接下來我放大招了,開始分析原始碼!

詳解

上面設定了這麼多種ScaleType,那麼最終生效的位置在哪呢?答案就在 configureBounds()方法裡,限於篇幅我貼了上面兩種樣式的法,其他的樣式實現也都類似。

   private void configureBounds() {
        //圖片寬高
        final int dwidth = mDrawableWidth;
        final int dheight = mDrawableHeight;
         //Imageview寬高
        final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;//
        final int vheight = getHeight() - mPaddingTop - mPaddingBottom;

        final boolean fits = (dwidth < 0 || vwidth == dwidth)
                && (dheight < 0 || vheight == dheight);

        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
         
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            mDrawable.setBounds(0, 0, dwidth, dheight);
             if (ScaleType.MATRIX == mScaleType) {//樣式應用自定義MATRIX
                if (mMatrix.isIdentity()) {
                    mDrawMatrix = null;
                } else {
                    mDrawMatrix = mMatrix;
                }
            } 
         if (ScaleType.CENTER == mScaleType) {
                // Center bitmap in view, no scaling.
                mDrawMatrix = mMatrix;
                mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
                                         Math.round((vheight - dheight) * 0.5f));
            } 

複製程式碼

可以看到最終的實現用到了Drawable類和Matrix類的方法。前者大家都很熟悉,就是我們設定的要顯示的圖片,而Matrix叫做矩陣,是一個專門用來處理圖形變換中的重要工具類,熟悉自定義View的同學應該對它不陌生,canvas.setMatrix(matrix)可以做出很多獨特效果來。而我們今天的主角是ImageView,所以沒錯imageView也有這個方法:

imageView.setImageMatrix(matrix)

通過上述方法我們就能將自己定義的Matrix應用到ImageView中。可以看到原始碼,要使這個方法生效,ScaleType一定要設為MATRIX,不然執行完自己的變換後還是會執行樣式自己的變換,相當於沒執行。

FIT_XY的實現很簡單,就用到了setBounds(int left, int top, int right, int bottom),這個四引數指的是drawable的繪製區域。 center用到了postTranslate(float dx, float dy),將Drawable移到ImageView中間,因為沒有其他處理,圖片如果比ImageView大,那麼多餘的區域就會被裁剪掉,不顯示了。

擴充

說了這麼多來個小栗子來加深理解吧: 還是上面那張圖片,scaleType設為matrix,我們再加一個按鈕,點選事件如下:

    int m = 20;
    //平移
    private void onClick(View v) {
        Matrix matrix = new Matrix();
        matrix.postTranslate(m, m);
        imageView.setImageMatrix(matrix);
        m = m + 3;
    }
複製程式碼

來看看效果:

ImageView之ScaleType詳解及擴充

通過改變postTranslate引數的值我們就能改變圖片在View中繪製的起始位置。 當然不會這麼簡單。Matrix還有其他方法: postRotate(float degrees, float px, float py)//旋轉: 我們仿照center的處理將圖片移到ImageView中心,然後進行旋轉:

    int m = 20;
    //旋轉
    private void onClick(View v) {
       Matrix matrix = new Matrix();
        int width = imageView.getDrawable().getIntrinsicWidth();
        int height = imageView.getDrawable().getIntrinsicHeight();
        final int vwidth = imageView.getWidth();
        final int vheight = imageView.getHeight();
        matrix.setTranslate(Math.round((vwidth - width) * 0.5f),
                Math.round((vheight - height) * 0.5f));
        matrix.postRotate(m, vwidth * 0.5f, vheight * 0.5f);
        imageView.setImageMatrix(matrix);
        m = m + 3;
    }
複製程式碼

看看效果:

ImageView之ScaleType詳解及擴充

還有一種是錯切,將圖片的形狀改變: void setSkew(float kx, float ky) 實際用到的比較少,就不貼程式碼了,直接看效果吧:

ImageView之ScaleType詳解及擴充

總結

說了這麼多,這篇文章就是給大家介紹了android:scaleType的自定義用法,當遇到Imageview預設的顯示模式不能滿足我們現有需求的時候(比如我就遇到原圖按照原來的大小居底部顯示,如果原圖的大小超過了ImageView的大小,那麼就剪裁掉多餘部分,保留底部,和centerCrop裁剪四周有所不同),我們就可以動態設定自己的matrix來自定義顯示效果。當然如果你經常要用到這種方式的時候,可以繼承Imageview,封裝這些自定義樣式。

相關文章