1. 程式人生 > >這才是真正的萬能圓角ImageView

這才是真正的萬能圓角ImageView

本文已授權我的公眾號:我就是馬雲飛 獨家釋出

不知道有沒有人記得我去年寫過一個圓角的imageview。不知道的可以先去看看:萬能圓角imagview,本文是基於上一篇的內容進行新增以及修改的。不然直接看這篇可能會有點懵。

前言

我為什麼要二次封裝?最近公司有個需求是這樣的。
這裡寫圖片描述

同事說,不知道怎麼搞,於是乎,我把之前寫的imageview給過去了。他來了句,你這圓角和fitxy同時設定會有問題啊,我反手就是一個大嘴巴子。我的程式碼怎麼會有問題。於是,拿來一瞧,的確有點問題。So,我決定對這個imageview進行二次的封裝。(當然了,這個問題的最後處理是後臺直接給一個圓角的imageview)。

如何實現

細想一下,上文我們是怎麼做的,我們是把繪製的區域,從(0,0)移動到我們想要的地方,說個粗暴點的話,我們強制的把這個imagview的scaletype的屬性設定了centercrop。那麼這次我們就要將它的scaletype設定成可調的屬性。

實現邏輯

我前面也說過了,上次我們是根據imageview的原始碼來修改他的編輯區域的,這次,我們照常開啟原始碼,找到園中對scaletype的處理邏輯,程式碼如下:


    private void configureBounds() {
        if (mDrawable == null || !mHaveFrame) {
            return
; } final int dwidth = mDrawableWidth; final int dheight = mDrawableHeight; 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) { /* If the drawable has no intrinsic size, or we're told to scaletofit, then we just fill our entire view. */ mDrawable.setBounds(0, 0, vwidth, vheight); mDrawMatrix = null; } else { // We need to do the scaling ourself, so have the drawable // use its native size. mDrawable.setBounds(0, 0, dwidth, dheight); if (ScaleType.MATRIX == mScaleType) { // Use the specified matrix as-is. if (mMatrix.isIdentity()) { mDrawMatrix = null; } else { mDrawMatrix = mMatrix; } } else if (fits) { // The bitmap fits exactly, no transform needed. mDrawMatrix = null; } else 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)); } else if (ScaleType.CENTER_CROP == mScaleType) { mDrawMatrix = mMatrix; float scale; float dx = 0, dy = 0; if (dwidth * vheight > vwidth * dheight) { scale = (float) vheight / (float) dheight; dx = (vwidth - dwidth * scale) * 0.5f; } else { scale = (float) vwidth / (float) dwidth; dy = (vheight - dheight * scale) * 0.5f; } mDrawMatrix.setScale(scale, scale); mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy)); } else if (ScaleType.CENTER_INSIDE == mScaleType) { mDrawMatrix = mMatrix; float scale; float dx; float dy; if (dwidth <= vwidth && dheight <= vheight) { scale = 1.0f; } else { scale = Math.min((float) vwidth / (float) dwidth, (float) vheight / (float) dheight); } dx = Math.round((vwidth - dwidth * scale) * 0.5f); dy = Math.round((vheight - dheight * scale) * 0.5f); mDrawMatrix.setScale(scale, scale); mDrawMatrix.postTranslate(dx, dy); } else { // Generate the required transform. mTempSrc.set(0, 0, dwidth, dheight); mTempDst.set(0, 0, vwidth, vheight); mDrawMatrix = mMatrix; mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType)); } } }

我們找到上次對其實位置修改的地方。跟著原始碼一樣改成一樣。改完之後程式碼如下:


        if (drawablewidth <= 0 || drawableheight <= 0) {
            drawable.setBounds(0, 0, viewwidth, viewheight);
            matrix = null;
        } else {
            drawable.setBounds(0, 0, drawablewidth, drawableheight);
            if (ScaleType.MATRIX == getScaleType()) {
                if (matrix.isIdentity()) {
                    matrix = null;
                }
            } else if (fits) {
                matrix = null;
            } else if (ScaleType.CENTER == getScaleType()) {
                matrix.setTranslate(Math.round((viewwidth - drawablewidth) * 0.5f),
                        Math.round((viewheight - drawableheight) * 0.5f));
            } else if (ScaleType.CENTER_CROP == getScaleType()) {
                if (drawablewidth * viewheight > viewwidth * drawableheight) {
                    dx = (viewwidth - drawablewidth * scale) * 0.5f;
                } else {
                    dy = (viewheight - drawableheight * scale) * 0.5f;
                }
                matrix.setScale(scale, scale);
                matrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
            } else if (ScaleType.CENTER_INSIDE == getScaleType()) {

                if (drawablewidth <= viewwidth && drawableheight <= viewheight) {
                    scale = 1.0f;
                } else {
                    scale = Math.min((float) viewwidth / (float) drawablewidth,
                            (float) viewheight / (float) drawableheight);
                }
                dx = Math.round((viewwidth - drawablewidth * scale) * 0.5f);
                dy = Math.round((viewheight - drawableheight * scale) * 0.5f);
                matrix.setScale(scale, scale);
                matrix.postTranslate(dx, dy);
            } else {
                if (drawablewidth * viewheight > viewwidth * drawableheight) {
                    dx = (viewwidth - drawablewidth * scale) * 0.5f;
                } else {
                    dy = (viewheight - drawableheight * scale) * 0.5f;
                }
                matrix.setScale(scale, scale);
                matrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
            }
        }
        if (ScaleType.FIT_XY == getScaleType() && matrix != null) {
            scale1 = viewwidth * 1.0f / drawablewidth;
            scale2 = viewheight * 1.0f / drawableheight;
            matrix.setScale(scale1, scale2);
        }
        bitmapShader.setLocalMatrix(matrix);
        paint.setShader(bitmapShader);

我們可以發現其實和原始碼對比下來,改動還是有的。為什麼呢?我們仔細看下這段程式碼:

     if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
            /* If the drawable has no intrinsic size, or we're told to
                scaletofit, then we just fill our entire view.
            */
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        }

原始碼裡面只對drawable進行了處理。但我們可以發現後面的判斷每次都會呼叫matrix.setScale這個方法。但在當scaletype為fitxy時,沒有進行處理。我們也知道,我們看的是原始碼,他肯定偷偷的在某個地方進行了處理。那麼我們要處理怎麼辦呢?仔細看程式碼:

        if (ScaleType.FIT_XY == getScaleType() && matrix != null) {
            scale1 = viewwidth * 1.0f / drawablewidth;
            scale2 = viewheight * 1.0f / drawableheight;
            matrix.setScale(scale1, scale2);
    }

fitxy我們都知道是充滿布局,然後在細看這塊程式碼,你是不是懂了呢?佈局的寬高除以圖片的寬高。然後設定它的比例。

加上邊框

為了更好的封裝,我選擇加上邊框和邊框顏色的自定義屬性。那麼接下來就是直接上程式碼了。
我們需要再定義一個畫筆:

        boder_paint.setAntiAlias(true);
        boder_paint.setStyle(Paint.Style.STROKE);
        boder_paint.setColor(border_color);
        boder_paint.setStrokeWidth(border_width);

接下來我們就是直接畫上去了。當然了,我們這邊預設是不設定,也就是borderwidth為0,所以我們要加一個判斷:

  if (type == TYPE_ROUND) {
            canvas.drawRoundRect(rectF, borderRadius, borderRadius,paint);
            if (border_width > 0) {
                canvas.drawRoundRect(rectF, borderRadius, borderRadius, boder_paint);
            }
        } else if (type == TYPE_CIRCLE) {
            canvas.drawCircle(radius, radius, radius, paint);
            if (border_width > 0) {
                canvas.drawCircle(radius, radius, radius, boder_paint);
            }
        } else {
            getDrawable().draw(canvas);
        }

這裡寫圖片描述
我們一編譯,一執行,效果炸了。你問我為什麼?我們先來看個效果在說把。
這裡寫圖片描述
我們發現我們修改的fitxy屬性已經生效了。但是,為什麼加了邊框是這樣呢?
仔細想想。我們畫圓角和圓的時候是不是忘記去掉了邊框的寬度呢?那麼我們既然找到了原因就可以找到解決方法了。那我們就直接去掉邊框的高度,注意!!圓角和圓的都要處理。

   canvas.drawCircle(radius, radius, radius, paint);
            if (border_width > 0) {
                canvas.drawCircle(radius, radius, radius - border_width / 2, boder_paint);
            }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (type == TYPE_ROUND) {
            rectF = new RectF(border_width/2,border_width/2, getWidth()- border_width / 2, getHeight()- border_width / 2);
        }
    }

我們再來看下效果:
這裡寫圖片描述
搞定。完美~

使用

在gradle加上如下程式碼:

compile 'com.angel:SWImageView:1.0.0' 

關於自定義屬性:

<declare-styleable name="SWImageView">
        <attr name="borderRadius" format="dimension"/>
        <attr name="type">
            <enum name="normal" value="-1"/>
            <enum name="circle" value="0"/>
            <enum name="round" value="1"/>
        </attr>
        <attr name="borderWidth" format="dimension" />
        <attr name="borderColor" format="integer" />
    </declare-styleable>