Android-ImageSpan-如何居中对齐文本末尾的图像

j2e*_*nue 1 android spannablestring

我在xml布局中具有以下内容:

在此处输入图片说明

注意六角形#4如何不在文本的中心对齐。我该怎么做:这是到目前为止我尝试过的事情:

为了真正获得其中带有#的视图,我给视图添加一个如下所示的视图:

//my_hexagon_button.xml:

     <?xml version="1.0" encoding="utf-8"?>
       <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                     xmlns:tools="http://schemas.android.com/tools"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:orientation="vertical"
                     android:padding="0dp"
                     tools:ignore="MissingPrefix">


           <Button
               android:id="@+id/tv_icon"
               fontPath="proxima_nova_semi_bold.otf"
               android:layout_width="16dp"
               android:layout_height="17.5dp"
               android:layout_marginBottom="5dp"
               android:layout_marginLeft="10dp"
               android:alpha=".25"
               android:background="@drawable/hexagon"
               android:clickable="true"
               android:contentDescription="@string/content_description"
               android:focusable="false"
               android:padding="0dp"
               android:text="4"
               android:textColor="@color/white"
               android:textSize="8dp"
               />

       </LinearLayout>
Run Code Online (Sandbox Code Playgroud)

扩大视图后,我获取其图形缓存的副本,并在ImageSpan中使用它。这是我如何获取图形缓存的副本:

public Bitmap getIconBitmap() {
               LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
               LinearLayout myRoot = new LinearLayout(getActivity());

               // inflate and measure the button then grab its image from the view cache
               ViewGroup parent = (ViewGroup) inflater.inflate(R.layout.my_hexagon_button, myRoot);
               TextView tv = (TextView) parent.findViewById(R.id.tv_icon);

               parent.setDrawingCacheEnabled(true);
               parent.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                       View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
               parent.layout(0, 0, parent.getMeasuredWidth(), parent.getMeasuredHeight());

               parent.buildDrawingCache(true);
               // if you need bounds on the view, swap bitmap for a drawable and call setbounds, im not using bounds
               Bitmap b = Bitmap.createBitmap(parent.getDrawingCache());
               parent.setDrawingCacheEnabled(false); // clear drawing cache

               return b;
           }
Run Code Online (Sandbox Code Playgroud)

因此,现在我有一个位图,看起来像我附加的图形中的六角形#4图像。现在,在ImageSpan中使用它:

public Spannable createImageSpan(TextView tv,Bitmap bitmapIcon) {

                   Spannable span = new SpannableString(tv.getText());
                   int start = span.length() - 1;
                   int end = span.length();

                   ImageSpan image = new ImageSpan(new BitmapDrawable(getResources(), bitmapIcon),ImageSpan.ALIGN_BASELINE);
                   span.setSpan(image, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

                   return span;

               }
Run Code Online (Sandbox Code Playgroud)

然后稍后我只需在我的textview中设置该跨度即可。并且扭曲,但是图像在文本中居中对齐。注意它如何下降到底部。我该如何彻底解决这个问题?

M.P*_*roy 9

对于 API >29,您可以使用ImageSpan.ALIGN_CENTER常量来完成此操作。(下面的 Kotlin 代码示例。)

val image: ImageSpan = ImageSpan(
    BitmapDrawable(resources, bitmapIcon),
    ImageSpan.ALIGN_CENTER);
span.setSpan(image, start, end, 0);
Run Code Online (Sandbox Code Playgroud)

如果您需要支持低于 29 的 API 级别(我想大多数人会暂时支持),您仍然需要像 RoShan Shan 的答案那样对 ImageSpan 进行子类化。但是,您只需要严格覆盖绘制即可使行为正常工作:

class CenteredImageSpanSubclass(
    context: Context, 
    bitmap: Bitmap): ImageSpan(context, bitmap) {

    override fun draw(...) {

        canvas.save()

        val transY = (bottom - top) / 2 - drawable.bounds.height() / 2

        canvas.translate(x, transY.toFloat())
        drawable.draw(canvas)
        canvas.restore()
    }
}
Run Code Online (Sandbox Code Playgroud)


RoS*_*han 6

你可以试试我的CenteredImageSpandraw您可以通过计算来自定义方法transY -= (paint.getFontMetricsInt().descent / 2 - 8);。(祝你好运。 :) )

public class CenteredImageSpan extends ImageSpan {
    private WeakReference<Drawable> mDrawableRef;

    // Extra variables used to redefine the Font Metrics when an ImageSpan is added
    private int initialDescent = 0;
    private int extraSpace = 0;

    public CenteredImageSpan(Context context, final int drawableRes) {
        super(context, drawableRes);
    }

    public CenteredImageSpan(Drawable drawableRes, int verticalAlignment) {
        super(drawableRes, verticalAlignment);
    }

    @Override
    public int getSize(Paint paint, CharSequence text,
                       int start, int end,
                       Paint.FontMetricsInt fm) {
        Drawable d = getCachedDrawable();
        Rect rect = d.getBounds();

//        if (fm != null) {
//            Paint.FontMetricsInt pfm = paint.getFontMetricsInt();
//            // keep it the same as paint's fm
//            fm.ascent = pfm.ascent;
//            fm.descent = pfm.descent;
//            fm.top = pfm.top;
//            fm.bottom = pfm.bottom;
//        }

        if (fm != null) {
            // Centers the text with the ImageSpan
            if (rect.bottom - (fm.descent - fm.ascent) >= 0) {
                // Stores the initial descent and computes the margin available
                initialDescent = fm.descent;
                extraSpace = rect.bottom - (fm.descent - fm.ascent);
            }

            fm.descent = extraSpace / 2 + initialDescent;
            fm.bottom = fm.descent;

            fm.ascent = -rect.bottom + fm.descent;
            fm.top = fm.ascent;
        }

        return rect.right;
    }

    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text,
                     int start, int end, float x,
                     int top, int y, int bottom, @NonNull Paint paint) {
        Drawable b = getCachedDrawable();
        canvas.save();

//        int drawableHeight = b.getIntrinsicHeight();
//        int fontAscent = paint.getFontMetricsInt().ascent;
//        int fontDescent = paint.getFontMetricsInt().descent;
//        int transY = bottom - b.getBounds().bottom +  // align bottom to bottom
//                (drawableHeight - fontDescent + fontAscent) / 2;  // align center to center

        int transY = bottom - b.getBounds().bottom;
        // this is the key
        transY -= (paint.getFontMetricsInt().descent / 2 - 8);

//        int bCenter = b.getIntrinsicHeight() / 2;
//        int fontTop = paint.getFontMetricsInt().top;
//        int fontBottom = paint.getFontMetricsInt().bottom;
//        int transY = (bottom - b.getBounds().bottom) -
//                (((fontBottom - fontTop) / 2) - bCenter);


        canvas.translate(x, transY);
        b.draw(canvas);
        canvas.restore();
    }


    // Redefined locally because it is a private member from DynamicDrawableSpan
    private Drawable getCachedDrawable() {
        WeakReference<Drawable> wr = mDrawableRef;
        Drawable d = null;

        if (wr != null)
            d = wr.get();

        if (d == null) {
            d = getDrawable();
            mDrawableRef = new WeakReference<>(d);
        }

        return d;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

我像这样实现了上面的代码:

Drawable myIcon = getResources().getDrawable(R.drawable.btn_feedback_yellow);
            int width = (int) Functions.convertDpToPixel(75, getActivity());
            int height = (int) Functions.convertDpToPixel(23, getActivity());
            myIcon.setBounds(0, 0, width, height);
            CenteredImageSpan btnFeedback = new CenteredImageSpan(myIcon, ImageSpan.ALIGN_BASELINE);
            ssBuilder.setSpan(
                    btnFeedback, // Span to add
                    getString(R.string.text_header_answer).length() - 1, // Start of the span (inclusive)
                    getString(R.string.text_header_answer).length(), // End of the span (exclusive)
                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);// Do not extend the span when text add later
Run Code Online (Sandbox Code Playgroud)


ran*_*dam 5

您可以使用此类将ImageSpan与文本对齐

public class VerticalImageSpan extends ImageSpan {

    public VerticalImageSpan(Drawable drawable) {
        super(drawable);
    }

    /**
     * update the text line height
     */
    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end,
                       Paint.FontMetricsInt fontMetricsInt) {
        Drawable drawable = getDrawable();
        Rect rect = drawable.getBounds();
        if (fontMetricsInt != null) {
            Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
            int fontHeight = fmPaint.descent - fmPaint.ascent;
            int drHeight = rect.bottom - rect.top;
            int centerY = fmPaint.ascent + fontHeight / 2;

            fontMetricsInt.ascent = centerY - drHeight / 2;
            fontMetricsInt.top = fontMetricsInt.ascent;
            fontMetricsInt.bottom = centerY + drHeight / 2;
            fontMetricsInt.descent = fontMetricsInt.bottom;
        }
        return rect.right;
    }

    /**
     * see detail message in android.text.TextLine
     *
     * @param canvas the canvas, can be null if not rendering
     * @param text the text to be draw
     * @param start the text start position
     * @param end the text end position
     * @param x the edge of the replacement closest to the leading margin
     * @param top the top of the line
     * @param y the baseline
     * @param bottom the bottom of the line
     * @param paint the work paint
     */
    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end,
                     float x, int top, int y, int bottom, Paint paint) {

        Drawable drawable = getDrawable();
        canvas.save();
        Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
        int fontHeight = fmPaint.descent - fmPaint.ascent;
        int centerY = y + fmPaint.descent - fontHeight / 2;
        int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2;
        canvas.translate(x, transY);
        drawable.draw(canvas);
        canvas.restore();
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢这个答案