草庐IT

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

coder 2023-12-03 原文

我正在尝试做类似于下面 Instagram 的事情 -

但我想要像 Instagram 这样的曲线 -

现在我陷入了另一个问题—— 当我打字时,。文本不会自动转到下一行,我必须按 return ,就像通常 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) {

        }
    }

}

下面是我的 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>

下面是我使用 setSpan 键入时得到的结果 -

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

最佳答案

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

主 Activity .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);
    }
}

BackgroundColorSpan.java

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

关于android - 像 Instagram 这样的圆形背景文本,ReplacementSpan 不能按要求工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48096722/

有关android - 像 Instagram 这样的圆形背景文本,ReplacementSpan 不能按要求工作的更多相关文章

  1. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  3. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  4. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  5. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  6. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

  7. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  8. ruby-on-rails - 使用 Rmagick 或 ImageMagick 在背景上放置标题 - 2

    我有一张背景图片,我想在其中添加一个文本框。我想弄清楚如何将标题放置在其顶部的正确位置。(我使用标题是因为我需要自动换行功能)。现在,我只能让文本显示在左上角,但我需要能够手动定位它的开始位置。require'RMagick'require'Pry'includeMagicktext="Loremipsumdolorsitamet"img=ImageList.new('template001.jpg')img 最佳答案 这是使用convert的ImageMagick命令行的答案。如果你想在Rmagick中使用这个方法,你必须自己移植

  9. ruby - JetBrains RubyMine 3.2.4 调试器不工作 - 2

    使用Ruby1.9.2运行IDE提示说需要gemruby​​-debug-base19x并提供安装它。但是,在尝试安装它时会显示消息Failedtoinstallgems.Followinggemswerenotinstalled:C:/ProgramFiles(x86)/JetBrains/RubyMine3.2.4/rb/gems/ruby-debug-base19x-0.11.30.pre2.gem:Errorinstallingruby-debug-base19x-0.11.30.pre2.gem:The'linecache19'nativegemrequiresinstall

  10. ruby - `rescue $!` 是如何工作的? - 2

    我知道全局变量$!包含最新的异常对象,但我对下面的语法感到困惑。谁能帮助我理解以下语法?rescue$! 最佳答案 此构造可防止异常停止您的程序并使堆栈跟踪冒泡。它还会将该异常作为值返回,这很有用。a=get_me_datarescue$!在此行之后,a将保存请求的数据或异常。然后您可以分析该异常并采取相应措施。defget_me_dataraise'Nodataforyou'enda=get_me_datarescue$!puts"Executioncarrieson"pa#>>Executioncarrieson#>>#更现实的

随机推荐