Android: How can I make text views float to the left?
I want to make textviews float to the left (like css floats). The reason I have so many textviews is I want every word in my app to be clickable. So I want this result:
Currently, I have the following xml codes:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="horizontal" android:baselineAligned="false">
<TextView android:text="@string/nicholasStr" android:layout_width="wrap_content" android:textSize="18sp" android:textStyle="bold" android:layout_height="wrap_content" android:id="@+id/nicholas" ></TextView>
<TextView android:text="@string/wasStr" android:layout_width="wrap_content" android:textSize="18sp" android:textStyle="bold" android:id="@+id/was" android:layout_height="wrap_content" ></TextView>
<TextView android:layout_width="wrap_content" android:textSize="18sp" android:textStyle="bold" android:id="@+id/dots" android:layout_height="wrap_content" android:text="@string/dots"></TextView>
<TextView android:layout_width="wrap_content" android:textSize="18sp" android:textStyle="bold" android:layout_height="wrap_content" android:text="@string/older" android:id="@+id/older"></TextView>
<TextView android:layout_width="wrap_content" andr开发者_C百科oid:textSize="18sp" android:textStyle="bold" android:layout_height="wrap_content" android:text="@string/older" android:id="@+id/older"></TextView>
<TextView android:layout_width="wrap_content" android:textSize="18sp" android:textStyle="bold" android:layout_height="wrap_content" android:text="@string/older" android:id="@+id/older"></TextView>
<TextView android:text="@string/older" android:layout_width="wrap_content" android:textSize="18sp" android:textStyle="bold" android:layout_height="wrap_content" android:id="@+id/older"></TextView>
</LinearLayout>
But the result goes like this:
I'm new to android dev, many thanks in advance. :)
What you really need is a row layout that wraps entire views. Unfortunately Android doesn't have one. Lucky for you though, I do! ;)
It is a custom class, but you can use it in xml just fine as long as you fully qualify it, and include the following attrs.xml file under res/values
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RowLayout">
<attr name="android:verticalSpacing" />
<attr name="android:horizontalSpacing" />
</declare-styleable>
</resources>
Here is the source:
package com.example.widget;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.example.widget.R;
public class RowLayout extends ViewGroup {
public static final int DEFAULT_HORIZONTAL_SPACING = 5;
public static final int DEFAULT_VERTICAL_SPACING = 5;
private final int horizontalSpacing;
private final int verticalSpacing;
private List<RowMeasurement> currentRows = Collections.emptyList();
public RowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.RowLayout);
horizontalSpacing = styledAttributes.getDimensionPixelSize(R.styleable.RowLayout_android_horizontalSpacing,
DEFAULT_HORIZONTAL_SPACING);
verticalSpacing = styledAttributes.getDimensionPixelSize(R.styleable.RowLayout_android_verticalSpacing,
DEFAULT_VERTICAL_SPACING);
styledAttributes.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int maxInternalWidth = MeasureSpec.getSize(widthMeasureSpec) - getHorizontalPadding();
final int maxInternalHeight = MeasureSpec.getSize(heightMeasureSpec) - getVerticalPadding();
List<RowMeasurement> rows = new ArrayList<RowMeasurement>();
RowMeasurement currentRow = new RowMeasurement(maxInternalWidth, widthMode);
rows.add(currentRow);
for (View child : getLayoutChildren()) {
LayoutParams childLayoutParams = child.getLayoutParams();
int childWidthSpec = createChildMeasureSpec(childLayoutParams.width, maxInternalWidth, widthMode);
int childHeightSpec = createChildMeasureSpec(childLayoutParams.height, maxInternalHeight, heightMode);
child.measure(childWidthSpec, childHeightSpec);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
if (currentRow.wouldExceedMax(childWidth)) {
currentRow = new RowMeasurement(maxInternalWidth, widthMode);
rows.add(currentRow);
}
currentRow.addChildDimensions(childWidth, childHeight);
}
int longestRowWidth = 0;
int totalRowHeight = 0;
for (int index = 0; index < rows.size(); index++) {
RowMeasurement row = rows.get(index);
totalRowHeight += row.getHeight();
if (index < rows.size() - 1) {
totalRowHeight += verticalSpacing;
}
longestRowWidth = Math.max(longestRowWidth, row.getWidth());
}
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? MeasureSpec.getSize(widthMeasureSpec) : longestRowWidth
+ getHorizontalPadding(), heightMode == MeasureSpec.EXACTLY ? MeasureSpec.getSize(heightMeasureSpec)
: totalRowHeight + getVerticalPadding());
currentRows = Collections.unmodifiableList(rows);
}
private int createChildMeasureSpec(int childLayoutParam, int max, int parentMode) {
int spec;
if (childLayoutParam == LayoutParams.FILL_PARENT) {
spec = MeasureSpec.makeMeasureSpec(max, MeasureSpec.EXACTLY);
} else if (childLayoutParam == LayoutParams.WRAP_CONTENT) {
spec = MeasureSpec.makeMeasureSpec(max, parentMode == MeasureSpec.UNSPECIFIED ? MeasureSpec.UNSPECIFIED
: MeasureSpec.AT_MOST);
} else {
spec = MeasureSpec.makeMeasureSpec(childLayoutParam, MeasureSpec.EXACTLY);
}
return spec;
}
@Override
protected void onLayout(boolean changed, int leftPosition, int topPosition, int rightPosition, int bottomPosition) {
final int widthOffset = getMeasuredWidth() - getPaddingRight();
int x = getPaddingLeft();
int y = getPaddingTop();
Iterator<RowMeasurement> rowIterator = currentRows.iterator();
RowMeasurement currentRow = rowIterator.next();
for (View child : getLayoutChildren()) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
if (x + childWidth > widthOffset) {
x = getPaddingLeft();
y += currentRow.height + verticalSpacing;
if (rowIterator.hasNext()) {
currentRow = rowIterator.next();
}
}
child.layout(x, y, x + childWidth, y + childHeight);
x += childWidth + horizontalSpacing;
}
}
private List<View> getLayoutChildren() {
List<View> children = new ArrayList<View>();
for (int index = 0; index < getChildCount(); index++) {
View child = getChildAt(index);
if (child.getVisibility() != View.GONE) {
children.add(child);
}
}
return children;
}
protected int getVerticalPadding() {
return getPaddingTop() + getPaddingBottom();
}
protected int getHorizontalPadding() {
return getPaddingLeft() + getPaddingRight();
}
private final class RowMeasurement {
private final int maxWidth;
private final int widthMode;
private int width;
private int height;
public RowMeasurement(int maxWidth, int widthMode) {
this.maxWidth = maxWidth;
this.widthMode = widthMode;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public boolean wouldExceedMax(int childWidth) {
return widthMode == MeasureSpec.UNSPECIFIED ? false : getNewWidth(childWidth) > maxWidth;
}
public void addChildDimensions(int childWidth, int childHeight) {
width = getNewWidth(childWidth);
height = Math.max(height, childHeight);
}
private int getNewWidth(int childWidth) {
return width == 0 ? childWidth : width + horizontalSpacing + childWidth;
}
}
}
You can do it easily with Relative layout with orientation = vertical.
for example: on activity_main.xml file
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="@+id/btnButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 1"/>
<Button
android:id="@+id/btnButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button 2"
android:layout_toRightOf="@+id/btnButton1"/>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btnButton3"
android:layout_marginTop="94dp"
android:text="User :"
android:textAppearance="?android:attr/textAppearanceLarge" />
.......
</RelativeLayout>
I found a pretty simple solution to create a menu with rows of options:
public class OptionMenu extends LinearLayout {
private int widthConsumed = 0;
private LinearLayout currentRow;
interface OptionRunnable {
void onOptionSelected(int option);
}
public OptionMenu(Context context) {
super(context);
setOrientation(VERTICAL);
}
public void setOptions(List<Integer> options, OptionRunnable runnable) {
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
Stream.of(options).forEach(option -> {
TextView text = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.incl_textviewer_option_button, OptionMenu.this, false);
text.setText(option);
text.setTag(option);
text.setOnClickListener(v -> runnable.onOptionSelected((Integer) v.getTag()));
text.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
getCurrentRow(text.getMeasuredWidth()).addView(text);
});
return true;
}
});
}
private LinearLayout getCurrentRow(int width) {
if(currentRow == null) {
currentRow = new LinearLayout(getContext());
currentRow.setOrientation(HORIZONTAL);
addView(currentRow);
}
widthConsumed += width;
if(widthConsumed < getWidth())
return currentRow;
LinearLayout newRow = new LinearLayout(getContext());
newRow.setOrientation(HORIZONTAL);
currentRow = newRow;
widthConsumed = width;
addView(newRow);
return newRow;
}
}
精彩评论