如何在Android TextView中将drawableLeft与顶部对齐而不是居中对齐?

53
TextView中,我设置了drawableLeft,其中drawable显示在中心位置。我需要将TextView内的drawableLeft与顶部对齐,就像这张图片一样。

enter image description here

是否有可能实现这个效果?

2
不是很简单。没有 drawableTopLeft。所以,要实现你想要的效果,可以使用一个单独的 ImageView 或者创建一个继承 TextView 的新控件。 - Phantômaxx
@FrankN.Stein,你可能是对的。你能否详细说明一下自定义控件? - androidcodehunter
@Sharif,你的文本视图高度是多少?它有固定的高度吗? - Naruto Uzumaki
@NarutoUzumaki TextView 的高度不是固定的。随着文本大小的增加,它会自动增长。 - androidcodehunter
1
这是一个广泛的话题。Lars Vogel 在这里进行了解释:http://www.vogella.com/tutorials/AndroidCustomViews/article.html,官方参考文献在这里:http://developer.android.com/guide/topics/ui/custom-components.html。 - Phantômaxx
显示剩余3条评论
7个回答

13

使用SpannableString和ImageSpan来实现这一点。

String msg=" "+"haii";
ImageSpan mImageSpan== new ImageSpan(mContext, R.drawable.icon);
SpannableString text = new SpannableString(msg);
text.setSpan(mImageSpan, 0, 1, 0);
mTextView.setText(text);

字符串变量中多余的空格被图标替代。


2
很好的解决方案。但是图标和文本不在同一重心上。你有什么建议吗? - User
尝试在TextView上移除fontPadding。 - Rahul
这不是通用解决方案,如果我需要在结尾处使用drawable,就像我们使用setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null)一样,该怎么办? - Choletski

10

我认为这比上面所有的答案都更简单:你只需要做这个:

public class TopGravityDrawable extends BitmapDrawable {

    public TopGravityDrawable(Resources res, Bitmap bitmap) {
        super(res, bitmap);
    }

    @Override
    public void draw(Canvas canvas) {
        int halfCanvas = canvas.getHeight() / 2;
        int halfDrawable = getIntrinsicHeight() / 2;
        canvas.save();
        canvas.translate(0, -halfCanvas + halfDrawable);
        super.draw(canvas);
        canvas.restore();
    }
}

然后

 final Bitmap bitmap = BitmapFactory.decodeResource(mTitle.getResources(), R.drawable.icon);
 icon = new TopGravityDrawable(mTitle.getResources(), bitmap);
 title.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);

请记住,这仅在使用左侧右侧复合可绘制对象时才能正常工作


你应该使用 title.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); 代替。 - Abdelalim Hassouna

7
请参见我的回答
您可以通过创建一个自定义Drawable来将复合Drawable对齐到顶部(或底部),然后通过覆盖方法onDraw(Canvas)来操作自定义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();
    }
}

1
如何将文本和左侧可绘制对象对齐到TextView的左侧。 - Reprator

4
你可以这样做:
public class DrawableTopLeftTextView extends TextView {

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

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

    public DrawableTopLeftTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (!TextUtils.isEmpty(getText())) {

            Drawable[] drawables = getCompoundDrawables();

            if (drawables != null) {

                Drawable drawableLeft = drawables[0];

                if (drawableLeft != null) {

                    Paint.FontMetricsInt fontMetricsInt = getPaint().getFontMetricsInt();
                    Rect bounds = new Rect();
                    getPaint().getTextBounds((String) getText(), 0, length(), bounds);
                    int textVerticalSpace = Math.round(bounds.top - fontMetricsInt.top);
                    int offset = (getHeight() - drawableLeft.getIntrinsicHeight()) / 2 - textVerticalSpace - getPaddingTop() / 2;
                    drawableLeft.setBounds(0, -offset, drawableLeft.getIntrinsicWidth(), drawableLeft.getIntrinsicHeight() - offset);
                }
            }
        }
        super.onDraw(canvas);
    }
}

3
如果你希望完全使用 XML 方案,那么可以使用一个 inset drawable 来重新定位你想要的 drawable。
<?xml version="1.0" encoding="utf-8"?>
<inset
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/actual_image_to_be_shown"
    android:insetTop="-32dp" />

根据您的情况,您可能需要调整插入值。然后,在您的TextView drawableLeft/Start定义中使用此XML可绘制图形。


3
如果你要给我一个负评,请至少告诉我为什么你不喜欢这个解决方案。 - Andrew Kelly
7
硬编码的值无法与动态文本配合使用。负偏移量取决于文本行数+TextView的“fontSize”和其他属性。然而,对于单行文本,这是一个不错的技巧。 - TWiStErRob
没有贬值,但可能有其原因:引用:“根据您的情况,您可能需要玩弄插入值”,这意味着对于具有动态长度的文本视图,它不是一个解决方案,或者至少不是最好的解决方案。 - Tima

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

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="100dp"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true" >

    <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=" 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 Text Text   Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text" />
</RelativeLayout>


1
我不想按照这种方式进行,因为这种方式需要使用嵌套布局。 - androidcodehunter
1
外层RelativeLayout完全没有用。而且自API Level 8起,fill_parent已被弃用。请使用match_parent代替。 - Phantômaxx
1
@androidcodehunter 要么是嵌套布局,要么是自定义代码,我们需要承受某种程度的影响 :( 如果可以的话,我总是更喜欢仅使用XML的解决方案,只要它被正确实现了(你可以用简单的水平LinearLayout来解决它)。顺便说一下,如果你使用ConstraintLayout或RelativeLayout,甚至不需要嵌套,但仍然有两个视图。 - TWiStErRob

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 提供, 点击上面的
可以查看英文原文,
原文链接