Align top of image to top of TextView
Asked Answered
G

5

44

I want to create a layout that aligns the top of an image to the top of a TextView like this:

---------  Text text text text text text text
| Image |  text text text text text text text
---------  text text text text text text text
           text text text text text text text
           text text text text text.

I tried doing this by setting android:drawableLeft to my image, but that centers the image vertically:

           Text text text text text text text
---------  text text text text text text text
| Image |  text text text text text text text
---------  text text text text text text text
           text text text text text.

Is this possible with just a TextView or do I need to create a RelativeLayout containing a TextView and an ImageView?

Here is the XML layout for my TextView that gives the incorrect layout:

<TextView
    android:gravity="top"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:drawableLeft="@drawable/image"
    android:drawablePadding="8dp"
    android:text="@string/text" />

The android:gravity="top" attribute only seems to affect the text, not the drawable.

Gotham answered 22/4, 2012 at 10:58 Comment(5)
I think you need to use a relativeLayout...Dill
whether you need at top or leftMongolia
as @Dill says use Relative layout Its best..Whitethorn
@Gotham Did you find answer to this? I am also facing similar problemSnatch
@Snatch Use a custom Drawable that wraps your Drawable. See my answer for sample code: https://mcmap.net/q/340244/-align-top-of-image-to-top-of-textview.Hymie
H
33

You can align a compound-Drawable to the top (or bottom) by creating a custom Drawable that wraps your Drawable, and then manipulate the drawing of your custom Drawable by overriding the method onDraw(Canvas).

The sample below is the simplest possible example. This aligns the image to the top, but you can also make it align to the bottom, left or right of the TextView by implementing the required logic in the onDraw(Canvas)-method. You might also want to build in a margin in the onDraw(Canvas), to make your design implementation pixel perfect.

Sample usage:

GravityCompoundDrawable gravityDrawable = new GravityCompoundDrawable(innerDrawable);
// NOTE: next 2 lines are important!
innerDrawable.setBounds(0, 0, innerDrawable.getIntrinsicWidth(), innerDrawable.getIntrinsicHeight());
gravityDrawable.setBounds(0, 0, innerDrawable.getIntrinsicWidth(), innerDrawable.getIntrinsicHeight());
mTextView.setCompoundDrawables(gravityDrawable, null, null, null);

Sample code:

public class GravityCompoundDrawable extends Drawable {

    // inner Drawable
    private final Drawable mDrawable;

    public GravityCompoundDrawable(Drawable drawable) {
        mDrawable = drawable;
    }

    @Override
    public int getIntrinsicWidth() {
        return mDrawable.getIntrinsicWidth();
    }

    @Override
    public int getIntrinsicHeight() {
        return mDrawable.getIntrinsicHeight();
    }

    @Override
    public void draw(Canvas canvas) {
        int halfCanvas= canvas.getHeight() / 2;
        int halfDrawable = mDrawable.getIntrinsicHeight() / 2;

        // align to top
        canvas.save();
        canvas.translate(0, -halfCanvas + halfDrawable);
        mDrawable.draw(canvas);
        canvas.restore();
    }
}
Hymie answered 6/3, 2015 at 19:57 Comment(6)
great solution. This has to be the accepted answer.Medeiros
how to used it to align text and left drawable to left of textviewStokeontrent
I had to set the bottom bounds of the gravityDrawable to 0: gravityCompoundDrawable.setBounds(0, 0, originalCompoundDrawable.getIntrinsicWidth(), 0);Jordanson
I don't understand how this is the accepted answer. It won't compile unless you also override getOpacity(), setAlpha(...) and setColorFilter(...). What would be the functionality for those 3 methods?Elfin
@Elfin Those methods were ommitted from the answer because they are not related to the problem of alignment, but you're right that they need to be implemented when you are creating a custom Drawable, as we are doing here. In this specific case you would probably call the respective method on the mDrawable field for each of the methods getOpacity(), setAlpha() and setColorFilter(). So in getOpacity() you would return mDrawable.getOpacity(), in setAlpha() you would call mDrawable.setAlpha(..), etc. The meaning of those methods you can find in the documentation of Drawable.Hymie
Upvoted , I want to give +100 to this answer but I cant.Thanks a lot @Reinier. I want to show the text below the image so could you please suggest how to do that with the above code.ThanksExpressly
S
1

Try to USe RelativeLayout......

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

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/imageView1"
        android:text="@string/text" />

</RelativeLayout>
Schilt answered 23/4, 2012 at 4:26 Comment(1)
The point of CompoundDrawable is to keep minimize the number of views in the the View hierarchy. This solution doesn't answer the question, even though it will visually look the same.Conley
A
0
<?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" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/imageView1"
        android:text="@string/text" />

</RelativeLayout>

This would do it.

Aube answered 30/11, 2012 at 5:33 Comment(0)
H
0

Use layout_alignTop. With it you can align the top of the view to the top of another view

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

    <TextView
        android:id="@+id/textView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/text" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/textView1"
        android:layout_toLeftOf="@id/textView1"
        android:src="@drawable/ic_launcher" />    

</RelativeLayout>
Hymnody answered 20/2, 2014 at 8:5 Comment(0)
M
0

This is what has worked for me:

class MyTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, style) {
    private val leftDrawable = ContextCompat.getDrawable(context, R.drawable.checkmark)

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        setBulletPoint(compoundDrawables[0], canvas)
    }

    private fun setBulletPoint(drawableLeft: Drawable?, canvas: Canvas?) {
        if (!TextUtils.isEmpty(text)) {
            leftDrawable?.let { drlft ->
                if (lineCount == 1) {
                    setCompoundDrawablesWithIntrinsicBounds(drlft, null, null, null)
                } else {
                    val buttonWidth = drlft.intrinsicWidth
                    val buttonHeight = drlft.intrinsicHeight
                    val topSpace = abs(buttonHeight - lineHeight) / 2
           
                    drlft.setBounds(0, topSpace, buttonWidth, topSpace + buttonHeight)
                    canvas?.apply {
                        save()
                        drlft.draw(canvas)
                        restore()
                    }
                }
            }
        }
    }
}
Moorehead answered 7/9, 2020 at 13:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.