像Instagram这样的圆形背景文字,ReplacementSpan不能按要求工作

kar*_*.io 6 android android-edittext instagram spannable

我试图在下面做类似Instagram的事情 -

这就是我所做的

但我希望像Instagram这样的曲线 - 这就是我要的

现在我陷入了另一个问题 - 当我打字时,文本不会自动进入下一行,我必须按返回,就像通常editText工作在固定宽度.(总之multiline不能正常工作ReplacementSpan)

以下是我所做的示例代码 -

public class EditextActivity extends AppCompatActivity {

    EditText edittext;
    RoundedBackgroundSpan roundedBackgroundSpan;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.editext_screen);
        edittext=(EditText)findViewById(R.id.edittext);
       // edittext.setText("Hello My name is Karandeep Atwal.\n\n Hii this is test");
        roundedBackgroundSpan= new RoundedBackgroundSpan(Color.RED,Color.WHITE);
        edittext.getText().setSpan(roundedBackgroundSpan, 0, edittext.getText().length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
    }


    public class RoundedBackgroundSpan extends ReplacementSpan implements LineHeightSpan {

        private static final int CORNER_RADIUS = 15;
        private static final int PADDING_X = 10;

        private int   mBackgroundColor;
        private int   mTextColor;

        /**
         * @param backgroundColor background color
         * @param textColor text color
         */
        public RoundedBackgroundSpan(int backgroundColor, int textColor) {
            mBackgroundColor = backgroundColor;
            mTextColor = textColor;
        }

        @Override
        public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
            return (int) (PADDING_X + paint.measureText(text,start, end) + PADDING_X);
        }

        @Override
        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
            float width = paint.measureText(text,start, end);
            RectF rect = new RectF(x, top, x + width + 2 * PADDING_X, bottom);
            paint.setColor(mBackgroundColor);
            canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, paint);
            paint.setColor(mTextColor);
            canvas.drawText(text, start, end, x + PADDING_X, y, paint);
        }

        @Override
        public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fontMetricsInt) {

        }
    }

}
Run Code Online (Sandbox Code Playgroud)

下面是我的xml -

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:layout_gravity="center"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:padding="5dp"
        android:background="@drawable/border"
        android:id="@+id/edittext"
        android:layout_centerInParent="true"
        android:textColor="@android:color/black"
        android:gravity="center"
        android:hint="hi"
        android:singleLine="false"
        android:inputType="textMultiLine"
        android:textSize="30sp"
        android:maxWidth="100dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

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

以下是我使用时输入的内容setSpan-

在此输入图像描述

这是固定宽度的正常行为,我想要 -

在此输入图像描述

小智 7

在此处输入图片说明 活动_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_purple"
    tools:context="com.tttzof.demotext.MainActivity">

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="Enter text"
        android:textSize="30sp"
        android:gravity="center"
        android:textColor="@android:color/black"
        android:background="@android:color/transparent"
        android:layout_gravity="center"/>

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

主活动.java

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextWatcher;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final EditText editText = (EditText) findViewById(R.id.editText);

        int padding = dp(8);
        int radius = dp(5);

        final Object span = new BackgroundColorSpan(
                        Color.WHITE,
                        (float)padding,
                        (float) radius
        );

        editText.setShadowLayer(padding, 0f, 0f, 0);
        editText.setPadding(padding, padding, padding, padding);

        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                s.setSpan(span, 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        });
    }

    private int dp(int value) {
        return (int) (getResources().getDisplayMetrics().density * value + 0.5f);
    }
}
Run Code Online (Sandbox Code Playgroud)

背景颜色跨度

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.text.style.LineBackgroundSpan;

public class BackgroundColorSpan implements LineBackgroundSpan {
    private float padding;
    private float radius;

    private RectF rect = new RectF();
    private Paint paint = new Paint();
    private Paint paintStroke = new Paint();
    private Path path = new Path();

    private float prevWidth = -1f;
    private float prevLeft = -1f;
    private float prevRight = -1f;
    private float prevBottom = -1f;
    private float prevTop = -1f;


    public BackgroundColorSpan(int backgroundColor,
                               float padding,
                               float radius) {
        this.padding = padding;
        this.radius = radius;

        paint.setColor(backgroundColor);
        //paintStroke.setStyle(Paint.Style.STROKE);
        //paintStroke.setStrokeWidth(5f);
        paintStroke.setColor(backgroundColor);
    }

    @Override
    public void drawBackground(
                    final Canvas c,
                    final Paint p,
                    final int left,
                    final int right,
                    final int top,
                    final int baseline,
                    final int bottom,
                    final CharSequence text,
                    final int start,
                    final int end,
                    final int lnum) {

        float width = p.measureText(text, start, end) + 2f * padding;
        float shift = (right - width) / 2f;

        rect.set(shift, top, right - shift, bottom);

        if (lnum == 0) {
            c.drawRoundRect(rect, radius, radius, paint);
        } else {
            path.reset();
            float dr = width - prevWidth;
            float diff = -Math.signum(dr) * Math.min(2f * radius, Math.abs(dr/2f))/2f;
            path.moveTo(
                            prevLeft, prevBottom - radius
            );

            path.cubicTo(
                            prevLeft, prevBottom - radius,
                            prevLeft, rect.top,
                            prevLeft + diff, rect.top
            );
            path.lineTo(
                            rect.left - diff, rect.top
            );
            path.cubicTo(
                            rect.left - diff, rect.top,
                            rect.left, rect.top,
                            rect.left, rect.top + radius
            );
            path.lineTo(
                            rect.left, rect.bottom - radius
            );
            path.cubicTo(
                            rect.left, rect.bottom - radius,
                            rect.left, rect.bottom,
                            rect.left + radius, rect.bottom
            );
            path.lineTo(
                            rect.right - radius, rect.bottom
            );
            path.cubicTo(
                            rect.right - radius, rect.bottom,
                            rect.right, rect.bottom,
                            rect.right, rect.bottom - radius
            );
            path.lineTo(
                            rect.right, rect.top + radius
            );
            path.cubicTo(
                            rect.right, rect.top + radius,
                            rect.right, rect.top,
                            rect.right + diff, rect.top
            );
            path.lineTo(
                            prevRight - diff, rect.top
            );
            path.cubicTo(
                            prevRight - diff, rect.top,
                            prevRight, rect.top,
                            prevRight, prevBottom - radius
            );
            path.cubicTo(
                            prevRight, prevBottom - radius,
                            prevRight, prevBottom,
                            prevRight - radius, prevBottom

            );
            path.lineTo(
                            prevLeft + radius, prevBottom
            );
            path.cubicTo(
                            prevLeft + radius, prevBottom,
                            prevLeft, prevBottom,
                            prevLeft, rect.top - radius
            );
            c.drawPath(path, paintStroke);
        }

        prevWidth = width;
        prevLeft = rect.left;
        prevRight = rect.right;
        prevBottom = rect.bottom;
        prevTop = rect.top;
    }
}
Run Code Online (Sandbox Code Playgroud)


Rah*_*ari 6

通过 @tttzof351增强BackgroundColorSpan以支持对齐:

import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.text.style.LineBackgroundSpan
import kotlin.math.abs
import kotlin.math.sign

class BackgroundColorSpan(backgroundColor: Int,
                          private val padding: Int,
                          private val radius: Int) : LineBackgroundSpan {
    private val rect = RectF()
    private val paint = Paint()
    private val paintStroke = Paint()
    private val path = Path()
    private var prevWidth = -1f
    private var prevLeft = -1f
    private var prevRight = -1f
    private var prevBottom = -1f
    private var prevTop = -1f

    private val ALIGN_CENTER = 0
    private val ALIGN_START = 1
    private val ALIGN_END = 2

    init {
        paint.color = backgroundColor
        paintStroke.color = backgroundColor
    }

    private var align = ALIGN_CENTER

    fun setAlignment(alignment: Int) {
        align = alignment
    }

    override fun drawBackground(
            c: Canvas,
            p: Paint,
            left: Int,
            right: Int,
            top: Int,
            baseline: Int,
            bottom: Int,
            text: CharSequence,
            start: Int,
            end: Int,
            lnum: Int) {


        val width = p.measureText(text, start, end) + 2f * padding
        val shiftLeft: Float
        val shiftRight: Float


        when (align) {
            ALIGN_START -> {
                shiftLeft = 0f - padding
                shiftRight = width + shiftLeft
            }

            ALIGN_END -> {
                shiftLeft = right - width + padding
                shiftRight = (right + padding).toFloat()
            }
            else -> {
                shiftLeft = (right - width) / 2
                shiftRight = right - shiftLeft
            }
        }

        rect.set(shiftLeft, top.toFloat(), shiftRight, bottom.toFloat())


        if (lnum == 0) {
            c.drawRoundRect(rect, radius.toFloat(), radius.toFloat(), paint)
        } else {
            path.reset()
            val difference = width - prevWidth
            val diff = -sign(difference) * (2f * radius).coerceAtMost(abs(difference / 2f)) / 2f
            path.moveTo(
                    prevLeft, prevBottom - radius
            )

            if (align != ALIGN_START) {
                path.cubicTo(//1
                        prevLeft, prevBottom - radius,
                        prevLeft, rect.top,
                        prevLeft + diff, rect.top
                )
            } else {
                path.lineTo(prevLeft, prevBottom + radius)
            }
            path.lineTo(
                    rect.left - diff, rect.top
            )
            path.cubicTo(//2
                    rect.left - diff, rect.top,
                    rect.left, rect.top,
                    rect.left, rect.top + radius
            )
            path.lineTo(
                    rect.left, rect.bottom - radius
            )
            path.cubicTo(//3
                    rect.left, rect.bottom - radius,
                    rect.left, rect.bottom,
                    rect.left + radius, rect.bottom
            )
            path.lineTo(
                    rect.right - radius, rect.bottom
            )
            path.cubicTo(//4
                    rect.right - radius, rect.bottom,
                    rect.right, rect.bottom,
                    rect.right, rect.bottom - radius
            )
            path.lineTo(
                    rect.right, rect.top + radius
            )

            if (align != ALIGN_END) {
                path.cubicTo(//5
                        rect.right, rect.top + radius,
                        rect.right, rect.top,
                        rect.right + diff, rect.top
                )
                path.lineTo(
                        prevRight - diff, rect.top
                )
                path.cubicTo(//6
                        prevRight - diff, rect.top,
                        prevRight, rect.top,
                        prevRight, prevBottom - radius
                )

            } else {
                path.lineTo(prevRight, prevBottom - radius)
            }
            path.cubicTo(//7
                    prevRight, prevBottom - radius,
                    prevRight, prevBottom,
                    prevRight - radius, prevBottom
            )

            path.lineTo(
                    prevLeft + radius, prevBottom
            )

            path.cubicTo(//8
                    prevLeft + radius, prevBottom,
                    prevLeft, prevBottom,
                    prevLeft, rect.top - radius
            )
            c.drawPath(path, paintStroke)

        }
        prevWidth = width
        prevLeft = rect.left
        prevRight = rect.right
        prevBottom = rect.bottom
        prevTop = rect.top
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述

  • 如果其他人正在努力让背景显示出来。确保 setPadding 和 setShadowLayer 的半径等于应用范围的文本视图上的填充。如果您实际上没有在文本视图上设置填充,如果您还在父视图组中设置 android:clipChildren="false" ,您仍然可以正确显示背景 (3认同)