将图片顶部与TextView顶部对齐

44

我想创建一个布局,使得一张图片的顶部与一个TextView的顶部对齐,就像这样:

---------  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.

我尝试通过将android:drawableLeft设置为我的图片来实现,但这会让图片垂直居中:

           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.
这只需要一个TextView就可以实现吗,还是需要创建一个包含TextView和ImageView的RelativeLayout?
这是我的TextView的XML布局,它会给出不正确的布局:
<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" />
< p > android:gravity="top" 属性似乎只影响文本,而不影响可绘制对象。


2
我认为你需要使用RelativeLayout... - amp
无论您需要在顶部还是左侧。 - Shankar Agarwal
正如@amp所说,使用相对布局是最好的。 - MAC
@Claes,你找到答案了吗?我也遇到了类似的问题。 - user3265443
@user3265443 使用自定义的Drawable来包装你的Drawable。请参考我的答案,其中包含示例代码:https://dev59.com/3mkv5IYBdhLWcg3w6k5Q#28906636。 - Reinier
5个回答

33

通过创建自定义Drawable来包装您的Drawable,并重写onDraw(Canvas)方法以操作自定义Drawable的绘制,可以将复合Drawable对齐到顶部(或底部)。

以下示例是最简单的示例。它将图像对齐到顶部,但您还可以通过在onDraw(Canvas)方法中实现所需逻辑来使其对齐到TextView的底部、左侧或右侧。您可能还想在onDraw(Canvas)中建立一个边距,以使设计实现完美。

用法示例:

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);

示例代码:

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();
    }
}

很棒的解决方案。这应该是被接受的答案。 - TharakaNirmana
1
如何使用它将文本和左侧可绘制对象对齐到TextView的左侧 - Reprator
我必须将gravityDrawable的底部边界设置为0: gravityCompoundDrawable.setBounds(0, 0, originalCompoundDrawable.getIntrinsicWidth(), 0); - luckyhandler
1
我不明白为什么这是被接受的答案。除非您还覆盖getOpacity()、setAlpha(...)和setColorFilter(...),否则它将无法编译。那么这三种方法的功能是什么? - Tharkius
2
@Tharkius 这些方法没有被包含在答案中,因为它们与对齐问题无关,但是您说得对,在创建自定义Drawable时需要实现它们,就像我们在这里所做的一样。在这种特定情况下,您可能会对每个方法getOpacity()、setAlpha()和setColorFilter()在mDrawable字段上调用相应的方法。因此,在getOpacity()中,您将返回mDrawable.getOpacity(),在setAlpha()中,您将调用mDrawable.setAlpha(..),等等。这些方法的含义可以在Drawable的文档中找到。 - Reinier
点赞了,我想给这个答案+100分但是我不能。非常感谢@Reinier。我想在图片下面显示文本,请问您能否建议如何使用上述代码实现。谢谢。 - mobi_dev

1

尝试使用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>

1
CompoundDrawable 的目的是尽量减少 View 层次结构中的视图数量。虽然这种解决方案在视觉上看起来相同,但并不能回答问题。 - rds

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>

这样就可以了。


0
使用layout_alignTop。通过它,您可以将视图的顶部与另一个视图的顶部对齐。
<?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>

0

这是对我有效的方法:

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()
                    }
                }
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接