“Mosaic” (splitted) images - Gmail's letters style

让人想犯罪 __ 提交于 2019-12-12 08:19:05

问题


On the new versions of gmail, there is a cool imageView that shows multiple contacts images in it (link here for example) .

for example, if someone has sent me an email, i only see his image:

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

if i've replied to him, i can see my image next to it, but both my image and his are halved and share the same space of the imageView (and i think both have scaleType to be center crop) :

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

if another person has joined the conversation, it could look like this:

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

and if another one has joined, it could look like this:

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

i'm not sure about the order of the items (and the rules, so everything here is my guess) , and what happens when more people are joining.

the important thing is that i want to know how to achieve this .

does anyone know of a solution for this? how they did it? which view was used?

it's most certainly a custom view, but what's the best way to do it? a way that is probably most efficient and doesn't use a lot of memory ...

i might even want to make the final image to be rounded, so it might be better to handle bitmaps instead of an imageView...

i'm not even sure how to call such a view. i've thought of a "CollageView" or a "MosaicView" .

just to make it clear, i think that such a problem should be handled using the next API :

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

or, if the bitmaps might take too much memory , we could use something like:

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

i've come up with 2 solutions, each has its own advantages and disadvantages, but i still need to perform special effects on the final result (especially rounded corners, but maybe other things too ), and this is something that i don't know how to do.

can anyone please help? what do you think google has used on their app ?


EDIT: i've come up with a few possible solutions, for each i've written an answer to this thread. i'm not sure which is the best so i've posted them all . i guess each has its own advantages and disadvantages.

none of my current solutions handles a bitmap as i've offered, but they are quite intuitive...

i would still wish for some advice as to how this should be done in your opinion.


回答1:


here's a solution i call:

The XML solution

it uses XML to set how the mosaicView would look like. still not as i've planned, but it might help some people who need such a thing and be able to change it the way they want.

what i've added is the ability to add custom dividers (uses IcsLinearLayout from actionBarSherlock for this) . of course, you can add whatever you wish...

here's the code:

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

}

mosaic_view.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>



回答2:


here's a solution i like to call

the viewGroup solution

sadly it uses multiple imageViews and it doesn't have a final bitmap to mess with.

please, if anyone knows of a good way to show the images, post it.

here's the code:

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

}



回答3:


I suggest you extend ViewGroup and lay your children out like you want them in the block. I achieved something similar by doing this. You can specify parameters that will determine your layout by the amount of images in each block. Your parent will specify your children's size and position. So for example if you have 2 items you want to display in the parent, the parent will see that and measure half of the block's width for the one child and the other half for the other child, then the parent will position the children so that they are displayed correctly.

For your children you can extend ImageView and fill it with a sampled bitmap. This will reduce memory usage and you will be able to use more than one image block in your parent. If your image is downloaded I suggest you create a AsyncTask that does all the work for you and then updates the ImageView Bitmap after sampling ect is done. You can also use this task to load your images into your ImageView when using recycling in your ListView. Your children's size will obviously be determined by the parent when the onMeasure is executed in the parent.

You can then use that custom view that you created and implement it in your ListView to get the desired effect

You can have a look at this, this and this to get you started

----- EDIT -----

Here is a screen shot of the control I implemented. This isn't exactly the same but it has the same approach and principle. In this control my Parent (full screen) is your small block that contains the images and my child is (the colored blocks) is your image. Now in your child you can do anything to achieve the desired effect. You can implement onTouch events on each child, add animations to each child ect. The possibilities are endless if you implement the parent child structure correctly.

This is how I layed out my children in the ViewGroup parent in the example screenshot above

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

}

This layout will only display 2 columns but an infinite amount of rows, so it won't work a hundred percent like you want it to, but you can use a similar approach.

Here is an example of my child

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

    }
}

I used a ViewGroup for my child because my requirements were different than yours but you can use a simple ImageViewbecause you only want to display a manipulated bitmap. You can give your bitmap rounded corners in the child by using this method (as you mentioned in the comments).

Hope this helps




回答4:


here's a solution i call:

the imageView solution

it extends from ImageView, and override its onDraw method. it works fine, but it has some disadvantages which i would be happy if anyone could improve:

  1. it doesn't do the operations on a bitmap.
  2. i have no idea how to perform special operations on the imageView i've extended from, such as reflection, rounded corners, etc...
  3. it doesn't follow the suggested API that i've written, in order to conserve memory usage.

the code is here:

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

        }
    }
}


来源:https://stackoverflow.com/questions/17315491/mosaic-splitted-images-gmails-letters-style

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!