64

In my app I have a header bar which consists of a single textview with fill_parent as width, which have a specific background color and some centered text. Now I want to add a drawable on the left side of the header bar, so I set the drawableLeft and sure enough the text and image is displayed. However the problem here is that the text is no longer properly centered, e.g., when the drawable is added the text is shifted a bit to the right as shown in the screenshots here:

without drawable
with drawable

Is there anyway I can center the text properly and have the drawable positioned as it is above without using an additional layout item (such as a LinearLayout)?

blahdiblah
  • 33,069
  • 21
  • 98
  • 152
Jacob
  • 3,521
  • 6
  • 26
  • 34

23 Answers23

32

Though fragile, you can avoid the use of a wrapper Layout by setting a negative padding on the drawable:

<TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerHorizontal="true"
    android:drawableLeft="@drawable/icon"
    android:drawablePadding="-20sp"
    android:text="blah blah blah" />

You'll have to adjust the padding to the width of the drawable, but you're left with just a single TextView instead of an extra LinearLayout or etc.

blahdiblah
  • 33,069
  • 21
  • 98
  • 152
  • 26
    I don't think android:drawablePadding="-20sp" is correct, you should use android:gravity="center" instead! – cwhsu Mar 02 '14 at 01:34
  • 4
    You should do it java code by getting exact width of drawable you set. gravity center will not work – Akhil Dad Apr 25 '15 at 07:40
  • 2
    Setting the drawable padding like this could have different behaviors on different APIs. It is also not safe to use it like this. – Ionut Negru Aug 29 '15 at 09:10
  • @cwhsu setting `android:gravity="center"` will have different effect if `RelativeLayout` is parent. – Faizan Mubasher Jun 24 '18 at 09:35
  • this answer is not correct,if the width of the text view is match parent,then drawable is getting shown at the start of the TextView. – Ajay Chauhan Dec 07 '18 at 07:21
  • 1
    If you have dynamic button size and what not this will unfortunately not be your silver bullet. So either do a custom view or wrap it inside a view group - which is super annoying... – slott Jan 03 '19 at 15:27
14

You can set the parent of the TextView as a RelativeLayout whose width is match_parent.

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<TextView
        android:id="@+id/edit_location_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/add_location_text_view"
        android:gravity="start|center_vertical"
        android:layout_centerHorizontal="true"
        android:drawableStart="@android:drawable/ic_menu_edit"
        android:text="Edit Location" />

</RelativeLayout>

This is what the result looks like.

Rohan Taneja
  • 9,687
  • 3
  • 36
  • 48
6

I've faced a similar problem. Solved it by using single TextView with layout_width="wrap_content" and layout_gravity="center" parameters:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:drawableLeft="@drawable/some_drawable"
    android:text="some text"
    android:layout_gravity="center"
    android:gravity="center"
    />
amukhachov
  • 5,822
  • 1
  • 41
  • 60
  • 5
    if width is wrap_content, then whats the point of center? Tricky part is to center when width is set to match_parent or some other value. Would be good to point this case. – Safeer Nov 15 '19 at 19:56
5

You can use android:gravity="center_vertical" to center the text vertically with the image. Or it can be centered inside the textView using android:gravity="center".

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:drawableLeft="@drawable/icon"
    android:text="Your text here" />
Quentin Colle
  • 246
  • 2
  • 8
5

Try following:

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:padding="10dp"
    android:textAlignment="center" />
Harshil
  • 914
  • 1
  • 12
  • 26
4

That work for me.

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableLeft="@drawable/trusted"
        android:gravity="center"
        android:text="@string/trusted"/>

</LinearLayout>
Hafiz Waleed Hussain
  • 1,062
  • 12
  • 25
3

use this way

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/White"
     >
  
  <RelativeLayout
       android:id="@+id/rlheader"
       android:layout_width="fill_parent"
       android:layout_height="50dp"
       android:layout_alignParentTop="true"
       android:background="@color/HeaderColor"
      
        >
 
        <TextView
           
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_centerHorizontal="true"
           android:layout_centerVertical="true"
           android:text="Header_Title"
            />
 
        <ImageView
           android:id="@+id/ivSetting"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentLeft="true"
           android:layout_centerVertical="true"
           android:layout_marginLeft="10dp"
           android:src="@drawable/setting" />
 
        
    </RelativeLayout>

</RelativeLayout>
      

it look something like this snap of my demo project

Header Image

Community
  • 1
  • 1
Khan
  • 7,585
  • 3
  • 27
  • 44
  • Thanks, is it not possible to do something similar in a linearlayout or would I need to use a relative layout somewhere? – Jacob Jun 29 '12 at 13:01
  • ya u can use parent layout as linear it works for u but child layout is relative in which imageview and textview i have put remain as it is – Khan Jun 29 '12 at 13:10
  • Why this iOS style back button in an Android app? – Christopher Perry Aug 13 '13 at 17:03
  • @ChristopherPerry because of the requirment of client – Khan Aug 14 '13 at 04:39
  • 6
    IMO it's your job as an Android developer, providing a service for your client regarding the Android platform, to inform the client that this is an iOS and not an Android pattern. I would flat out refuse to implement this if they still insisted and find another client. – Christopher Perry Aug 14 '13 at 17:32
  • @ChristopherPerry yes you are right i am working as android developer in company as project manaager told me than i have to do that way if i will be in that position than i am sure i told same as your reply.Thanks for giving me suggestion – Khan Aug 15 '13 at 04:31
2
> This line is important ->> android:gravity="start|center_vertical


 <LinearLayout
            android:layout_width="match_parent"
            android:layout_margin="@dimen/marginplus2"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/like"
                android:gravity="start|center_vertical"
                android:drawablePadding="@dimen/marginplus1"
                android:drawableLeft="@drawable/ic_like"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_width="0dp"
                android:layout_weight="1.5"
                android:text="@string/comments"
                android:drawablePadding="@dimen/marginplus1"
                android:gravity="start|center_vertical"
                android:drawableLeft="@drawable/ic_comment"
                android:layout_height="wrap_content" />
            <TextView
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/share"
                android:drawablePadding="@dimen/marginplus1"
                android:gravity="start|center_vertical"
                android:drawableLeft="@drawable/ic_share"
                android:layout_height="wrap_content" />

        </LinearLayout>
2

Try flowing:

public class DrawableCenterTextView extends AppCompatTextView {

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

    public DrawableCenterTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public DrawableCenterTextView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // We want the icon and/or text grouped together and centered as a group.
        // We need to accommodate any existing padding
        final float buttonContentWidth = getWidth() - getPaddingLeft() - getPaddingRight();

        float textWidth = 0f;
        final Layout layout = getLayout();
        if (layout != null) {
            for (int i = 0; i < layout.getLineCount(); i++) {
                textWidth = Math.max(textWidth, layout.getLineRight(i));
            }
        }

        // Compute left drawable width, if any
        Drawable[] drawables = getCompoundDrawables();
        Drawable drawableLeft = drawables[0];

        int drawableWidth = (drawableLeft != null) ? drawableLeft.getIntrinsicWidth() : 0;

        // We only count the drawable padding if there is both an icon and text
        int drawablePadding = ((textWidth > 0) && (drawableLeft != null)) ? getCompoundDrawablePadding() : 0;

        // Adjust contents to center
        float bodyWidth = textWidth + drawableWidth + drawablePadding;
        int translate = (int) ((buttonContentWidth - bodyWidth));
        if (translate != 0)
            setPadding(translate, 0, translate, 0);
        super.onDraw(canvas);
    }
}
sanjeev
  • 1,664
  • 19
  • 35
yifei chen
  • 91
  • 2
  • Not sure why you're downvoted, this is a better solution than half of the upvoted answers here. However you should add a description of why this works. – Tom Oct 29 '20 at 22:43
  • this answer supports only the text + drawable being centered inside the full width of the textview, should be the accepted answer! – Marcos Vasconcelos Mar 24 '23 at 16:29
1

I had same problem and I solved it with small changes in Textview,

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:drawableLeft="@drawable/search_red"
    android:text="your text goes here"
    />
Deepak Ganachari
  • 378
  • 5
  • 13
1

drawable is part of the TextView, so the textview's height will be the drawable's height if your text's height is smaller than drawable's height.

You can set TextView's gravity attribute to center_vertical, so the text will be in the center.

abcfrom0
  • 31
  • 2
1

One simple solution is to use a transparent image with the same size on the opposite side of the textview. In my example i copied my actual vector_image and changed the colors to be transparent.

<TextView
        android:id="@+id/name"
        style="@style/TextStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableEnd="@drawable/ic_vector_caret_down_green"
        android:drawableStart="@drawable/ic_vector_space_18"
        android:gravity="center"
        android:padding="12dp"
        android:textColor="@color/green"/>
Furedal
  • 523
  • 2
  • 8
  • 19
1

If none of the above solutions did not work for you?. Then try below answer

Sample screen shot of the design. enter image description here

For the above image please find the code below.

<?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="match_parent">

    <android.support.v7.widget.CardView
        android:id="@+id/my_card"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:id="@+id/bb"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:divider="@color/gray"
            android:orientation="horizontal"
            android:weightSum="2">

            <TextView
                android:id="@+id/filter_tv"
                style="?android:attr/buttonBarButtonStyle"
                android:layout_width="wrap_content"
                android:layout_height="?attr/actionBarSize"
                android:layout_gravity="center_horizontal"
                android:layout_weight="1"
                android:drawableLeft="@drawable/ic_baseline_filter_list"
                android:gravity="center|fill_horizontal"
                android:paddingLeft="60dp"
                android:text="Filter"
                android:textAllCaps="false"
                android:textStyle="normal" />

            <View
                android:layout_width="1dp"
                android:layout_height="match_parent"
                android:layout_below="@+id/bb"
                android:background="@color/gray" />

            <TextView
                android:id="@+id/sort_tv"
                style="?android:attr/buttonBarButtonStyle"
                android:layout_width="wrap_content"
                android:layout_height="?attr/actionBarSize"
                android:layout_gravity="center|center_horizontal"
                android:layout_weight="1"
                android:drawableLeft="@drawable/ic_baseline_sort"
                android:drawablePadding="20dp"
                android:gravity="center|fill_horizontal"
                android:paddingLeft="40dp"
                android:text="Sort"
                android:textAllCaps="false" />

        </LinearLayout>
    </android.support.v7.widget.CardView>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.9dp"
        android:layout_below="@+id/my_card"
        android:background="@color/gray" />
</RelativeLayout>
Suresh Maidaragi
  • 2,173
  • 18
  • 25
1

The accepted answer is not working for me, it fails if TextView width is match parent I did it using FrameLayout.

 <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:background="#000">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:drawableStart="@drawable/ic_circle_check_white"
            android:drawablePadding="10dp"
            android:text="Header"
            android:textColor="#fff"
            android:textSize="14sp"/>

    </FrameLayout>
Ajay Chauhan
  • 1,471
  • 4
  • 17
  • 37
1

no test too much, another way

    class CenterDrawableToggleButton @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
    ) : AppCompatToggleButton(context, attrs, defStyleAttr) {

        private val centerMatrix = Matrix()

        override fun onDraw(canvas: Canvas?) {
            canvas?.save()?:return
            centerMatrix.reset()
            centerMatrix.postTranslate(width / 2F - (getDrawWidth() / 2), 0F)
            canvas.concat(centerMatrix)
            super.onDraw(canvas)
            canvas.restore()
        }

        private fun getDrawWidth(): Float {
            return paint.measureText(text.toString()) + compoundPaddingRight + compoundPaddingLeft
        }
    }
mixer
  • 1,583
  • 14
  • 27
1

Wrap your TextView inside a LinearLayout. Then set android:gravity="center_horizontal" to your LinearLayout..

<LinearLayout
        android:layout_width="0dp"
        android:gravity="center_horizontal"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawableStart="@drawable/your_drawable"
            android:drawablePadding="5dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:textAlignment="center"
            android:textColor="@color/ic_text_color"
            android:textSize="15sp"
            tools:text="Your Text.." />

    </LinearLayout>
abhi
  • 961
  • 9
  • 13
0

u can set your header like this

<RelativeLayout
             android:id="@+id/linearLayout1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="@drawable/head" >

             <ImageView
                 android:id="@+id/cat"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"

                 android:layout_marginLeft="5dp"
                 android:onClick="onClick"
                 android:layout_marginTop="10dp"
                 android:src="@drawable/btn_back_" />


         <RelativeLayout
             android:id="@+id/linearLayout1"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:gravity="center" >

           <TextView
 android:id="@+id/TvTitle"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text=""
 android:layout_marginLeft="10dp"
 android:layout_marginRight="10dp"
 android:textColor="#000000" 

 android:textSize="20sp"/>
             </RelativeLayout>

         </RelativeLayout>

Just Give Hieght of Textview,image source as per your need

Aamirkhan
  • 5,746
  • 10
  • 47
  • 74
  • 1
    I realize there are multiple ways of doing this including your above answer, however I do wish to try and limit the amount of elements in the layout so I'm looking for a solution that only uses one textview, or knowledge about whether that is possible or not. – Jacob Jun 29 '12 at 12:46
0
<TextView
    android:id="@+id/distance"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:drawableLeft="@drawable/distance"
    android:drawablePadding="10dp"
    android:padding="10dp"
    android:textAlignment="center"
    android:textColor="#ffffff"
    android:textSize="20sp" />
Harshil
  • 914
  • 1
  • 12
  • 26
0

Add this to your textview instead of wrapping with another layout

android:gravity="center|start"
All Іѕ Vаиітy
  • 24,861
  • 16
  • 87
  • 111
-1

simple as this.. try it..

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center|left"
                android:drawableLeft="@drawable/icon"
                android:text="something something" />
Mr.Lights
  • 29
  • 4
-1

the best way is to use ConstraintLayout, to make the textview at the center of the layout and the width warp_content(don't use 0dp now) so that the drawable will follow the text.

<TextView
    android:id="@+id/tv_test"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="test"
    android:drawableLeft="@drawable/you_drawable"
    android:drawablePadding="5dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    />
sumang_87
  • 19
  • 1
-2

Remove the DrawableLeft and use a container FrameLayout.

<FrameLayout
     android:id="@+id/container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content" >

     <ImageView
         android:id="@+id/image"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginLeft="5dp"
         android:layout_gravity="center_vertical"
         android:src="@drawable/image" />

     <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Text"
        android:layout_gravity="center" />
</FrameLayout>
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • That's not an answer, that's an ugly workaround :)). P.S. As you've stated don't "answer questions for points" – Farid Mar 24 '19 at 01:42
  • @farid I provided this answer because drawableLeft is untrustworthy. Just make sure the text won't overlap the image if it's too long. Set the left and right margins correctly – EpicPandaForce Mar 24 '19 at 11:26
-18

The simplest way to obtain that is:

<RelativeLayout
         android:id="@+id/linearLayout1"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@drawable/head" >

         <ImageView
             android:id="@+id/cat"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginLeft="5dp"
             android:onClick="onClick"
             android:layout_marginTop="10dp"
             android:layout_alignParentLeft="true"
             android:src="@drawable/btn_back_d" />

       <TextView
            android:id="@+id/TvTitle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text=""
            android:layout_centerHorizontal="true" 
            android:textColor="#000000" 
            android:textSize="20sp"/>

Where

  • ImageView has the alignParentLeft setted true and
  • TextView has the centerHorizontal setted true
Stefano Ortisi
  • 5,288
  • 3
  • 33
  • 41
  • Thanks, is it not possible to do something similar in a linearlayout or would I need to use a relative layout somewhere? – Jacob Jun 29 '12 at 13:01
  • Why you want use linearlayout? Relative Layout is the more flexible solution and it doesn't take any much load than LinearLayout. If you want read more about efficence differences between the two solutions you can check this link: http://stackoverflow.com/questions/4069037/android-is-a-relativelayout-more-expensive-than-a-linearlayout – Stefano Ortisi Jun 29 '12 at 13:03
  • well my overall design is much more suited for a linear layout I think so I would have to do header, so if I could avoid using that extra linearlayout it would be great. Of course if that's not possible its ok as well I guess. – Jacob Jun 29 '12 at 13:06
  • Funnily enough, this is the only solution that worked for me, so I don't really get the downvotes. Drawable left sucks, this works. However, it CAN be simplified using FrameLayout and `layout_gravity`. – EpicPandaForce Jun 05 '18 at 14:53