I have a relative layout section. Within it, i have to programmatically add several clickable textviews that may vary in widths. I need them to appear one after another. If a textView has width such that it cannot fit in the remaining space of the current row, it should start populating below the row in a similar fashion.
I need something like this:
What i've managed so far is this code below:
public static void updateWordsListingOnUi(final ArrayList<String> wordsList) { //TODO: incomplete and may be inefficient
mUiHandler.post(new Runnable() {
#Override
public void run() {
TextView textView;
wordsListingContainer.removeAllViewsInLayout(); //TODO
for (final String word : wordsList)
{
textView = new TextView(context);
textView.setText(word);
textView.setClickable(true);
textView.setPadding(1,1,10,1);
if (wordsList.indexOf(word)%2 == 0) {
textView.setBackgroundColor(context.getResources().getColor(R.color.colorPrimary));
}
else {
textView.setBackgroundColor(context.getResources().getColor(R.color.colorAccent));
}
textView.setId(wordsList.indexOf(word)); //give IDs from 0 - (maxSize-1)
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, word, Toast.LENGTH_SHORT).show();
}
});
if(wordsList.indexOf(word) > 0) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.RIGHT_OF, textView.getId()-1); //TODO
wordsListingContainer.addView(textView,layoutParams);
}
else {
wordsListingContainer.addView(textView);
}
}
}
});
}
This does the job partially. It puts textviews one after the other, but when there i insufficient space, it doesn't wrap to the next row. I cannot think of how i can do that.
Here is a tutorial, that explains how, you can do this.
In short, your class need to be something like:
public class TagLayout extends ViewGroup {
int deviceWidth;
public TagLayout(Context context) {
this(context, null, 0);
}
public TagLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Point deviceDisplay = new Point();
display.getSize(deviceDisplay);
deviceWidth = deviceDisplay.x;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
int curWidth, curHeight, curLeft, curTop, maxHeight;
//get the available size of child view
final int childLeft = this.getPaddingLeft();
final int childTop = this.getPaddingTop();
final int childRight = this.getMeasuredWidth() - this.getPaddingRight();
final int childBottom = this.getMeasuredHeight() - this.getPaddingBottom();
final int childWidth = childRight - childLeft;
final int childHeight = childBottom - childTop;
maxHeight = 0;
curLeft = childLeft;
curTop = childTop;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE)
return;
//Get the maximum size of the child
child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST));
curWidth = child.getMeasuredWidth();
curHeight = child.getMeasuredHeight();
//wrap is reach to the end
if (curLeft + curWidth >= childRight) {
curLeft = childLeft;
curTop += maxHeight;
maxHeight = 0;
}
//do the layout
child.layout(curLeft, curTop, curLeft + curWidth, curTop + curHeight);
//store the max height
if (maxHeight < curHeight)
maxHeight = curHeight;
curLeft += curWidth;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
// Measurement will ultimately be computing these values.
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
int mLeftWidth = 0;
int rowCount = 0;
// Iterate through all children, measuring them and computing our dimensions
// from their size.
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE)
continue;
// Measure the child.
measureChild(child, widthMeasureSpec, heightMeasureSpec);
maxWidth += Math.max(maxWidth, child.getMeasuredWidth());
mLeftWidth += child.getMeasuredWidth();
if ((mLeftWidth / deviceWidth) > rowCount) {
maxHeight += child.getMeasuredHeight();
rowCount++;
} else {
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
}
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Report our final dimensions.
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));
}
}
Why don't you try out libraries, here i am sharing some of the links
Chips EditText Library
MultiTextTagView
TokenAutoComplete
Android Chips
These libraries will help you to achieve your output.
I would like to create a custom RelativeLayout that has two views in one row: one on the left side of the screen (android:layout_alignParentStart="true") and one on the right (android:layout_alignParentEnd="true"). The view on the right will grow toward the left view until it takes up all the space between the two views. Then it will move to a new line under the view on the left.
I have implemented a slightly modified version of Romain Guy's FlowLayout that extends RelativeLayout. However, this class seems to ignore the RelativeLayout's align properties and just sticks the views right next to each other. Is there a way to implement a such a layout that will anchor the views to the left and right?
FlowLayout class:
public class FlowLayout extends RelativeLayout {
private int mHorizontalSpacing;
private int mVerticalSpacing;
public FlowLayout(Context context) {
super(context);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
mHorizontalSpacing = attributes.getDimensionPixelSize(R.styleable
.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = attributes.getDimensionPixelSize(R.styleable
.FlowLayout_verticalSpacing, 0);
attributes.recycle();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int width = 0;
int height = getPaddingTop();
int currentWidth = getPaddingStart();
int currentHeight = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
measureChild(child, widthMeasureSpec, heightMeasureSpec);
if (currentWidth + child.getMeasuredWidth() > widthSize) {
height += currentHeight + mVerticalSpacing;
currentHeight = 0;
width = Math.max(width, currentWidth);
currentWidth = getPaddingEnd();
}
int spacing = mHorizontalSpacing;
if (lp.spacing > -1) {
spacing = lp.spacing;
}
lp.x = currentWidth + spacing;
lp.y = currentHeight;
currentWidth += child.getMeasuredWidth();
currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
}
width += getPaddingEnd();
height += getPaddingBottom();
setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height,
heightMeasureSpec));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child
.getMeasuredHeight());
}
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout
.LayoutParams.WRAP_CONTENT);
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}
#Override
public RelativeLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends RelativeLayout.LayoutParams {
public int spacing;
public int x;
public int y;
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable
.FlowLayout_LayoutParams);
spacing = attributes.getDimensionPixelSize(R.styleable
.FlowLayout_LayoutParams_layoutSpacing, -1);
attributes.recycle();
}
public LayoutParams(int width, int height) {
super(width, height);
}
}
}
It turns out that rather than calculating the right view's new position yourself, you can change its LayoutParams and have the OS handle positioning for you. I created a custom layout that extends RelativeLayout and overrides the onMeasure() method. This will adjust the LayoutParams accordingly.
More specifically:
Call the super method then find the widths of the two views and their parent in onMeasure(). Use these to figure out if the right view will overlap the left view. If so, change the right view's layout_alignParentEnd="true" property to be layout_alignParentStart="true" and give it the layout_below="#id/left_view" property. Do the opposite when there will be no overlap. Call the super method again to have the OS remeasure the views for you.
The layout class:
public class WrappingLayout extends RelativeLayout {
private TextView leftView;
private EditText rightView;
//Use this to prevent unnecessarily adjusting the LayoutParams
//when the right view is already in the correct position
private boolean isMultiline = false;
public WrappingLayout(Context context) {
super(context);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.wrapping_layout, this);
leftView = (TextView) findViewById(R.id.left_view);
rightView = (EditText) findViewById(R.id.right_view);
}
public WrappingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.wrapping_layout, this);
leftView = (TextView) findViewById(R.id.left_view);
rightView = (EditText) findViewById(R.id.right_view);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Call first to make sure the views' initial widths have been set
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int screenWidth = getMeasuredWidth();
int leftViewWidth = getPaddingStart() + leftView.getMeasuredWidth() + leftView.getPaddingEnd();
int rightViewWidth = getPaddingEnd() + rightView.getMeasuredWidth() + rightView.getPaddingStart();
LayoutParams rightViewParams = (LayoutParams) rightView.getLayoutParams();
if (!isMultiline && rightViewWidth + leftViewWidth > screenWidth) {
isMultiline = true;
rightViewParams.addRule(BELOW, R.id.left_view);
rightViewParams.removeRule(ALIGN_PARENT_END);
rightViewParams.addRule(ALIGN_PARENT_START);
//Call again here to adjust dimensions for new params
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else if (isMultiline && rightViewWidth + leftViewWidth < screenWidth) {
isMultiline = false;
rightViewParams.removeRule(BELOW);
rightViewParams.addRule(ALIGN_PARENT_END);
rightViewParams.removeRule(ALIGN_PARENT_START);
//Call again here to adjust dimensions for new params
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
The layout XML:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#id/left_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"/>
<EditText
android:id="#id/right_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:gravity="center"
android:text="#string/hello"/>
</merge>
I want to display multiple text view in a layout dynamically.
Suppose,User selects 10 items from drop down menu then I can display it to layout one after another.I need same view as attached image -
I have selected item in a list,now I am not getting how to display data in desire manner.When I try list view all data are display one after other vertically.
When I use Linearlayout data can be either added in horizontal or vertical order.So I am not getting correct way to display data.
Please help me in solving this issue.
Thanks in advance.
what you are looking for is called chips.why not use material design chips, you can refer it here. http://www.google.com/design/spec/components/chips.html#
you can implement like using grid view if you are not interested using chips just have one row of gridview and populate it there with one cell
Normally if you use LinearLayout with horizontal orientation, it will put your items as you want.
try this one..i used to acheive using custom view.if you are ok with this just give a try..
Custom View class is.
public class WrapLayout extends ViewGroup {
private int paddingHorizontal;
private int paddingVertical;
public WrapLayout(Context context) {
super(context);
init();
}
public WrapLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WrapLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
paddingHorizontal = getResources().getDimensionPixelSize(10);
paddingVertical = getResources().getDimensionPixelSize(10);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lineHeight = 0;
// 100 is a dummy number, widthMeasureSpec should always be EXACTLY for FlowLayout
int myWidth = resolveSize(100, widthMeasureSpec);
int wantedHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
// let the child measure itself
child.measure(
getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width),
getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height));
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
// lineheight is the height of current line, should be the height of the heightest view
lineHeight = Math.max(childHeight, lineHeight);
if (childWidth + childLeft + getPaddingRight() > myWidth) {
// wrap this line
childLeft = getPaddingLeft();
childTop += paddingVertical + lineHeight;
lineHeight = childHeight;
}
childLeft += childWidth + paddingHorizontal;
}
wantedHeight += childTop + lineHeight + getPaddingBottom();
setMeasuredDimension(myWidth, resolveSize(wantedHeight, heightMeasureSpec));
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lineHeight = 0;
int myWidth = right - left;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
lineHeight = Math.max(childHeight, lineHeight);
if (childWidth + childLeft + getPaddingRight() > myWidth) {
childLeft = getPaddingLeft();
childTop += paddingVertical + lineHeight;
lineHeight = childHeight;
}
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + paddingHorizontal;
}
}
}
and my xml is :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/editText_sample"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add It"
android:id="#+id/addit"
/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- i mean where you put the WrapLayout class in your project that package name -->
<your.package.name.WrapLayout
android:id="#+id/flow_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"/>
</ScrollView>
</LinearLayout>
And finally in MainActivity
ViewGroup flowContainer;
In OnCreate
flowContainer= (ViewGroup) findViewById(R.id.flow_container);
final EditText text=(EditText)findViewById(R.id.editText_sample);
Button addIT=(Button)findViewById(R.id.addit);
addIT.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String data=text.getText().toString();
flowContainer.addView(createTextView(data),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
});
Copy this function within mainactivity
private View createTextView(String text) {
TextView textView = new TextView(this);
textView.setText(text);
textView.setGravity(Gravity.CENTER);
textView.setCompoundDrawablesWithIntrinsicBounds(0,0,R.mipmap.ic_launcher,0);
return textView;
}
I need to create a basic Tag Cloud. My aim is to add multiple TextView(Tags) that automatically fits themselves in a new line if the number exceeds the device width. (Like Instagram).
Right now when the width of the layout containing (TextView)tags exceeds the (device width) the TextView(s) at the end wraps itself.
I tried using RelativeLayout but i am unable to figure out the logic.
I viewed this post but if there is a better or simply a cleaner solution.
Thanks for your precious time.
It is not necessary that the result looks like Instagram.
The cleaner solution is to write your own custom ViewGroup class. Find the sample below.
For complete descriptive explanation visit, How to Create Custom Layout in Android by Extending ViewGroup Class.
public class TagLayout extends ViewGroup {
public TagLayout(Context context) {
super(context);
}
public TagLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
int curWidth, curHeight, curLeft, curTop, maxHeight;
//get the available size of child view
final int childLeft = this.getPaddingLeft();
final int childTop = this.getPaddingTop();
final int childRight = this.getMeasuredWidth() - this.getPaddingRight();
final int childBottom = this.getMeasuredHeight() - this.getPaddingBottom();
final int childWidth = childRight - childLeft;
final int childHeight = childBottom - childTop;
maxHeight = 0;
curLeft = childLeft;
curTop = childTop;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE)
return;
//Get the maximum size of the child
child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST));
curWidth = child.getMeasuredWidth();
curHeight = child.getMeasuredHeight();
//wrap is reach to the end
if (curLeft + curWidth >= childRight) {
curLeft = childLeft;
curTop += maxHeight;
maxHeight = 0;
}
//do the layout
child.layout(curLeft, curTop, curLeft + curWidth, curTop + curHeight);
//store the max height
if (maxHeight < curHeight)
maxHeight = curHeight;
curLeft += curWidth;
}
}
}
To use the TagLayout, you can add it to your activity/fragment layout declaration.
main_activity.xml
<com.javatechig.taglayout.TagLayout
android:id="#+id/tagLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Now we can have a custom view to allow some level of customization for each tag item layout.
tag_layout.xml
<TextView
android:id="#+id/tagTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="#a000"
android:padding="10dp"
android:textColor="#fff" />
MainActivity.java
Finally, from activity class you can add the tag items as follows.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TagLayout tagLayout = (TagLayout) findViewById(R.id.tagLayout);
LayoutInflater layoutInflater = getLayoutInflater();
String tag;
for (int i = 0; i <= 20; i++) {
tag = "#tag" + i;
View tagView = layoutInflater.inflate(R.layout.tag_layout, null, false);
TextView tagTextView = (TextView) tagView.findViewById(R.id.tagTextView);
tagTextView.setText(tag);
tagLayout.addView(tagView);
}
}
}
Result
I believe the solution above always consider the whole screen width, so if the developer sets android:layout_width parameter for example it won't respect its value or even parent's margin and padding values.
I've fixed it as follows:
private int mScreenWidth = 0;
private int mAvailableWidth = -1;
public TagLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
Point deviceSize = new Point();
display.getSize(deviceSize);
mScreenWidth = deviceSize.x;
}
private void calculateAvailableWidth() {
if(getLayoutParams() != null && getLayoutParams().width > 0) {
mAvailableWidth = getLayoutParams().width;
return;
}
mAvailableWidth = mScreenWidth;
ViewGroup parent = this;
while(parent != null) {
mAvailableWidth -= parent.getPaddingLeft() + parent.getPaddingRight();
if(parent.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)parent.getLayoutParams();
mAvailableWidth -= layoutParams.leftMargin + layoutParams.rightMargin;
}
if(parent.getParent() instanceof ViewGroup)
parent = (ViewGroup)parent.getParent();
else
parent = null;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int currentRowWidth = 0;
int currentRowHeight = 0;
int maxItemWidth = 0;
int maxWidth = 0;
int maxHeight = 0;
if(mAvailableWidth == -1)
calculateAvailableWidth();
for(int i = 0; i < count; i++) {
View child = getChildAt(i);
if(child.getVisibility() == GONE)
continue;
try {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
}
catch(Exception e) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
int childWidth = child.getMeasuredWidth() + child.getPaddingRight() + child.getPaddingLeft();
int childHeight = child.getMeasuredHeight() + child.getPaddingTop() + child.getPaddingBottom();
maxItemWidth = Math.max(maxItemWidth, childWidth);
if(currentRowWidth + childWidth < mAvailableWidth) {
currentRowWidth += childWidth;
maxWidth = Math.max(maxWidth, currentRowWidth);
currentRowHeight = Math.max(currentRowHeight, childHeight);
}
else {
currentRowWidth = childWidth;
maxHeight += currentRowHeight;
}
}
if(getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
mAvailableWidth = maxItemWidth;
maxWidth = maxItemWidth;
}
maxHeight += currentRowHeight + getPaddingTop() + getPaddingBottom();
setMeasuredDimension(maxWidth, maxHeight);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
int currentLeft = getPaddingLeft();
int currentTop = getPaddingTop();
int currentRight;
int currentBottom;
int parentWidth = this.getPaddingRight() - this.getPaddingLeft() + right;
for(int i = 0; i < count; i++) {
View child = getChildAt(i);
if(child.getVisibility() == View.GONE)
return;
int currentWidth = child.getMeasuredWidth() + child.getPaddingRight() + child.getPaddingLeft();
int currentHeight = child.getMeasuredHeight() + child.getPaddingBottom() + child.getPaddingTop();
if(currentLeft + currentWidth > parentWidth) {
currentLeft = getPaddingLeft();
currentTop += currentHeight;
}
currentBottom = currentTop + currentHeight;
currentRight = currentLeft + currentWidth;
child.layout(currentLeft, currentTop, currentRight, currentBottom);
currentLeft += currentWidth;
}
}
This way you are able to set android:layout_width="250dp" and get a result like this:
Set android:layout_width="match_parent" and get a result like this:
Or event use android:layout_width="wrap_content and get a result like this:
Hope it helps.
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);
}
}
}