1

在新版本的 gmail 中,有一个很酷的 imageView,其中显示了多个联系人图像(例如链接这里)。

例如,如果有人给我发了一封电子邮件,我只会看到他的图片:

#######
#     #
#  A  #
#     #
#######

如果我回复了他,我可以在旁边看到我的图像,但我的图像和他的图像都减半并共享 imageView 的相同空间(我认为两者都有 scaleType 作为中心裁剪):

#######
#  #  #
# A# B#
#  #  #
#######

如果另一个人加入了对话,它可能如下所示:

#######
#  # B#
# A####
#  # C#
#######

如果另一个人加入了,它可能看起来像这样:

#######
# A# C#
#######
# B# D#
#######

我不确定项目的顺序(和规则,所以这里的一切都是我的猜测),以及当更多人加入时会发生什么。

重要的是我想知道如何实现这一点。

有谁知道这个的解决方案?他们是怎么做到的?使用了哪个视图?

它肯定是一个自定义视图,但最好的方法是什么?一种可能是最有效且不使用大量内存的方式...

我什至可能想让最终图像变圆,所以处理位图而不是 imageView 可能会更好......

我什至不知道如何称呼这种观点。我想到了“拼贴视图”或“马赛克视图”。

为了清楚起见,我认为应该使用下一个 API 来处理这样的问题:

public static Bitmap createMosaicOfBitmaps(int targetWidth,int targetHeight,ArrayList<Bitmap> imagesToShow)

或者,如果位图可能占用太多内存,我们可以使用类似:

public static Bitmap createMosaicOfBitmaps(int targetWidth,int targetHeight,ArrayList<LazyBitmap> imagesToShow)

/**interface for lazy loading of a bitmap, while downscaling the bitmap to the needed size*/
public interface LazyBitmap{
   public getBitmap(int width,int height);
}

我想出了2个解决方案,每个都有自己的优点和缺点,但我仍然需要对最终结果进行特殊效果(尤其是圆角,但也许还有其他东西),这是我不做的事情知道该怎么做。

有人可以帮忙吗?你认为谷歌在他们的应用上使用了什么?


编辑:我想出了一些可能的解决方案,对于每一个我都写了这个线程的答案。我不确定哪个是最好的,所以我把它们都贴出来了。我想每个人都有自己的优点和缺点。

我目前的解决方案都没有像我提供的那样处理位图,但它们非常直观......

我仍然希望就您认为应该如何做到这一点提出一些建议。

4

4 回答 4

4

这是我称之为的解决方案:

XML 解决方案

它使用 XML 来设置镶嵌视图的外观。仍然不是我计划的那样,但它可能会帮助一些需要这样的东西并能够以他们想要的方式改变它的人。

我添加的是添加自定义分隔线的能力(为此使用 actionBarSherlock 中的 IcsLinearLayout)。当然,你可以添加任何你想要的......

这是代码:

public class MosaicView extends FrameLayout {

    public static final int SHOW_DIVIDER_NONE = 0;
    public static final int SHOW_DIVIDER_OUTER = 0x01;
    public static final int SHOW_DIVIDER_INNER = 0x02;

    private ImageView mTopLeftImageView, mTopRightImageView, mBottomRightImageView, mBottomLeftImageView;
    private IcsLinearLayout mLeftContainer, mRightContainer, mMainContainer;
    private int mShowDivider;
    private Drawable mHorizontalDividerDrawable;
    private Drawable mVerticalDividerDrawable;

    public MosaicView(final Context context) {
        super(context);
        init(context, null, 0);
    }

    public MosaicView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public MosaicView(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(final Context context, final AttributeSet attrs, final int defStyle) {
        removeAllViews();
        final LayoutInflater inflater = LayoutInflater.from(context);
        inflater.inflate(R.layout.mosaic_view, this, true);
        mTopLeftImageView = (ImageView) findViewById(R.id.mosaicView__topLeftImageView);
        mTopRightImageView = (ImageView) findViewById(R.id.mosaicView__topRightImageView);
        mBottomLeftImageView = (ImageView) findViewById(R.id.mosaicView__bottomLeftImageView);
        mBottomRightImageView = (ImageView) findViewById(R.id.mosaicView__bottomRightImageView);
        mLeftContainer = (IcsLinearLayout) findViewById(R.id.mosaicView__leftContainer);
        mRightContainer = (IcsLinearLayout) findViewById(R.id.mosaicView__rightContainer);
        mMainContainer = (IcsLinearLayout) findViewById(R.id.mosaicView__mainContainer);
        //
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MosaicView, defStyle, 0);
        final int attributeCount = a.getIndexCount();
        for (int i = 0; i < attributeCount; i++) {
            final int curAttr = a.getIndex(i);
            switch (curAttr) {
            case R.styleable.MosaicView_mosaicVerticalDividerDrawable:
                setVerticalDividerDrawable(a.getDrawable(curAttr));
                break;
            case R.styleable.MosaicView_mosaicHorizontalDividerDrawable:
                setHorizontalDividerDrawable(a.getDrawable(curAttr));
                break;
            case R.styleable.MosaicView_mosaicShowDividers:
                setShowDivider(a.getInt(curAttr, SHOW_DIVIDER_NONE));
                break;
            }
        }
        a.recycle();
        //
        if (!isInEditMode())
            resetAllImageViews();
        else {
            final ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
            for (int i = 0; i < 4; ++i)
                bitmaps.add(BitmapFactory.decodeResource(getResources(), android.R.drawable.sym_def_app_icon));
            setImages(bitmaps);
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void setVerticalDividerDrawable(final Drawable drawable) {
        mVerticalDividerDrawable = drawable;
        mMainContainer.setDividerDrawable(drawable);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void setHorizontalDividerDrawable(final Drawable drawable) {
        mHorizontalDividerDrawable = drawable;
        mLeftContainer.setDividerDrawable(drawable);
        mRightContainer.setDividerDrawable(drawable);
    }

    public Drawable getVerticalDividerDrawable() {
        return this.mVerticalDividerDrawable;
    }

    public Drawable getHorizontalDividerDrawable() {
        return this.mHorizontalDividerDrawable;
    }

    public int getShowDivider() {
        return this.mShowDivider;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void setShowDivider(final int dividers) {
        mShowDivider = dividers;
        int containersDividers = IcsLinearLayout.SHOW_DIVIDER_NONE;
        if ((dividers & SHOW_DIVIDER_INNER) != 0)
            containersDividers |= IcsLinearLayout.SHOW_DIVIDER_MIDDLE;
        if ((dividers & SHOW_DIVIDER_OUTER) != 0)
            containersDividers |= IcsLinearLayout.SHOW_DIVIDER_END | IcsLinearLayout.SHOW_DIVIDER_BEGINNING;
        mLeftContainer.setShowDividers(containersDividers);
        mRightContainer.setShowDividers(containersDividers);
        mMainContainer.setShowDividers(containersDividers);
    }

    private void resetAllImageViews() {
        mTopLeftImageView.setImageResource(0);
        mTopRightImageView.setImageResource(0);
        mBottomLeftImageView.setImageResource(0);
        mBottomRightImageView.setImageResource(0);
        mTopLeftImageView.setVisibility(View.GONE);
        mTopRightImageView.setVisibility(View.GONE);
        mBottomLeftImageView.setVisibility(View.GONE);
        mBottomRightImageView.setVisibility(View.GONE);
        mLeftContainer.setVisibility(View.GONE);
        mRightContainer.setVisibility(View.GONE);
    }

    public void setImages(final ArrayList<Bitmap> images) {
        resetAllImageViews();
        if (images == null || images.size() == 0)
            return;
        switch (images.size()) {
        case 1:
            mTopLeftImageView.setImageBitmap(images.get(0));
            mTopLeftImageView.setVisibility(View.VISIBLE);
            mLeftContainer.setVisibility(View.VISIBLE);
            break;
        case 2:
            mTopLeftImageView.setImageBitmap(images.get(0));
            mTopRightImageView.setImageBitmap(images.get(1));
            mTopLeftImageView.setVisibility(View.VISIBLE);
            mTopRightImageView.setVisibility(View.VISIBLE);
            mLeftContainer.setVisibility(View.VISIBLE);
            mRightContainer.setVisibility(View.VISIBLE);
            break;
        case 3:
            mTopLeftImageView.setImageBitmap(images.get(0));
            mTopRightImageView.setImageBitmap(images.get(1));
            mBottomRightImageView.setImageBitmap(images.get(2));
            mBottomRightImageView.setVisibility(View.VISIBLE);
            mTopLeftImageView.setVisibility(View.VISIBLE);
            mTopRightImageView.setVisibility(View.VISIBLE);
            mLeftContainer.setVisibility(View.VISIBLE);
            mRightContainer.setVisibility(View.VISIBLE);
            break;
        default:
            // TODO handle case of more than 4 images
        case 4:
            mTopLeftImageView.setImageBitmap(images.get(0));
            mTopRightImageView.setImageBitmap(images.get(1));
            mBottomRightImageView.setImageBitmap(images.get(2));
            mBottomLeftImageView.setImageBitmap(images.get(3));
            mBottomLeftImageView.setVisibility(View.VISIBLE);
            mBottomRightImageView.setVisibility(View.VISIBLE);
            mTopLeftImageView.setVisibility(View.VISIBLE);
            mTopRightImageView.setVisibility(View.VISIBLE);
            mLeftContainer.setVisibility(View.VISIBLE);
            mRightContainer.setVisibility(View.VISIBLE);
            break;
        }
    }

}

马赛克视图.xml:

<com.actionbarsherlock.internal.widget.IcsLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mosaicView__mainContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".MainActivity" >

    <com.actionbarsherlock.internal.widget.IcsLinearLayout
        android:id="@+id/mosaicView__leftContainer"
        android:layout_width="0px"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical" >

        <ImageView
            android:id="@+id/mosaicView__topLeftImageView"
            android:layout_width="match_parent"
            android:layout_height="0px"
            android:layout_weight="1"
            android:scaleType="centerCrop"
            android:src="@android:drawable/sym_def_app_icon" />

        <ImageView
            android:id="@+id/mosaicView__bottomLeftImageView"
            android:layout_width="match_parent"
            android:layout_height="0px"
            android:layout_weight="1"
            android:scaleType="centerCrop"
            android:src="@android:drawable/sym_def_app_icon" />
    </com.actionbarsherlock.internal.widget.IcsLinearLayout>

    <com.actionbarsherlock.internal.widget.IcsLinearLayout
        android:id="@+id/mosaicView__rightContainer"
        android:layout_width="0px"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical" >

        <ImageView
            android:id="@+id/mosaicView__topRightImageView"
            android:layout_width="match_parent"
            android:layout_height="0px"
            android:layout_weight="1"
            android:scaleType="centerCrop"
            android:src="@android:drawable/sym_def_app_icon" />

        <ImageView
            android:id="@+id/mosaicView__bottomRightImageView"
            android:layout_width="match_parent"
            android:layout_height="0px"
            android:layout_weight="1"
            android:scaleType="centerCrop"
            android:src="@android:drawable/sym_def_app_icon" />
    </com.actionbarsherlock.internal.widget.IcsLinearLayout>

</com.actionbarsherlock.internal.widget.IcsLinearLayout>

attr.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <declare-styleable name="MosaicView">
        <attr name="mosaicVerticalDividerDrawable" format="reference" />
        <attr name="mosaicHorizontalDividerDrawable" format="reference" />
        <attr name="mosaicShowDividers">
            <flag name="none" value="0x00" />
            <flag name="outer" value="0x01" />
            <flag name="inner" value="0x02" />
        </attr>
    </declare-styleable>

</resources>
于 2013-06-27T19:42:14.913 回答
3

这是我喜欢打电话的解决方案

viewGroup 解决方案

可悲的是,它使用了多个 imageViews,并且没有最终的位图可以处理。

请,如果有人知道显示图像的好方法,请将其发布。

这是代码:

public class MosaicView extends ViewGroup {

    private ArrayList<Bitmap> mImages;
    private ImageView[] mImageViews;

    public MosaicView(final Context context) {
        super(context);
    }

    public MosaicView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public MosaicView(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setImages(final ArrayList<Bitmap> images) {
        this.mImages = images;
        removeAllViews();
        mImageViews = new ImageView[Math.min(4, mImages.size())];
        for (int i = 0; i < mImageViews.length; ++i) {
            ImageView imageView;
            imageView = mImageViews[i] = new ImageView(getContext());
            imageView.setImageBitmap(mImages.get(i));
            imageView.setScaleType(ScaleType.CENTER_CROP);
            addView(mImageViews[i]);
        }
        invalidate();
    }

    @Override
    protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
        if (!changed)
            return;
        final int width = r - l;
        final int height = b - t;
        if (mImageViews != null)
            switch (mImageViews.length) {
            case 0:
                break;
            case 1:
                // all area
                mImageViews[0].layout(0, 0, width, height);
                break;
            case 2:
                // left
                mImageViews[0].layout(0, 0, width / 2, height);
                // right
                mImageViews[1].layout(width / 2, 0, width, height);
                break;
            case 3:
                // left
                mImageViews[0].layout(0, 0, width / 2, height);
                // right top
                mImageViews[1].layout(width / 2, 0, width, height / 2);
                // right bottom
                mImageViews[2].layout(width / 2, height / 2, width, height);
                break;
            default:
                // TODO think what should be done when more than 4 items should be shown
            case 4:
                // left top
                mImageViews[0].layout(0, 0, width / 2, height / 2);
                // right top
                mImageViews[1].layout(width / 2, 0, width, height / 2);
                // right bottom
                mImageViews[2].layout(width / 2, height / 2, width, height);
                // left bottom
                mImageViews[3].layout(0, height / 2, width / 2, height);
                break;
            }
    }

}
于 2013-06-27T19:33:43.103 回答
2

我建议您像您希望他们在街区中一样扩展ViewGroup和布置您的孩子。通过这样做,我实现了类似的目标。您可以指定将根据每个块中的图像数量确定布局的参数。您的父母将指定您孩子的大小和位置。因此,例如,如果您要在父项中显示 2 个项目,则父项会看到并为一个子项测量块宽度的一半,为另一个子项测量块宽度的一半,然后父项将定位子项,以便它们显示正确。

对于您的孩子,您可以扩展ImageView并使用采样位图填充它。这将减少内存使用,并且您将能够在父级中使用多个图像块。如果您的图像已下载,我建议您创建一个AsyncTask为您完成所有工作的图像,然后ImageView Bitmap在采样等完成后更新。ImageView当您在ListView. 当在父母中执行时,您孩子的大小显然将由父母决定onMeasure

然后,您可以使用您创建的自定义视图并在 ListView 中实现它以获得所需的效果

你可以看看这个这个这个来帮助你开始

- - - 编辑 - - -

是我实现的控件的屏幕截图。这并不完全相同,但它具有相同的方法和原则。在此控件中,我的父母(全屏)是包含图像的小块,而我的孩子(彩色块)是您的图像。现在,在您的孩子身上,您可以做任何事情来达到预期的效果。您可以onTouch在每个孩子上实现事件,为每个孩子添加动画等。如果您正确实现父子结构,则可能性是无穷无尽的。

在上面的示例屏幕截图中,这就是我在ViewGroup父级中布置孩子的方式

@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {

    int childCount = getChildCount();
    final int childWidth = _viewWidth;
    final int childHeight = _viewHeight;
    final int hPadding = (int) _paddingW; //set horizontal padding
    final int vPadding = (int) _paddingH; //set vertical padding

    if (childCount > 0) {

        int rowTop = 0;
        int rowBottom = 1;
        int columnCount = 1;
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);

            int childLeft   = (columnCount != 1) ? (hPadding * columnCount) + (childWidth * (columnCount-1)) : hPadding;
            int childRight  = (columnCount != 1) ? (hPadding * columnCount) + childWidth * columnCount : hPadding + childWidth;
            int childTop    = (rowTop == 0) ? vPadding : vPadding + ((childHeight + vPadding) * rowTop);
            int childBottom = (rowBottom == 1) ? vPadding + childHeight : (childHeight + vPadding) * rowBottom;

            child.layout(childLeft, childTop, childRight, childBottom);

            if (columnCount < BLOCK_COUNT) {
                columnCount++;
            } else {
                rowTop++;
                rowBottom++;
                columnCount = 1;
            }
        }
    }

}

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int desiredWidth = 100;
    int desiredHeight = 100;

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;
    int height;
    int maxHeight = 0;

    if (widthMode == MeasureSpec.EXACTLY) {
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        width = Math.min(desiredWidth, widthSize);
    } else {
        width = desiredWidth;
    }

    if (heightMode == MeasureSpec.EXACTLY) {
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        height = Math.min(desiredHeight, heightSize);
    } else {
        height = desiredHeight;
    }

    setMeasuredItemDimentions(width, height);

    final int childWidth = _viewWidth;
    final int childHeight = _viewHeight;
    final int vPadding = (int) _paddingH; //set vertical padding
    final int count = getChildCount();
    int columnCount = 1;
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() == GONE) {
            continue;
        }

        child.measure(childWidth, childHeight);

        if (columnCount < BLOCK_COUNT) {
            columnCount++;
        } else {
            maxHeight += childHeight + vPadding;
            columnCount = 1;
        }
    }

    if (count % BLOCK_COUNT != 0) maxHeight += childHeight + vPadding;

    maxHeight += vPadding;

    setMeasuredDimension(width, maxHeight);

}

此布局将仅显示 2 列但无限数量的行,因此它不会像您希望的那样百分百工作,但您可以使用类似的方法。

这是我孩子的一个例子

public class Block extends ViewGroup {

    private static final String TAG = Block.class.getSimpleName();

    private String _text;
    private State _state;
    private Context _context;

    private int _viewWidth;
    private int _viewHeight;
    private int _textSize;

    public enum State {
        GOOD, NEAR, PASSED;
    }

    public Block(Context context) {
        super(context);

        _context = context;
        _textSize = 15;
        TextView tx = new TextView(context);
        tx.setTextColor(context.getResources().getColor(R.color.terminal_text_color));
        tx.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        tx.setGravity(Gravity.CENTER);
        tx.setTypeface(null, Typeface.BOLD);
        addView(tx);

        TextView stateText = new TextView(context);
        stateText.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        stateText.setTextSize(18);
        stateText.setGravity(Gravity.CENTER);
        stateText.setTextColor(context.getResources().getColor(R.color.terminal_text_color));
        stateText.setGravity(Gravity.CENTER);

        addView(stateText);
    }

    @Override
    public void onLayout(boolean changed, int left, int top, int right, int bottom) {

        int childCount = getChildCount();
        final int childWidth = _viewWidth;
        final int childHeight = _viewHeight;

        if (childCount > 0) {

            TextView child = (TextView) getChildAt(0);
            int padding = (int) (childWidth * 0.05);

            int childLeft   = padding;
            int childRight  = childWidth - padding;
            int childTop    = padding;
            int childBottom = (int) (childHeight * 0.5);

            if (child != null) {
                child.layout(childLeft, childTop, childRight, childBottom);
                child.setText(_text);
                child.setTextSize(_textSize);
            }

            TextView stateText = (TextView) getChildAt(1);

            if (stateText != null) {
                stateText.layout(padding, ((int) (childHeight * 0.75)), childWidth - padding, ((int) (childHeight * 0.95)));

                if (stateText != null)
                    switch (_state) {
                        case GOOD:
                            stateText.setBackgroundColor(_context.getResources().getColor(R.color.google_green));
                            stateText.setText(_context.getResources().getString(R.string.bottom_bar_legend_good));
                            break;
                        case NEAR:
                            stateText.setBackgroundColor(_context.getResources().getColor(R.color.google_yellow));
                            stateText.setText(_context.getResources().getString(R.string.bottom_bar_legend_mild));
                            break;
                        case PASSED:
                            stateText.setBackgroundColor(_context.getResources().getColor(R.color.google_red));
                            stateText.setText(_context.getResources().getString(R.string.bottom_bar_legend_passed));
                            break;
                    }
            }

        }

    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        _viewWidth = widthMeasureSpec;
        _viewHeight = heightMeasureSpec;

        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);

        int padding = (int) (widthMeasureSpec * 0.05);
        TextView child = (TextView) getChildAt(0);
        if (child != null) child.measure(widthMeasureSpec - ((int)(widthMeasureSpec * 0.1)), heightMeasureSpec - ((int)(widthMeasureSpec * 0.5)) - padding);

        TextView childLayout = (TextView) getChildAt(1);
        if (childLayout != null) childLayout.measure(widthMeasureSpec - ((int)(widthMeasureSpec * 0.1)), heightMeasureSpec);

    }
}

ViewGroup为我的孩子使用了 a,因为我的要求与您的不同,但您可以使用简单的ImageView,因为您只想显示操纵的位图。您可以使用此方法在子项中为您的位图圆角(正如您在评论中提到的那样)。

希望这可以帮助

于 2013-06-26T09:19:43.750 回答
2

这是我称之为的解决方案:

imageView 解决方案

它从 ImageView 扩展而来,并覆盖其 onDraw 方法。它工作正常,但它有一些缺点,如果有人可以改进,我会很高兴:

  1. 它不对位图进行操作。
  2. 我不知道如何对我扩展的 imageView 执行特殊操作,例如反射、圆角等...
  3. 它不遵循我编写的建议 API,以节省内存使用。

代码在这里:

public class MosaicView extends ImageView {

    private ArrayList<Bitmap> mImages;
    private ArrayList<Rect> mImagesRects;
    private final Paint mPaint = new Paint();
    private Rect mTopLeftRect, mLeftRect, mWholeRect, mRightRect, mTopRightRect, mBottomLeftRect, mBottomRightRect;
    private boolean mIsDirty = false;
    private final Rect mCenterCropRect = new Rect();

    public MosaicView(final Context context) {
        super(context);
    }

    public MosaicView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public MosaicView(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setImages(final ArrayList<Bitmap> images) {
        this.mImages = images;
        if (mImages == null)
            mImagesRects = null;
        else {
            mImagesRects = new ArrayList<Rect>(images.size());
            for (final Bitmap bitmap : images)
                mImagesRects.add(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
        }
        mIsDirty = true;
        invalidate();
    }

    @Override
    protected void onDraw(final Canvas canvas) {
        super.onDraw(canvas);
        final int width = getWidth();
        final int height = getHeight();
        if (mIsDirty) {
            mIsDirty = false;
            mTopLeftRect = new Rect(0, 0, width / 2, height / 2);
            mLeftRect = new Rect(0, 0, width / 2, height);
            mWholeRect = new Rect(0, 0, width, height);
            mRightRect = new Rect(width / 2, 0, width, height);
            mTopRightRect = new Rect(width / 2, 0, width, height / 2);
            mBottomLeftRect = new Rect(0, height / 2, width / 2, height);
            mBottomRightRect = new Rect(width / 2, height / 2, width, height);
        }
        if (mImages == null)
            return;
        Bitmap b;
        switch (mImages.size()) {
        case 0:
            break;
        case 1:
            b = mImages.get(0);
            getCenterCropRect(mImagesRects.get(0), mWholeRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mWholeRect, mPaint);
            break;
        case 2:
            b = mImages.get(0);
            getCenterCropRect(mImagesRects.get(0), mLeftRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mLeftRect, mPaint);
            b = mImages.get(1);
            getCenterCropRect(mImagesRects.get(1), mRightRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mRightRect, mPaint);
            break;
        case 3:
            b = mImages.get(0);
            getCenterCropRect(mImagesRects.get(0), mLeftRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mLeftRect, mPaint);
            b = mImages.get(1);
            getCenterCropRect(mImagesRects.get(1), mTopRightRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mTopRightRect, mPaint);
            b = mImages.get(2);
            getCenterCropRect(mImagesRects.get(2), mBottomRightRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mBottomRightRect, mPaint);
            break;
        default:
        case 4:
            b = mImages.get(0);
            getCenterCropRect(mImagesRects.get(0), mTopLeftRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mTopLeftRect, mPaint);
            b = mImages.get(1);
            getCenterCropRect(mImagesRects.get(1), mTopRightRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mTopRightRect, mPaint);
            b = mImages.get(2);
            getCenterCropRect(mImagesRects.get(2), mBottomRightRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mBottomRightRect, mPaint);
            b = mImages.get(3);
            getCenterCropRect(mImagesRects.get(3), mBottomLeftRect, mCenterCropRect);
            canvas.drawBitmap(b, mCenterCropRect, mBottomLeftRect, mPaint);
            break;

        }
    }

    private void getCenterCropRect(final Rect srcRect, final Rect limitRect, final Rect dstRect) {
        final float scaleX = (float) srcRect.width() / limitRect.width();
        final float scaleY = (float) srcRect.height() / limitRect.height();
        if (scaleX >= scaleY) {
            // image will fit in height, and truncate from the width
            dstRect.top = srcRect.top;
            dstRect.bottom = srcRect.bottom;
            final float newWidth = limitRect.width() * scaleY;
            dstRect.left = (int) (srcRect.width() / 2 - newWidth / 2);
            dstRect.right = (int) (srcRect.width() / 2 + newWidth / 2);
        } else {
            // image will fit in width, and truncate from the height
            dstRect.left = srcRect.left;
            dstRect.right = srcRect.right;
            final float newHeight = limitRect.height() * scaleX;
            dstRect.top = (int) (srcRect.height() / 2 - newHeight / 2);
            dstRect.bottom = (int) (srcRect.height() / 2 + newHeight / 2);

        }
    }
}
于 2013-06-27T19:35:57.053 回答