安卓开发进阶之文字上标(翻译)

来源:互联网 发布:java就业培训教程豆瓣 编辑:程序博客网 时间:2024/05/18 00:18

原文链接

https://blog.stylingandroid.com/superscript/


本文讨论的上标类似为1st,需要说明的是上标使用于任何语言而不仅仅限于英语。

本文涉及到两个关键技术,正则表达与正则读者,前者用来匹配任意给定串的序数(类似第一第二称为序数)。

下面是正则表达式:

(?<=\b\d{1,10})(st|nd|rd|th)(?=\b)

看起来恐怖吗,恐怖就对了(逗你的...)。实际上我们只要把它分为三部分就豁然开朗了。

(?<=\b\d{1,10})
(st|nd|rd|th)
(?=\b)

第一部分是后行断言(lookbehind),用来匹配我们希望展示内容之前的指定模式。后行断言用?<=表示,后面跟着我们希望匹配的模式。

此处\b\d{1,10}要么匹配一个空格或者是一个字符串的开始(\b代表);然后后面跟1到10的数字(\d{1,10}代表)。

在java中不可能用通配符匹配后行断言,因为假如编译器无法确定后行断言的最大长度正则编辑会失败,这就是我为什么在这里指定了一个范围,而不是使用\\d+。

第二部分是我们真正要展示的内容。正如我们想要展示的序数后缀,我们使用(st|nd|rd|th)。

第三部分是先行断言(lookahead),用来匹配我们希望展示内容之后的指定模式。后行断言用?=表示,后面跟着我们希望匹配的模式。

此处\b要么匹配一个空格要么匹配一个字符串的结尾(\b代表)

认真的你可能会想到一个问题,为什么我们不只用第二个部分就可以了呢?

因为这样我们会在“first”上匹配"st"成first,很明显这不是我们想要的。

正确使用先行断言与后行断言才能达到我们的目的,最后我们要匹配如下的模式:

<whitespace or start><one or more digits>st|nd|rd|th<whitespace or end>

要知道我们真正要作为上标的部分是st,nd,rd,th。在Demo中有个JUnit5测试单元用例测试正则表达式的结果。

当然,我的正则表达式也不是完美的,我也知道有些特殊情况会有问题。比如说错误的匹配成11st,尽管我能够解决这个问题,

但是为了降低正则表达式的复杂度,我并不打算这么做。


为了实现我们上标的目的,我们使用SuperscriptSpan和RelativeSizeSpan ,前者用来升高文字基准线,后者用来改变TextView文字大小。

代码如下:

public class OrdinalSuperscriptFormatter {    private static final String SUPERSCRIPT_REGEX = "(?<=\\b\\d{0,10})(st|nd|rd|th)(?=\\b)";    private static final Pattern PATTERN = Pattern.compile(SUPERSCRIPT_REGEX);    private static final float PROPORTION = 0.5f;     private final SpannableStringBuilder stringBuilder;     public OrdinalSuperscriptFormatter(@NonNull SpannableStringBuilder stringBuilder) {        this.stringBuilder = stringBuilder;    } //执行正则表达式的逻辑    public void format(TextView textView) {        CharSequence text = textView.getText();        Matcher matcher = PATTERN.matcher(text);        stringBuilder.clear();        stringBuilder.append(text);        while (matcher.find()) {            int start = matcher.start();            int end = matcher.end();            createSuperscriptSpan(start, end);        }        textView.setText(stringBuilder);    }     private void createSuperscriptSpan(int start, int end) {        SuperscriptSpan superscript = new SuperscriptSpan();        RelativeSizeSpan size = new RelativeSizeSpan(PROPORTION);        stringBuilder.setSpan(superscript, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);        stringBuilder.setSpan(size, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    }}

使用:

public class MainActivity extends AppCompatActivity {     @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);         TextView textView = (TextView) findViewById(R.id.text_view);        OrdinalSuperscriptFormatter formatter = new OrdinalSuperscriptFormatter(new SpannableStringBuilder());        formatter.format(textView);    }}

运行结果:




源码下载

https://github.com/StylingAndroid/Superscript


原文链接

https://blog.stylingandroid.com/superscript/






原创粉丝点击