ImageView之setImageDrawable

2,717 阅读2分钟

一、背景

在开发的时候需要为 ImageView 设置图片, 视图包含33 x 33的一个背景, 以及一个 20 x 20 的前景图片, 但是视觉切图大小为 24 x 24。背景我在xml中使用 background 来设置, 前景通过 setImageDrawable 方法进行设置, 并通过 Drawable 的 setBounds 方法进行了设置, 但是结果不生效。当对视图设置 padding 属性之后可以生效。

二、为什么setBounds 无效

查看 setImageDrawable 的源码发现内部调用了 updateDrawable, 进而调用了 configureBounds方法, 该方法会对 Drawable 进行重新设置, 因此导致我们自己设置的 Drawable 无法生效。

三、如何解决该问题

分析 configureBounds的实现, 主要包括以下几步, 在我的场景中进入 other 这里, 由于 scaleType 默认是 FIT_CENTER 因此最终的效果是图片放大充满 View, 所以想要固定图片为 20 x 20 可以通过设置 View 的 padding 来限制 dst rect 的范围。

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));
        }
    }
}

1. 计算 Drawable 的固有宽高 和 View 的可用宽高(移除padding)

  • drawable

    dwidth & dheight

  • view

    vwidth & vheight

    getWidth - paddingLeft - paddingRight

    getHeight - paddingTop - paddingBottom

2. 根据 scaleType 的取值调用 setBounds 以及构造 Matrix 矩阵

scaleType 的类型默认为 FIT_CENTER

  • FIT_XY 或者固有宽高小于等于0

    这种情况下会将 Drawable 拉伸至和 View 一样大小

    setBounds(0, 0, vwidth, vheight)

    mDrawMatrix = null

  • MATRIX

  • CENTER

    setBounds(0, 0, dwidth, dheight)

    mDrawMatrix.setTranslate((vwidth - dwidth) * 0.5, (vheight-dheight) * 0.5)

    可以看到只是将图片平移到了视图的中心位置, 并不涉及缩放的操作

  • CENTER_CORP

  • CENTER_INSIDE

    根据 Drawable的固有大小和View的大小计算缩放比

    // drawable 大小小于 View大小
    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);
    
  • others

    将Src拓展至Dst

    mTempSrc.set(0, 0, dwidth, dheight);
    mTempDst.set(0, 0, vwidth, vheight);
    mDrawMatrix = mMatrix;
    mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));