Is there an easy way to add a border to the top and bottom of an Android View?

469,298

Solution 1

In android 2.2 you could do the following.

Create an xml drawable such as /res/drawable/textlines.xml and assign this as a TextView's background property.

<TextView
android:text="My text with lines above and below"
android:background="@drawable/textlines"
/>

/res/drawable/textlines.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
      <shape 
        android:shape="rectangle">
            <stroke android:width="1dp" android:color="#FF000000" />
            <solid android:color="#FFDDDDDD" />

        </shape>
   </item>

   <item android:top="1dp" android:bottom="1dp"> 
      <shape 
        android:shape="rectangle">
            <stroke android:width="1dp" android:color="#FFDDDDDD" />
            <solid android:color="#00000000" />
        </shape>
   </item>

</layer-list>

The down side to this is that you have to specify an opaque background colour, as transparencies won't work. (At least i thought they did but i was mistaken). In the above example you can see that the solid colour of the first shape #FFdddddd is copied in the 2nd shapes stroke colour.

Solution 2

I've used a trick so that the border is displayed outside the container. With this trick only a line is drawn so the background will be shown of the underlying view.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:bottom="1dp"
        android:left="-2dp"
        android:right="-2dp"
        android:top="-2dp">
        <shape android:shape="rectangle" >
            <stroke
                android:width="1dp"
                android:color="#FF000000" />

            <solid android:color="#00FFFFFF" />

            <padding android:left="10dp"
                android:right="10dp"
                android:top="10dp"
                android:bottom="10dp" />
        </shape>
    </item>

</layer-list>

Solution 3

To add a 1dp white border at the bottom only and to have a transparent background you can use the following which is simpler than most answers here.

For the TextView or other view add:

android:background="@drawable/borderbottom"

And in the drawable directory add the following XML, called borderbottom.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:top="-2dp" android:left="-2dp" android:right="-2dp">
        <shape android:shape="rectangle">
            <stroke android:width="1dp" android:color="#ffffffff" />
            <solid android:color="#00000000" />
        </shape>
    </item>
</layer-list>

If you want a border at the top, change the android:top="-2dp" to android:bottom="-2dp"

The colour does not need to be white and the background does not need to be transparent either.

The solid element may not be required. This will depend on your design (thanks V. Kalyuzhnyu).

Basically, this XML will create a border using the rectangle shape, but then pushes the top, right and left sides beyond the render area for the shape. This leaves just the bottom border visible.

Solution 4

Option 1: Shape Drawable

This is the simplest option if you want a border around a layout or view in which you can set the background. Create an XML file in the drawable folder that looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid android:color="#8fff93" />

    <stroke
        android:width="1px"
        android:color="#000" />

</shape>

You can remove the solid if you don't want a fill. The set background="@drawable/your_shape_drawable" on your layout/view.

Option 2: Background View

Here's a little trick I've used in a RelativeLayout. Basically you have a black square under the view you want to give a border, and then give that view some padding (not margin!) so the black square shows through at the edges.

Obviously this only works properly if the view doesn't have any transparent areas. If it does I would recommend you write a custom BorderView which only draws the border - it should only be a few dozen lines of code.

<View
    android:id="@+id/border"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@+id/image"
    android:layout_alignLeft="@+id/image"
    android:layout_alignRight="@+id/image"
    android:layout_alignTop="@+id/main_image"
    android:background="#000" />

<ImageView
    android:id="@+id/image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_...
    android:padding="1px"
    android:src="@drawable/..." />

If you're wondering, it does work with adjustViewBounds=true. However, it doesn't work if you want to have a background in an entire RelativeLayout, because there is a bug that stops you filling a RelativeLayout with a View. In that case I'd recommend the Shape drawable.

Option 3: 9-patch

A final option is to use a 9-patch drawable like this one:

You can use it on any view where you can set android:background="@drawable/...". And yes it does need to be 6x6 - I tried 5x5 and it didn't work.

The disadvantage of this method is you can't change the colours very easily, but if you want fancy borders (e.g. only a border at the top and bottom, as in this question) then you may not be able to do them with the Shape drawable, which isn't very powerful.

Option 4: Extra views

I forgot to mention this really simple option if you only want borders above and below your view. You can put your view in a vertical LinearLayout (if it isn't already) and then add empty Views above and below it like this:

<View android:background="#000" android:layout_width="match_parent" android:layout_height="1px"/>

Solution 5

The currently accepted answer doesn't work. It creates thin vertical borders on the left and right sides of the view as a result of anti-aliasing.

This version works perfectly. It also allows you to set the border widths independently, and you can also add borders on the left / right sides if you want. The only drawback is that it does NOT support transparency.

Create an xml drawable named /res/drawable/top_bottom_borders.xml with the code below and assign it as a TextView's background property.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#DDDD00" /> <!-- border color -->
        </shape>
    </item>

    <item
        android:bottom="1dp" 
        android:top="1dp">   <!-- adjust borders width here -->
        <shape android:shape="rectangle">
            <solid android:color="#FFFFFF" />  <!-- background color -->
        </shape>
    </item>
</layer-list>

Tested on Android KitKat through Marshmallow

Share:
469,298
emmby
Author by

emmby

Author of Android Application Development for Dummies. Author of RoboGuice, as well as the OpenTable, TripIt, and Digg Android applications. bebop Mobile at Google http://about.me/michaelburton

Updated on August 20, 2022

Comments

  • emmby
    emmby almost 2 years

    I have a TextView and I'd like to add a black border along its top and bottom borders. I tried adding android:drawableTop and android:drawableBottom to the TextView, but that only caused the entire view to become black.

    <TextView
        android:background="@android:color/green"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:drawableTop="@android:color/black"
        android:drawableBottom="@android:color/black"
        android:text="la la la" />
    

    Is there a way to easily add a top and bottom border to a View (in particular, a TextView) in Android?

  • emmby
    emmby over 13 years
    Also see this solution, which also works for TextViews, if you want a border all around: stackoverflow.com/questions/3263611/…
  • Matt Briançon
    Matt Briançon almost 13 years
    Try using android:color="@null" to avoid opaque background problem.
  • Chetan
    Chetan about 12 years
    @emmby : when testing this code on tablet , it takes time to render on screen, after 1 sec borders get displayed when I scroll
  • Nikhil Dinesh
    Nikhil Dinesh about 12 years
    Yes, It will Work, I have checked. Button button= (Button) findViewById(R.id.button); button.setBackgroundResource(R.layout.border);
  • Nikhil Dinesh
    Nikhil Dinesh about 12 years
    I have used black color in the above xml file, your background color might be black, try by using some other color. It will definitely work <?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="schemas.android.com/apk/res/android"> <solid android:color="#474848" /> <stroke android:width="1dp" android:color="#ffff00" /> <padding android:left="1dp" android:top="1dp" android:right="1dp" android:bottom="1dp" /> </shape>
  • Emile
    Emile over 11 years
    9 patch and the shape drawable are ok, but i would recommend against adding views or imageviews to solve such a problem. The reason being is that you should be removing as many such <elements> from your layouts to improve layout redrawing and animation. I would strip these out right away and implement some similar to option 1 or 2.
  • Tom
    Tom over 11 years
    @Emile That's true, but if you're using a custom ListView that doesn't support dividers a simple View is fine. The performance impact is next to nil, especially if you're sure to keep your layout depth down. It also circumvents bugs with ListView dividers across devices (I've seen some devices that ignore this property).
  • Emile
    Emile over 11 years
    You can also use the efficient viewholder pattern with listviews to ensure you reduce the number of calls to findViewByID which is often the cause of poor list performance.
  • Jeff
    Jeff about 11 years
    This worked great for me, with a few tweaks to make the line a bit thinner.
  • Max
    Max about 11 years
    Very nice trick, although it could be nicer if we don't have to trick the computer in order to get things done.
  • asakura89
    asakura89 about 11 years
    nicely done! what you should watch is item's left, top, right, bottom should -(stroke's width)dp
  • Rick Pastoor
    Rick Pastoor almost 11 years
    The android:color="@null" trick didn't do the trick for me for keeping transparency and just displaying a border at one side of the view. Answer from user1051892 below did the job!
  • AlikElzin-kilaka
    AlikElzin-kilaka almost 11 years
    Didn't work for me. The border line was painted in the middle of the text view - horizontally.
  • AlikElzin-kilaka
    AlikElzin-kilaka almost 11 years
    This solution made a border on the left and right as well - though a thinner one.
  • AlikElzin-kilaka
    AlikElzin-kilaka almost 11 years
    Didn't show any border - just made my view content look smaller :(
  • Shajeel Afzal
    Shajeel Afzal almost 11 years
    dear! what is the value of "@color/background_trans_light"?
  • ademar111190
    ademar111190 almost 11 years
    why not use @android:color/transparent instead #00000000 to get a better pattern?
  • Emile
    Emile almost 11 years
    Yes, you can use @android:color/transparent. I didn't just for clarity in the example code.
  • Ratata Tata
    Ratata Tata over 10 years
    In iOS you have only one way to do that. On android you should test at least four, in my case option 4 was the way to go. Sometimes drawables as background hang to render, be aware of it.
  • HumaN
    HumaN over 10 years
    Nicely done, worked with transparent background. Tested in 2.2 and 4.4.2. Should cover all in between :)
  • MaxEcho
    MaxEcho over 10 years
    Working without background color, great answer.
  • andrea.rinaldi
    andrea.rinaldi over 10 years
    That's the right one! This one also works on Android 4.0.3 and higher.
  • shanehoban
    shanehoban about 10 years
    Cheers @phreakhead, this actually works for me! No top, left, or right border. Exactly what I needed, thanks!
  • developer82
    developer82 about 10 years
    Thanks! I was messing around to try to do such a thing for half a day now. You just solved it to me :) much appreciated!
  • markshiz
    markshiz about 10 years
    This is broken on Ice Cream Sandwich. A line shows on the right hand edge.
  • Dylan Vander Berg
    Dylan Vander Berg almost 10 years
    @kilaka If you changed the colors, make sure that you changed them in the bottom item too.
  • Francisco Romero
    Francisco Romero almost 9 years
    Please can you explain why when you put -2dp it dissapears and when you put -1dp it is still visible? Thanks!
  • Greg Ennis
    Greg Ennis over 8 years
    This is the best solution, as it does not create overdraw like the others
  • Hitesh Sahu
    Hitesh Sahu almost 8 years
    How to make inside transparent ?
  • chntgomez
    chntgomez almost 8 years
    @HiteshSahu I think the downside of this is that you would have to match the background of the layout with the background of the widget here
  • Denys Kniazhev-Support Ukraine
    Denys Kniazhev-Support Ukraine almost 8 years
    Is there a reason that negative positions are twice as big as stroke with (-2dp vs 1dp)? Seems to work when they are equal in amount (-1 and 1).
  • Tigger
    Tigger almost 8 years
    @DenisKniazhev : When using -2dp the result was always clean. I think there was test case where the result was not clean. I can't remember if it was a low or high density screen or even an older version of Android (2.x maybe).
  • Denys Kniazhev-Support Ukraine
    Denys Kniazhev-Support Ukraine almost 8 years
    Interesting. Do you know if this is a general case, for example if I use 4dp for stroke width should I use -8dp for positioning?
  • Tigger
    Tigger almost 8 years
    @DenisKniazhev : (from memory) it looked like a floating point calculation that was just a little to big or small. This means a value of -5dp would be all that is needed with a width value of 4pd. It simply needed to be a little bigger to get over the float value inaccuracy.
  • Asif Mushtaq
    Asif Mushtaq about 7 years
    Why android didn't have the easiest solution for such things? as in other language, like HTML, CSS, or something JavaFX, WPF......
  • SMBiggs
    SMBiggs about 7 years
    Pretty simple. You control the size and direction of outlines by the margins of the inner View. Thanks.
  • Kevin D.
    Kevin D. about 7 years
    This solution works - I just don't like having to explain myself to the next dev why I added negative values. This is, after all, a hack. Nicely done though.
  • Vinay Vissh
    Vinay Vissh almost 7 years
    Using "Option 2: Background View (in Relative Layout)" and it is working like a charm. Thanks a lot :)
  • xarlymg89
    xarlymg89 over 6 years
    @emmby I suggest to select this answer as the valid one, since it doesn't add a background and it only adds a border to the bottom. Will be more easy for other people to spot the right answer.
  • Dino Tw
    Dino Tw about 6 years
    Simple and elegant.
  • Vlad
    Vlad about 6 years
    As for me solid is redunt
  • DawnYu
    DawnYu about 6 years
    I delete this line <solid android:color="#00FFFFFF" />, it also works~
  • Big McLargeHuge
    Big McLargeHuge almost 5 years
    Option 1 sounds good but it creates borders to the left and right as well.
  • Maveňツ
    Maveňツ almost 4 years
    @phreakhead better if you add some explanation to this
  • gumuruh
    gumuruh about 2 years
    aaah you are my saviour...! :D