Andy
Andy

Reputation: 10840

Alternative to LinearLayouts' layout_weight attribute?

I need a View to hold a number of TextViews, and the exact number I will not know unfortunately. I need the TextViews to sort of stack either under each other or right next to each other (imagine Tetris, but with just rectangles or squares). The way I have tried thinking about it is using LinearLayout to hold them either horizontally or vertically. I then use their weights to stretch them appropriately if they are next to each other. Otherwise, I use a vertical orientation. Problem is the performance with nested LinearLayouts with more complicated stacks. I thought about using RelativeLayout, but that wouldn't work because I need the TextViews to not overlap. So like if they are next to each other, they need to each take enough space evenly. With layout_weight it works great. I was hoping someone had any idea on how to make this work right/alternative.

Heres an example that is giving me a warning (only a simple example of what I am doing programmatically):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<LinearLayout 
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_weight="1">
    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Hello World 1"
        android:background="#000000"
        android:layout_weight="1"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Hello World 3"
        android:background="#000000"
        android:layout_weight="1"/>
        <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Hello World 4"
        android:background="#000000"
        android:layout_weight="1"/>
</LinearLayout>
<LinearLayout 
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_weight="1">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World 2"
        android:background="#000000" />
</LinearLayout>

I've tried it with RelativeLayout by the way, but one of the TextView's needs to be a set size, but I need them to take space evenly. I won't know specific sizes. Measuring the screen width and dividing it that way is also both clunky and not precise. I appreciate anybodies input.

Edit: So I've been thinking about it some more and thought of a solution using RelativeLayout. There is a nice example in the Android docs, but the only problem is one of the Views needs to be a set size so the other one could stretch. But if there is a way to allow all of them not to have a set size, that could work too, so then they can stretch. Anyone tried doing that at some point?

Upvotes: 1

Views: 2302

Answers (2)

Nitesh Tiwari
Nitesh Tiwari

Reputation: 4762

Update: As we know the percent support library is deprecated from API level 26. ConstraintLayout is the new way to achieve the same flat xml structure.

Updated Github Project

Updated Samples:

<android.support.constraint.ConstraintLayout 
    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/fifty_thirty"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ffff8800"
        android:gravity="center"
        android:text="@string/fifty_fifty_text"
        android:textColor="@android:color/white"
        app:layout_constraintHeight_default="percent"
        app:layout_constraintHeight_percent="0.5"
        android:textSize="25sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_default="percent"
        app:layout_constraintWidth_percent="0.5" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ffff5566"
        android:gravity="center"
        android:text="@string/fifty_fifty_text"
        android:textColor="@android:color/white"
        android:textSize="25sp"
        app:layout_constraintHeight_default="percent"
        app:layout_constraintHeight_percent="0.5"
        app:layout_constraintLeft_toRightOf="@id/fifty_thirty"
        app:layout_constraintTop_toBottomOf="@id/fifty_thirty"
        app:layout_constraintWidth_default="percent"
        app:layout_constraintWidth_percent="0.5" />

</android.support.constraint.ConstraintLayout>

Deprecated

update: We can use android support library for the same.

compile 'com.android.support:percent:23.0.0'

Consider this demo for dividing the screen into 50-50 percent.

<android.support.percent.PercentRelativeLayout
    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/fifty_huntv"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff7acfff"
        android:text="20% - 50%"
        android:textColor="@android:color/white"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="50%" />
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_toRightOf="@id/fifty_huntv"
        android:background="#ffff5566"
        android:text="80%-50%"
        app:layout_heightPercent="80%"
        app:layout_widthPercent="50%"
        />

</android.support.percent.PercentRelativeLayout>

Output:

percent support library demo

Demo HERE!!!

GitHub Project HERE!!!

Upvotes: 3

devunwired
devunwired

Reputation: 63303

It's a bit unclear what your final use case looks like, so I don't know if this will completely solve your problem, but you might take a look at TableLayout (docs link). It's based on LinearLayout so items can still be defined to evenly occupy a given space, but it allows you to define your positions and spans in terms of rows and columns.

So, for instance, your example code would look like:

<TableLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:stretchColumns="*" >
    <!-- Wrap all items in a given row in a TableRow -->
    <TableRow>
        <TextView
            android:text="Hello World 1"
            android:background="#000000"/>
        <TextView
            android:text="Hello World 3"
            android:background="#000000"/>
        <TextView
            android:text="Hello World 4"
            android:background="#000000"/>
    </TableRow>
    <!-- If a child occupies an entire row, it can be by itself -->
    <TextView
        android:text="Hello World 2"
        android:background="#000000"/>
</TableLayout>

You can also use the android:layout_column and android:layout_span attributes to define exactly which column an item should be in, and how many columns it should occupy. Also note that TableLayout and TableRow have basically ignore any layout params applied and use very specific (documented) parameters, so adding them to your code will only confuse what is actually going on. Of course, this can all be built programmatically as well as in XML.

If this does not provide the flexibility you need, I would then recommend creating your own custom layout that either extends or is based on LinearLayout. The mechanism is uses to measure children with weight is not that complex, and then you can override how each child is placed after measurement. Here is a source link to LinearLayout if you want to see how the platform does these things.

Upvotes: 3

Related Questions