I want to implement chip something like this:
chip
But since I've a predefined Array Adapter & just need to put its values into this chip & I want to set these chips to a Textview or simply View. I've tried many chip libraries but all of them gave some kind of error while adding to dependencies.
I just need to implement chips for the look of the app & not search functionality so can anyone help me in this & if you have any better ideas then please feel free to share.
Finally I did it like this with the help of a nine patch image chip.9.png & FlowLayout:
MainActivity:
LinearLayout llTemp = new LinearLayout(this);
llTemp.setOrientation(LinearLayout.HORIZONTAL);
llTemp.setBackgroundResource(R.drawable.chip);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
llTemp.setLayoutParams(params);
llTemp.setGravity(Gravity.CENTER_VERTICAL);
TextView tv = new TextView(this);
tv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
tv.setPadding(0, 0, 5, 0);
tv.setText("Some text");
CircularImageView iv = new CircularImageView(this);
LinearLayout.LayoutParams ivParams = new LinearLayout.LayoutParams(50, 50);
ivParams.setMargins(0, 0, 5, 0);
iv.setLayoutParams(ivParams);
iv.setBorderWidth(0);
Picasso.with(this).load(IMAGE_LINK + IMAGE_FILE).into(iv);
ImageView ivCloseBtn = new ImageView(this);
ivCloseBtn.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
ivCloseBtn.setPadding(2, 2, 2, 2);
ivCloseBtn.setBackgroundResource(R.drawable.icon_close);
FlowLayout.java:
public class FlowLayout extends ViewGroup {
private int paddingHorizontal;
private int paddingVertical;
public FlowLayout(Context context) {
super(context);
init();
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
paddingHorizontal = getResources().getDimensionPixelSize(R.dimen.flowlayout_horizontal_padding);
paddingVertical = getResources().getDimensionPixelSize(R.dimen.flowlayout_vertical_padding);
}
#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 highest 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;
}
}}
& then in Main Activity: flowLayout.addView(llTemp)
& flow layout within xml file(mainactivity):
<ScrollView android:id="#+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<com.example.FlowLayout
android:id="#+id/fl"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
Final Product: final_chip_image
Related
I am trying to make a scrollable layout, where I would have a single bigger custom view at the start and dynamic amount of smaller TextViews after it.
Like this:
|-----|---Start of devices screen
| |
| | -Custom view 1
| |
|-----|
| |---TextView & End of devices screen
|-----|
|-----|
| |
|-----|
The custom view is a view where I do some drawing in onDraw().
I have been trying to fiddle with the onMeasure() method, layout_height and layout_weight properties, but I am unable to get a satisfactory result. I always end up with a unscrollable layout or one without the custom view. Only way I have been able to get the functionality is by setting the custom view's height to some 'dp' value, but I would like it leave the same physical amount of space in the botto of the screen.
Is something like this possible with the tools Android provides, or would this need some custom view for the scrolling too? How would I go about implementing this?
Edit: To clarify, I need the first element I have in the ScrollView to initially either fill the whole visible area of the ScrollView or leave a small space under it that is constant on every device.
Use Flow Layout In place of your custom layout
find flow layout implementation link
https://docs.google.com/file/d/0B_VKZCqEnHblNDdiY2UxODgtYWNhNS00MmU4LWE4NDMtZjQ1OWI5MDMxZTVh/edit?num=50&sort=name&layout=list&pli=1#
package com.example.android.layout;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class FlowLayout extends ViewGroup {
private int mHorizontalSpacing;
private int mVerticalSpacing;
private Paint mPaint;
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
try {
mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
mVerticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
} finally {
a.recycle();
}
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(0xffff0000);
mPaint.setStrokeWidth(2.0f);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;
int width = 0;
int height = getPaddingTop();
int currentWidth = getPaddingLeft();
int currentHeight = 0;
boolean breakLine = false;
boolean newLine = false;
int spacing = 0;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
spacing = mHorizontalSpacing;
if (lp.horizontalSpacing >= 0) {
spacing = lp.horizontalSpacing;
}
if (growHeight && (breakLine || currentWidth + child.getMeasuredWidth() > widthSize)) {
height += currentHeight + mVerticalSpacing;
currentHeight = 0;
width = Math.max(width, currentWidth - spacing);
currentWidth = getPaddingLeft();
newLine = true;
} else {
newLine = false;
}
lp.x = currentWidth;
lp.y = height;
currentWidth += child.getMeasuredWidth() + spacing;
currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
breakLine = lp.breakLine;
}
if (!newLine) {
height += currentHeight;
width = Math.max(width, currentWidth - spacing);
}
width += getPaddingRight();
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 drawChild(Canvas canvas, View child, long drawingTime) {
boolean more = super.drawChild(canvas, child, drawingTime);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.horizontalSpacing > 0) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y - 4.0f, x, y + 4.0f, mPaint);
canvas.drawLine(x, y, x + lp.horizontalSpacing, y, mPaint);
canvas.drawLine(x + lp.horizontalSpacing, y - 4.0f, x + lp.horizontalSpacing, y + 4.0f, mPaint);
}
if (lp.breakLine) {
float x = child.getRight();
float y = child.getTop() + child.getHeight() / 2.0f;
canvas.drawLine(x, y, x, y + 6.0f, mPaint);
canvas.drawLine(x, y + 6.0f, x + 6.0f, y + 6.0f, mPaint);
}
return more;
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
#Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
#Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p.width, p.height);
}
public static class LayoutParams extends ViewGroup.LayoutParams {
int x;
int y;
public int horizontalSpacing;
public boolean breakLine;
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout_LayoutParams);
try {
horizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_LayoutParams_layout_horizontalSpacing, -1);
breakLine = a.getBoolean(R.styleable.FlowLayout_LayoutParams_layout_breakLine, false);
} finally {
a.recycle();
}
}
public LayoutParams(int w, int h) {
super(w, h);
}
}
}
<resources>
<declare-styleable name="FlowLayout">
<attr name="horizontalSpacing" format="dimension" />
<attr name="verticalSpacing" format="dimension" />
</declare-styleable>
<declare-styleable name="FlowLayout_LayoutParams">
<attr name="layout_breakLine" format="boolean" />
<attr name="layout_horizontalSpacing" format="dimension" />
</declare-styleable>
</resources>
I am trying to put a custom view into scrollView. This is my custom view MyCustomLayout.java
public class MyCustomLayout extends ViewGroup {
private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10;
private int mMaxChildWidth = 0;
private int mMaxChildHeight = 0;
public MyCustomLayout(Context context) {
super(context, null);
}
public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public MyCustomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMaxChildWidth = 0;
mMaxChildHeight = 0;
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight());
}
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
mMaxChildWidth, MeasureSpec.EXACTLY);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
mMaxChildHeight, MeasureSpec.EXACTLY);
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
setMeasuredDimension(
resolveSize(mMaxChildWidth, widthMeasureSpec),
resolveSize(mMaxChildHeight, heightMeasureSpec));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = r - l;
int height = b - t;
final int count = getChildCount();
// Calculate the number of visible children.
int visibleCount = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
++visibleCount;
}
if (visibleCount == 0) {
return;
}
int bestSpaceDifference = Integer.MAX_VALUE;
int spaceDifference;
int hSpace = 0;
int vSpace = 0;
int cols = 1;
int rows;
while (true) {
rows = (visibleCount - 1) / cols + 1;
hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
spaceDifference = Math.abs(vSpace - hSpace);
if (rows * cols != visibleCount) {
spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER;
}
if (spaceDifference < bestSpaceDifference) {
bestSpaceDifference = spaceDifference;
if (rows == 1) {
break;
}
} else {
--cols;
rows = (visibleCount - 1) / cols + 1;
hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
break;
}
++cols;
}
hSpace = Math.max(0, hSpace);
vSpace = Math.max(0, vSpace);
width = (width - hSpace * (cols + 1)) / cols;
height = (height - vSpace * (rows + 1)) / rows;
int left, top;
int col, row;
int visibleIndex = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
row = visibleIndex / cols;
col = visibleIndex % cols;
left = hSpace * (col + 1) + width * col;
top = vSpace * (row + 1) + height * row;
child.layout(left, top,
(hSpace == 0 && col == cols - 1) ? r : (left + width),
(vSpace == 0 && row == rows - 1) ? b : (top + height));
++visibleIndex;
}
}
}
When I use in a LinearLayout with those xml code. It works fine.
<com.crazyCoder.androiddeshboard.MyCustomLayout
android:id="#+id/layout_custom"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#f8f9fe" >
</com.crazyCoder.androiddeshboard.MyCustomLayout>
But when I put into a scrollView like this
<ScrollView
android:id="#+id/scrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffd9f4">
<com.crazyCoder.androiddeshboard.MyCustomLayout
android:id="#+id/layout_custom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f8f9fe" >
</com.crazyCoder.androiddeshboard.MyCustomLayout>
</ScrollView>
The custom view overlapping by the scrollView. The output like this.
ScrollView Output
Please Help me. Thanks in advance.
Try put FrameLayout in ScrollView and put MyCustomLatyout in FrameLayout.
<ScrollView
android:id="#+id/scrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffd9f4">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.crazyCoder.androiddeshboard.MyCustomLayout
android:id="#+id/layout_custom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f8f9fe" >
</com.crazyCoder.androiddeshboard.MyCustomLayout>
</FrameLayout>
</ScrollView>
I am trying to hide a child in a view during onLayout. The problem is the postInvalidate is not recalling onLayout after i hide the child
public class EllipseFlowLayout extends ViewGroup {
private int paddingHorizontal;
private int paddingVertical;
private int mMaxLines = -1;
private EllipseView mEllipse;
private View ellipseView;
private int numHidden;
private boolean mEllipseRemoved = true;
public EllipseFlowLayout(Context context) {
super(context);
init();
}
public void setMaxLines(int max) {
mMaxLines = max;
}
public EllipseFlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public EllipseFlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
paddingHorizontal = getResources().getDimensionPixelSize(R.dimen.flowlayout_horizontal_padding);
paddingVertical = getResources().getDimensionPixelSize(R.dimen.flowlayout_vertical_padding);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lineHeight = 0;
int numLines = 1;
// 100 is a dummy number, widthMeasureSpec should always be EXACTLY for FlowLayout
int myWidth = resolveSize(MeasureSpec.EXACTLY, widthMeasureSpec);
int wantedHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
if (child.getVisibility() == View.GONE || child.getTag() == 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;
numLines++;
Log.d("TEST", "measure" + numLines);
}
childLeft += childWidth + paddingHorizontal;
}
wantedHeight += childTop + lineHeight + getPaddingBottom();
setMeasuredDimension(myWidth, resolveSize(wantedHeight, heightMeasureSpec));
}
#Override
public void addView(View child) {
//super.addView(child);
if (mEllipseRemoved) {
addEllipseView();
}
super.addView(child, getChildCount() - 1);
}
public void setEllipseView(EllipseView ellipse) {
mEllipse = ellipse;
ellipseView = mEllipse.getView();
}
#Override
public void removeAllViews() {
mEllipseRemoved = true;
super.removeAllViews();
}
private void addEllipseView() {
mEllipseRemoved = false;
if (ellipseView.getParent() != null) {
((ViewGroup) ellipseView.getParent()).removeView(ellipseView);
}
ellipseView.setVisibility(View.GONE);
addView(ellipseView);
}
#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;
int lineNum = 1;
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;
lineNum++;
Log.d("TEST", "layout" + lineNum);
}
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + paddingHorizontal;
}
if (lineNum > mMaxLines) {
// if (ellipseView.getVisibility() == View.GONE) {
// ellipseView.setVisibility(VISIBLE);
// }
//Skip the last view which is the hidden one
int at = numHidden;
if (at < getChildCount() && getChildAt(at) != null) {
View child = getChildAt(at);
numHidden++;
if (child.getVisibility() == View.VISIBLE) {
child.setVisibility(View.GONE);
child.setTag(GONE);
requestLayout();
}
}
if (mEllipse.getCount() != numHidden) {
mEllipse.setEllipseSize(numHidden);
}
}
}
/**
* Was removed so semi untested but leaving for possible use later
*
* #param max max children
*/
public void setMaxChildren(int max) {
}
}
Hide the views in onDraw remember to call this.setWillNotDraw(false);
I am using this flow layout class to arrange dynamically created TextView in multiple lines but i have got following issues:
The height of this layout doesn't behave as WRAP_CONTENT(as shown in
image given in the link).
I am placing this layout toRightOf an another static
TextViews(To,Cc). It leaves a margin in left equal to TextView width
in every line(as shown in image given in the link).
I want to delete this margin when content switches to next line. How to resolve this?
Any help would be appreciated.
public class FlowLayout extends ViewGroup
{
private int paddingHorizontal;
public FlowLayout(Context context)
{
super(context);
init();
}
public FlowLayout(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
}
private void init()
{
paddingHorizontal = getResources().getDimensionPixelSize(R.dimen.flowlayout_horizontal_padding);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
int lineHeight = 0;
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;
}
child.measure(getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width), getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height));
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
lineHeight = Math.max(childHeight, lineHeight);
if (childWidth + childLeft + getPaddingRight() > myWidth)
{
childLeft = getPaddingLeft();
childTop += 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 += lineHeight;
lineHeight = childHeight;
}
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + paddingHorizontal;
}
}
}
I am using it in my xml like this:
<RelativeLayout
android:id="#+id/main_relative_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<RelativeLayout
android:id="#+id/To_textView_wrapper_relative_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/To_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="#string/to"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp" />
<PackageName.FlowLayout
android:id="#+id/flow_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/To_textView" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/Cc_textView_wrapper_relative_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/To_textView_wrapper_relative_layout" >
<TextView
android:id="#+id/PhoneNo_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="#string/cc"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp" />
<PackageName.FlowLayout
android:id="#+id/flow_container1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/Cc_textView" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/Bcc_textView_wrapper_relative_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/PhoneNo_textView_wrapper_relative_layout" >
<TextView
android:id="#+id/Bcc_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="#string/bcc"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp" />
<PackageName.FlowLayout
android:id="#+id/flow_container2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/Bcc_textView" />
</RelativeLayout>
</RelativeLayout>
I'd like to take an arbitrary Matrix and apply it to an android.views.View.
The only reliable way I've found is this hack:
MyAnimation animation = new MyAnimation(matrix);
animation.setDuration(0);
animation.setFillAfter(true);
view.setAnimation(animation);
Is there a better way? I tried leveraging getChildStaticTransformation and putting it in a parent, but that wasn't working out (maybe I was doing it wrong?)
In the end, I created my own layout based on AbsoluteLayout, added a Matrix to my LayoutParams, leveraged getChildStaticTransformation, and overrode dispatchTouchEvent in order for my child to respond to the correct bounds when rotated. A lot more difficult than I anticipated.
public class UIViewLayout extends ViewGroup {
#Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
if(child instanceof UIViewLayout) {
t.getMatrix().reset();
UIViewLayout.LayoutParams params = (UIViewLayout.LayoutParams)child.getLayoutParams();
t.setTransformationType(Transformation.TYPE_MATRIX);
t.getMatrix().set(params.matrix);
}
return true;
}
public UIViewLayout(android.content.Context context) {
super(context);
this.setClipChildren(false);
this.setClipToPadding(false);
this.setChildrenDrawingOrderEnabled(true);
this.setStaticTransformationsEnabled(true);
}
public UIViewLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public UIViewLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
// Find out how big everyone wants to be
measureChildren(widthMeasureSpec, heightMeasureSpec);
// Find rightmost and bottom-most child
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
int childRight;
int childBottom;
UIViewLayout.LayoutParams lp
= (UIViewLayout.LayoutParams) child.getLayoutParams();
childRight = lp.x + child.getMeasuredWidth();
childBottom = lp.y + child.getMeasuredHeight();
maxWidth = Math.max(maxWidth, childRight);
maxHeight = Math.max(maxHeight, childBottom);
}
}
// Account for padding too
//maxWidth += mPaddingLeft + mPaddingRight;
//maxHeight += mPaddingTop + mPaddingBottom;
// Check against minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
#Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0);
}
#Override
protected void onLayout(boolean changed, int l, int t,
int r, int b) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
UIViewLayout.LayoutParams lp =
(UIViewLayout.LayoutParams) child.getLayoutParams();
int childLeft = lp.x;
int childTop = lp.y;
child.layout(childLeft, childTop,
childLeft + child.getMeasuredWidth(),
childTop + child.getMeasuredHeight());
}
}
}
#Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new UIViewLayout.LayoutParams(getContext(), attrs);
}
// Override to allow type-checking of LayoutParams.
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof UIViewLayout.LayoutParams;
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
#Override
public boolean shouldDelayChildPressedState() {
return false;
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
for(int i = 0; i < this.getChildCount(); i++) {
View child = getChildAt(i);
if(child instanceof UIViewLayout) {
UIViewLayout.LayoutParams params = (UIViewLayout.LayoutParams)child.getLayoutParams();
if(!params.matrix.isIdentity()) {
MotionEvent ev2 = MotionEvent.obtain(ev);
ev2.setLocation(ev2.getX() - params.x, ev2.getY() - params.y);
Matrix m = new Matrix();
params.matrix.invert(m);
ev2.transform(m);
if(child.dispatchTouchEvent(ev2)) {
return true;
}
ev2.recycle();
}
}
}
return super.dispatchTouchEvent(ev);
}
public static class LayoutParams extends ViewGroup.LayoutParams {
public int x;
public int y;
public Matrix matrix;
public LayoutParams(int width, int height, int x, int y) {
super(width, height);
this.x = x;
this.y = y;
this.matrix = new Matrix();
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}