I have multiple images which is created dynamically in for loop.
for (int i = 0; i < 15; i++) {
ivFront = new TouchImageView(this);
ivFront.setId(i + 1);
ivFront.setBackgroundResource(frontImages[i]);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
frontWidth[i], frontHeight[i]);
((MarginLayoutParams) params).setMargins(frontX_axis[i],
frontY_axis[i], 0, 0);
ivFront.setAdjustViewBounds(true);
ivFront.setLayoutParams(params);
ivFront.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent arg1) {
Bitmap bmp = loadBitmapFromView(v);
return true;
}
});
//Bitmap bmp_new = loadBitmapFromView(ivFront);
rlMain.addView(ivFront, params);
}
public static Bitmap loadBitmapFromView(View v) {
Canvas bitmapCanvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(v.getWidth() * 2,
v.getHeight() * 2, Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.scale(2.0f, 2.0f);
v.draw(bitmapCanvas);
return bitmap;
}
When i write ivFront.getWidth() & ivFront.getHeight() then it shows me 0(Height)-0(Width). I already tried to get ImageView height & width in onWindowFocusChanged() method, but the output is same 0(Height)-0(Width).
So when i open the comment then it shows me below error:
11-05 19:16:28.522: E/AndroidRuntime(9153): Caused by: java.lang.IllegalArgumentException: width and height must be > 0
If you have created multiple images dynamically than try this one:
// initialize your images array
private ImageView myImages[] = new ImageView[your_array_length];
// create programatically and add to parent view
for (int i = 0; i < your_array_length; i++) {
myImages[i] = new ImageView(this);
myImages[i].setId(i + 1);
myImages[i].setBackgroundResource(your_array[i]);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
frontWidth[i], frontHeight[i]);
((MarginLayoutParams) params).setMargins(frontX_axis[i],
frontY_axis[i], 0, 0);
myImages[i].setAdjustViewBounds(true);
myImages[i].setLayoutParams(params);
if (getIntent() != null && i != your_array,length) {
final int j = i;
myImages[j].getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
myImages[j].getViewTreeObserver().removeOnPreDrawListener(this);
finalHeight = myImages[j].getMeasuredHeight();
finalWidth = myImages[j].getMeasuredWidth();
your_textview.setText("Height: " + finalHeight + " Width: " + finalWidth);
return true;
}
});
}
your_parent_layout.addView(myImages[i], params);
}
// Run code
Use a PreDrawListener to get the dimensions once your view has been measured. OnWindowFocusChanged does not guarantee all views have been measured.
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
int width = view.getWidth();
}
});
Add this code for each one of the dynamic imageview :
ivFront.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
iv.getViewTreeObserver().removeOnPreDrawListener(this);
height = iv.getMeasuredHeight();
width = iv.getMeasuredWidth();
return true;
}
});
ivFront.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent arg1) {
Bitmap bmp = loadBitmapFromView(v);
return true;
}
});
If you want to save all of your dynamic imageview's width and height, you can use List of integer to save the width and height of each your imageview.
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.
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>
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;
}
}
}
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
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:
it doesn't do the operations on a bitmap.
i have no idea how to perform special operations on the imageView i've extended from, such as reflection, rounded corners, etc...
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);
}
}
}
I'm new with Android.
In my project I have the custom View MyView with the follow code
public class MyView extends View {
private final Bitmap baseBitmap = BitmapFactory.decodeResource(
getResources(), R.drawable.myImage);
private final Matrix matrix;
private boolean active = true;
public MyView(Context context, Matrix matrix) {
super(context);
this.matrix = matrix;
this.setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (active) {
System.out.println("draw "+this.getId());
canvas.drawBitmap(baseBitmap, matrix, null);
} else {
...
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
System.out.println("--------->"+this.getId());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
this.matrix.setTranslate(event.getX()-(baseBitmap.getWidth()/2), event.getY()-(baseBitmap.getHeight()/2));
this.invalidate();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
this.active = false;
}
return true;
}
In my Activity, I instantiate MyView many times and then add them to the main layout. This is its code:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
float cx = display.getWidth() / 2, cy = display.getHeight() / 2;
int radius = 80;
double distance = 0, distancePoint = 0;
final int flags = PathMeasure.POSITION_MATRIX_FLAG
| PathMeasure.TANGENT_MATRIX_FLAG;
float length = 0;
setContentView(R.layout.main);
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_view);
Path pathCircle = new Path();
pathCircle.addCircle(cx, cy, radius, Direction.CW);
PathMeasure meas = new PathMeasure(pathCircle, false);
int nObject = 10;
length = meas.getLength();
distance = length/nObject;
int i = 0;
while(i<nObject){
Matrix m = new Matrix();
meas.getMatrix((float)distancePoint, m, flags);
MyView myView = new MyView(this, m);
System.out.println(myView.toString());
myView.setId(i);
mainLayout.addView(myView,i);
i++;
distancePoint = distance*i;
}
}
}
At runtime, when I touch any MyView element I always get the last. With "System.out.println("--------->"+this.getId());" I can see that the id of the touched element is always the last, even if I toch the first or any other element. Actualy, I just can move the last element.
Does anyone know why can't I get the event of the right istance of MyView touched?
(I hope my question is clear)
Thanks
I changed the code adding the onMeasure method. I used the code of a tutorial, dimensions are not specific for my image. The views are drawn and the result is the same, unfortunately with the same problem. I post the layout xml too, maybe could be useful.
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
float cx = display.getWidth() / 2, cy = display.getHeight() / 2;
int radius = 80;
double distance = 0, distancePoint = 0;
final int flags = PathMeasure.POSITION_MATRIX_FLAG
| PathMeasure.TANGENT_MATRIX_FLAG;
float length = 0;
setContentView(R.layout.main);
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_view);
Path pathCircle = new Path();
pathCircle.addCircle(cx, cy, radius, Direction.CW);
PathMeasure meas = new PathMeasure(pathCircle, false);
int nObject = 10;
length = meas.getLength();
distance = length/nObject;
int i = 0;
while(i<nObject){
Matrix m = new Matrix();
meas.getMatrix((float)distancePoint, m, flags);
MyView myView = new MyView(this, m);
System.out.println(myView.toString());
myView.setId(i);
nt spec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
myView.measure(spec, spec);
mainLayout.addView(myView,i);
i++;
distancePoint = distance*i;
}
}
}
public class MyView extends View {
private final Bitmap baseBitmap = BitmapFactory.decodeResource(
getResources(), R.drawable.myImage);
private final Matrix matrix;
private boolean active = true;
public MyView(Context context, Matrix matrix) {
super(context);
this.matrix = matrix;
this.setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (active) {
System.out.println("draw "+this.getId());
canvas.drawBitmap(baseBitmap, matrix, null);
} else {
...
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
System.out.println("--------->"+this.getId());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
this.matrix.setTranslate(event.getX()-(baseBitmap.getWidth()/2), event.getY()-(baseBitmap.getHeight()/2));
this.invalidate();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
this.active = false;
}
return true;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
int chosenDimension = Math.min(chosenWidth, chosenHeight);
setMeasuredDimension(chosenDimension, chosenDimension);
}
private int chooseDimension(int mode, int size) {
if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
return size;
} else { // (mode == MeasureSpec.UNSPECIFIED)
return getPreferredSize();
}
}
// in case there is no size specified
private int getPreferredSize() {
return 300;
}
}
The main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="#+id/main_view"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF66FF33">
</RelativeLayout>
I'm pretty sure that it's because you're basically piling up your views at the top left corner of your RelativeLayout. So, only the uppermost (the last one added) is touchable.
I think that if you try adding them to a LinearLayout, as a test, you'll see that your view works. Setting LayoutParams for a RelativeLayout programmatically is not very comfy IMHO.
EDIT
I tried your code. The fact is that your views are just made to be drawn one over the other, or else the overall drawing wouldn't come, so my first guess is right (the uppermost covers the others - even in its transparent parts)(btw try Hierarchy Viewer and you can see that yourself). So you need to do your job in a single view, or handle the touches like this:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if(!isPetaloTouched()) {// check if the actual drawing was touched
return false; // discard the event so that it reaches
// the underlying view
}
//......
See this post for an explanation of how events work in Android.
Both ways would need an isPetaloTouched() logic to detect if/which drawing must be moved, but the first would be more efficient of course.
Also, forget about the onMeasure() thing, I thought that could help giving the view a size around which to wrap, so that it wouldn't fill its parent and aligning views aside would make sense. However, be sure that the touch would work if the views were not piled up.
(...allora mPetali stava proprio per petali!)