Percentage width in a RelativeLayout

2018-12-31 16:22发布

问题:

I am working on a form layout for a Login Activity in my Android App. The image below is how I want it to look like:

\"enter

I was able to achieve this layout with the following XML. The problem is, it\'s a bit hackish. I had to hard-code a width for the host EditText. Specifically, I had to specify:

android:layout_width=\"172dp\" 

I\'d really like to give a percentage width to the host and port EditText\'s . (Something like 80% for the host, 20% for the port.) Is this possible? The following XML works on my Droid, but it doesn\'t seem to work for all screens. I would really like a more robust solution.

<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"
    android:id=\"@+id/main\"
    android:layout_width=\"fill_parent\"
    android:layout_height=\"fill_parent\" >

    <TextView
        android:id=\"@+id/host_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@+id/home\"
        android:paddingLeft=\"15dp\"
        android:paddingTop=\"0dp\"
        android:text=\"host\"
        android:textColor=\"#a5d4e2\"
        android:textSize=\"25sp\"
        android:textStyle=\"normal\" />

    <TextView
        android:id=\"@+id/port_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@+id/home\"
        android:layout_toRightOf=\"@+id/host_input\"
        android:paddingTop=\"0dp\"
        android:text=\"port\"
        android:textColor=\"#a5d4e2\"
        android:textSize=\"25sp\"
        android:textStyle=\"normal\" />

    <EditText
        android:id=\"@+id/host_input\"
        android:layout_width=\"172dp\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@id/host_label\"
        android:layout_marginLeft=\"15dp\"
        android:layout_marginRight=\"15dp\"
        android:layout_marginTop=\"4dp\"
        android:background=\"@android:drawable/editbox_background\"
        android:inputType=\"textEmailAddress\" />

    <EditText
        android:id=\"@+id/port_input\"
        android:layout_width=\"100dp\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@id/host_label\"
        android:layout_marginTop=\"4dp\"
        android:layout_toRightOf=\"@id/host_input\"
        android:background=\"@android:drawable/editbox_background\"
        android:inputType=\"number\" />

    <TextView
        android:id=\"@+id/username_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@+id/host_input\"
        android:paddingLeft=\"15dp\"
        android:paddingTop=\"15dp\"
        android:text=\"username\"
        android:textColor=\"#a5d4e2\"
        android:textSize=\"25sp\"
        android:textStyle=\"normal\" />

    <EditText
        android:id=\"@+id/username_input\"
        android:layout_width=\"fill_parent\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@id/username_label\"
        android:layout_marginLeft=\"15dp\"
        android:layout_marginRight=\"15dp\"
        android:layout_marginTop=\"4dp\"
        android:background=\"@android:drawable/editbox_background\"
        android:inputType=\"textEmailAddress\" />

    <TextView
        android:id=\"@+id/password_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@+id/username_input\"
        android:paddingLeft=\"15dp\"
        android:paddingTop=\"15dp\"
        android:text=\"password\"
        android:textColor=\"#a5d4e2\"
        android:textSize=\"25sp\"
        android:textStyle=\"normal\" />

    <EditText
        android:id=\"@+id/password_input\"
        android:layout_width=\"fill_parent\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@id/password_label\"
        android:layout_marginLeft=\"15dp\"
        android:layout_marginRight=\"15dp\"
        android:layout_marginTop=\"4dp\"
        android:background=\"@android:drawable/editbox_background\"
        android:inputType=\"textPassword\" />

    <ImageView
        android:id=\"@+id/home\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:layout_alignParentTop=\"true\"
        android:layout_centerHorizontal=\"true\"
        android:layout_centerVertical=\"false\"
        android:paddingLeft=\"15dp\"
        android:paddingRight=\"15dp\"
        android:paddingTop=\"15dp\"
        android:scaleType=\"fitStart\"
        android:src=\"@drawable/home\" />

    <Button
        android:id=\"@+id/login_button\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:layout_below=\"@+id/password_input\"
        android:layout_marginLeft=\"15dp\"
        android:layout_marginTop=\"15dp\"
        android:text=\"   login   \"
        android:textSize=\"18sp\" >
    </Button>

</RelativeLayout>

回答1:

You are looking for the android:layout_weight attribute. It will allow you to use percentages to define your layout.

In the following example, the left button uses 70% of the space, and the right button 30%.

<LinearLayout
    android:layout_width=\"match_parent\" 
    android:layout_height=\"wrap_content\"
    android:orientation=\"horizontal\">

    <Button
        android:text=\"left\" 
        android:layout_width=\"0dp\" 
        android:layout_height=\"wrap_content\" 
        android:layout_weight=\".70\" /> 

    <Button
        android:text=\"right\" 
        android:layout_width=\"0dp\" 
        android:layout_height=\"wrap_content\" 
        android:layout_weight=\".30\" />

</LinearLayout>

It works the same with any kind of View, you can replace the buttons with some EditText to fit your needs.

Be sure to set the layout_width to 0dp or your views may not be scaled properly.

Note that the weight sum doesn\'t have to equal 1, I just find it easier to read like this. You can set the first weight to 7 and the second to 3 and it will give the same result.



回答2:

This does not quite answer the original question, which was for a 70/30 split, but in the special case of a 50/50 split between the components there is a way: place an invisible strut at the center and use it to position the two components of interest.

<RelativeLayout 
    android:layout_width=\"match_parent\"
    android:layout_height=\"wrap_content\">
    <View android:id=\"@+id/strut\"
        android:layout_width=\"0dp\"
        android:layout_height=\"0dp\" 
        android:layout_centerHorizontal=\"true\"/>
    <Button
        android:layout_width=\"0dp\"
        android:layout_height=\"wrap_content\"
        android:layout_alignRight=\"@id/strut\"
        android:layout_alignParentLeft=\"true\"
        android:text=\"Left\"/> 
    <Button 
        android:layout_width=\"0dp\"
        android:layout_height=\"wrap_content\"
        android:layout_alignLeft=\"@id/strut\"
        android:layout_alignParentRight=\"true\"
        android:text=\"Right\"/>
</RelativeLayout>

As this is a pretty common case, this solution is more than a curiosity. It is a bit of a hack but an efficient one because the empty, zero-sized strut should cost very little.

In general, though, it\'s best not to expect too much from the stock Android layouts...



回答3:

Update 1

As pointed by @EmJiHash PercentRelativeLayout is deprecated in API level 26.0.0

Below quoting google comment:

This class was deprecated in API level 26.0.0. consider using ConstraintLayout and associated layouts instead. The following shows how to replicate the functionality of percentage layouts with a ConstraintLayout


Google introduced new API called android.support.percent

Then you can just specify percentage to take by view

Add compile dependency like

compile \'com.android.support:percent:22.2.0

in that, PercentRelativeLayout is what we can do a percentage wise layout

 <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\">
     <ImageView
         app:layout_widthPercent=\"50%\"
         app:layout_heightPercent=\"50%\"
         app:layout_marginTopPercent=\"25%\"
         app:layout_marginLeftPercent=\"25%\"/>
 </android.support.percent.PercentRelativeLayout>


回答4:

You cannot use percentages to define the dimensions of a View inside a RelativeLayout. The best ways to do it is to use LinearLayout and weights, or a custom Layout.



回答5:

You can have a look at the new percent support library.

compile \'com.android.support:percent:22.2.0\'

docs

sample



回答6:

You can use PercentRelativeLayout, It is a recent undocumented addition to the Design Support Library, enables the ability to specify not only elements relative to each other but also the total percentage of available space.

Subclass of RelativeLayout that supports percentage based dimensions and margins. You can specify dimension or a margin of child by using attributes with \"Percent\" suffix.

<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\">
  <ImageView
      android:layout_width=\"match_parent\"
      android:layout_height=\"match_parent\"
      app:layout_widthPercent=\"50%\"
      app:layout_heightPercent=\"50%\"
      app:layout_marginTopPercent=\"25%\"
      app:layout_marginLeftPercent=\"25%\"/>
</android.support.percent.PercentFrameLayout>

The Percent package provides APIs to support adding and managing percentage based dimensions in your app.

To use, you need to add this library to your Gradle dependency list:

dependencies {
    compile \'com.android.support:percent:22.2.0\'//23.1.1
}


回答7:

I have solved this creating a custom View:

public class FractionalSizeView extends View {
  public FractionalSizeView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public FractionalSizeView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    setMeasuredDimension(width * 70 / 100, 0);
  }
}

This is invisible strut I can use to align other views within RelativeLayout.



回答8:

Update

As pointed by @EmJiHash PercentRelativeLayout and PercentFrameLayout is deprecated in API level 26.0.0

Consider Using ConstraintLayout

Google introduced new API called android.support.percent

1)PercentRelativeLayout

2)PercentFrameLayout

Add compile dependency like

compile \'com.android.support:percent:23.1.1\'

You can specify dimension in percentage so get both benefit of RelativeLayout and percentage

 <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
         app:layout_widthPercent=\"40%\"
         app:layout_heightPercent=\"40%\"
         app:layout_marginTopPercent=\"15%\"
         app:layout_marginLeftPercent=\"15%\"/>
 </android.support.percent.PercentRelativeLayout/>


回答9:

PercentRelativeLayout is deprecated from Revision 26.0.0 of support Library.

Google introduced new Layout called ConstraintLayout.

Add the library as a dependency in your module-level build.gradle file:

     dependencies {
        compile \'com.android.support.constraint:constraint-layout:1.0.1\'
      }

just add in a layout file:

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<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\">
</android.support.constraint.ConstraintLayout>

Constraints

Constraints help you keep widgets aligned. You can use anchors, such as the constraint handles shown below, to determine alignment rules between various widgets.

\"\"

  1. Wrap Content: The view expands as needed to fit its contents.
  2. Match Constraints: The view expands as needed to meet the definition of its constraints after accounting for margins. However, if the given dimension has only one constraint, then the view expands to fit its contents. Using this mode on either the height or width also allows you to set a size ratio.
  3. Fixed: You specify a specific dimension in the text box below or by resizing the view in the editor.
  4. Spread: The views are evenly distributed (after margins are accounted for). This is the default.
  5. Spread inside: The first and last view are affixed to the constraints on each end of the chain and the rest are evenly distributed.
  6. Weighted: When the chain is set to either spread or spread inside, you can fill the remaining space by setting one or more views to \"match constraints\" (0dp). By default, the space is evenly distributed between each view that\'s set to \"match constraints,\" but you can assign a weight of importance to each view using the layout_constraintHorizontal_weight and layout_constraintVertical_weight attributes. If you\'re familiar with layout_weight in a linear layout, this works the same way. So the view with the highest weight value gets the most amount of space; views that have the same weight get the same amount of space.
  7. Packed: The views are packed together (after margins are accounted for). You can then adjust the whole chain\'s bias (left/right or up/down) by changing the chain\'s head view bias.
  8. Center Horizontally or Center Vertically: To create a chain of views quickly, select them all, right-click one of the views, and then select either Center Horizontally or Center Vertically, to create either a horizontal or vertical chain
  9. Baseline alignment: Align the text baseline of a view to the text baseline of another view.
  10. Constrain to a guideline: You can add a vertical or horizontal guideline to which you can constrain views, and the guideline will be invisible to app users. You can position the guideline within the layout based on either dp units or percent, relative to the layout\'s edge.
  11. Adjust the constraint bias: When you add a constraint to both sides of a view (and the view size for the same dimension is either \"fixed\" or \"wrap content\"), the view becomes centered between the two constraints with a bias of 50% by default. You can adjust the bias by dragging the bias slider in the Properties window
  12. Set size as a ratio: You can set the view size to a ratio such as 16:9 if at least one of the view dimensions is set to \"match constraints\" (0dp).

You can learn more from the official doc.

\"\"



回答10:

Since PercentRelativeLayout was deprecated in 26.0.0 and nested layouts like LinearLayout inside RelativeLayout have a negative impact on performance (Understanding the performance benefits of ConstraintLayout) the best option for you to achieve percentage width is to replace your RelativeLayout with ConstraintLayout.

This can be solved in two ways.

SOLUTION #1 Using guidelines with percentage offset

\"Layout

<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/host_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:text=\"Host\"
        android:layout_marginTop=\"16dp\"
        android:layout_marginLeft=\"8dp\"
        app:layout_constraintTop_toTopOf=\"parent\"
        app:layout_constraintLeft_toLeftOf=\"@+id/host_input\" />

    <TextView
        android:id=\"@+id/port_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:text=\"Port\"
        android:layout_marginTop=\"16dp\"
        android:layout_marginLeft=\"8dp\"
        app:layout_constraintTop_toTopOf=\"parent\"
        app:layout_constraintLeft_toLeftOf=\"@+id/port_input\" />

    <EditText
        android:id=\"@+id/host_input\"
        android:layout_width=\"0dp\"
        android:layout_height=\"wrap_content\"
        android:layout_marginTop=\"8dp\"
        android:layout_marginLeft=\"8dp\"
        android:layout_marginRight=\"8dp\"
        android:inputType=\"textEmailAddress\"
        app:layout_constraintTop_toBottomOf=\"@+id/host_label\"
        app:layout_constraintLeft_toLeftOf=\"parent\"
        app:layout_constraintRight_toLeftOf=\"@+id/guideline\" />

    <EditText
        android:id=\"@+id/port_input\"
        android:layout_width=\"0dp\"
        android:layout_height=\"wrap_content\"
        android:layout_marginTop=\"8dp\"
        android:layout_marginLeft=\"8dp\"
        android:layout_marginRight=\"8dp\"
        android:inputType=\"number\"
        app:layout_constraintTop_toBottomOf=\"@+id/port_label\"
        app:layout_constraintLeft_toLeftOf=\"@+id/guideline\"
        app:layout_constraintRight_toRightOf=\"parent\" />

    <android.support.constraint.Guideline
        android:id=\"@+id/guideline\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:orientation=\"vertical\"
        app:layout_constraintGuide_percent=\"0.8\" />

</android.support.constraint.ConstraintLayout>

SOLUTION #2 Using chain with weighted width for EditText

\"Layout

<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/host_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:text=\"Host\"
        android:layout_marginTop=\"16dp\"
        android:layout_marginLeft=\"8dp\"
        app:layout_constraintTop_toTopOf=\"parent\"
        app:layout_constraintLeft_toLeftOf=\"@+id/host_input\" />

    <TextView
        android:id=\"@+id/port_label\"
        android:layout_width=\"wrap_content\"
        android:layout_height=\"wrap_content\"
        android:text=\"Port\"
        android:layout_marginTop=\"16dp\"
        android:layout_marginLeft=\"8dp\"
        app:layout_constraintTop_toTopOf=\"parent\"
        app:layout_constraintLeft_toLeftOf=\"@+id/port_input\" />

    <EditText
        android:id=\"@+id/host_input\"
        android:layout_width=\"0dp\"
        android:layout_height=\"wrap_content\"
        android:layout_marginTop=\"8dp\"
        android:layout_marginLeft=\"8dp\"
        android:layout_marginRight=\"8dp\"
        android:inputType=\"textEmailAddress\"
        app:layout_constraintHorizontal_weight=\"0.8\"
        app:layout_constraintTop_toBottomOf=\"@+id/host_label\"
        app:layout_constraintLeft_toLeftOf=\"parent\"
        app:layout_constraintRight_toLeftOf=\"@+id/port_input\" />

    <EditText
        android:id=\"@+id/port_input\"
        android:layout_width=\"0dp\"
        android:layout_height=\"wrap_content\"
        android:layout_marginTop=\"8dp\"
        android:layout_marginLeft=\"8dp\"
        android:layout_marginRight=\"8dp\"
        android:inputType=\"number\"
        app:layout_constraintHorizontal_weight=\"0.2\"
        app:layout_constraintTop_toBottomOf=\"@+id/port_label\"
        app:layout_constraintLeft_toRightOf=\"@+id/host_input\"
        app:layout_constraintRight_toRightOf=\"parent\" />

</android.support.constraint.ConstraintLayout>

In both cases, you get something like this

\"Result



回答11:

You can accomplish this via layout weights. A weight dictates how the unclaimed portions of the screen are divided up. Give each EditText a layout_width of 0, and some proportional weight. I.e., give one a weight of 2, and the other a weight of 1 if you want the first to take up twice as much space.



回答12:

Interestingly enough, building on the answer from @olefevre, one can not only do 50/50 layouts with \"invisible struts\", but all sorts of layouts involving powers of two.

For example, here is a layout that cuts the width into four equal parts (actually three, with weights of 1, 1, 2):

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"
    android:layout_width=\"match_parent\"
    android:layout_height=\"wrap_content\" >

    <View
        android:id=\"@+id/strut\"
        android:layout_width=\"1dp\"
        android:layout_height=\"match_parent\"
        android:layout_centerHorizontal=\"true\"
        android:background=\"#000000\" />

    <RelativeLayout
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:layout_toLeftOf=\"@+id/strut\" >

        <View
            android:id=\"@+id/left_strut\"
            android:layout_width=\"1dp\"
            android:layout_height=\"match_parent\"
            android:layout_toLeftOf=\"@+id/strut\"
            android:layout_centerHorizontal=\"true\"
            android:background=\"#000000\" />

        <Button
            android:layout_width=\"0dp\"
            android:layout_height=\"wrap_content\"
            android:layout_alignParentLeft=\"true\"
            android:layout_alignRight=\"@+id/left_strut\"
            android:text=\"Far Left\" />

        <Button
            android:layout_width=\"0dp\"
            android:layout_height=\"wrap_content\"
            android:layout_alignParentRight=\"true\"
            android:layout_toRightOf=\"@+id/left_strut\"
            android:text=\"Near Left\" />
    </RelativeLayout>

        <Button
            android:layout_width=\"0dp\"
            android:layout_height=\"wrap_content\"
            android:layout_alignLeft=\"@id/strut\"
            android:layout_alignParentRight=\"true\"
            android:text=\"Right\" />

</RelativeLayout>


回答13:

Just put your two textviews host and port in an independant linearlayout horizontal and use android:layout_weight to make the percentage



回答14:

Check https://github.com/mmin18/FlexLayout which you can use percent or java expression directly in layout xml.

<EditText
    app:layout_left=\"0%\"
    app:layout_right=\"60%\"
    app:layout_height=\"wrap_content\"/>
<EditText
    app:layout_left=\"prev.right+10dp\"
    app:layout_right=\"100%\"
    app:layout_height=\"wrap_content\"/>