如何使 RelativeSizeSpan 对齐到顶部

Posted

技术标签:

【中文标题】如何使 RelativeSizeSpan 对齐到顶部【英文标题】:How to make RelativeSizeSpan align to top 【发布时间】:2016-08-26 03:09:41 【问题描述】:

我有以下字符串 RM123.456。我愿意

使 RM 相对较小 使 RM 与顶部完全对齐

我几乎可以通过使用来实现它

spannableString.setSpan(new RelativeSizeSpan(0.50f), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString, TextView.BufferType.SPANNABLE);

结果看起来像

但是,它与底部对齐。它与顶部不对齐。

我尝试使用SuperscriptSpan。好像

它不符合我的要求

SuperscriptSpan 不会使文本变小。我无法控制它的大小。 SuperscriptSpan 将使文本“在顶部对齐”

请问,我怎样才能让 RelativeSizeSpan 与顶部完全对齐

这是我希望达到的目标。

请注意,我们不希望使用 2 个 TextViews 解决方案。

【问题讨论】:

您可以使用两个 textView 轻松完成。 ***.com/questions/28830159/… 你能张贴你想要的屏幕截图吗? 【参考方案1】:

我知道这是一个旧线程,但我今天才遇到这个问题,我很惊讶没有人回答这个问题......我建议不要对 2, 4 进行硬编码,但你可以弄清楚如何

val span = SpannableStringBuilder.valueOf("$temp")
span.append("°F", SuperscriptSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
span.setSpan(RelativeSizeSpan(0.5f), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tempTextView.setText(span, TextView.BufferType.SPANNABLE)

【讨论】:

【参考方案2】:

这里提供的许多答案要求您设置文本大小比例百分比并猜测偏移量以解释他们在计算正确偏移量时的错误,或者他们要求您设置多个跨度。

所以,为了更新这个问题,这里有一个解决方案,它设置了正确的上标基线,并且只要求您为仅 1 个所需跨度提供文本比例百分比:

class TopAlignSuperscriptSpan(private val textSizeScalePercentage: Float = 0.5f) : MetricAffectingSpan() 

    override fun updateDrawState(tp: TextPaint) 
        tp.baselineShift += (tp.ascent() * textSizeScalePercentage).toInt()
        tp.textSize = tp.textSize * textSizeScalePercentage
    

    override fun updateMeasureState(tp: TextPaint) 
        updateDrawState(tp)
    

您可以使用它而无需设置其他跨度,如下所示:

val spannableString = SpannableString("RM123.456")
spannableString.setSpan(TopAlignSuperscriptSpan(0.6f), 0, 2, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
myTextView.text = spannableString

“RM”将是“123.456”文本大小的 60%,并且它的顶部将与“123.456”的顶部完全对齐

更新:您不应该使用它,因为它不准确,就像这里的所有其他答案一样。 相反,我建议计算文本视图每个部分的高度并手动设置每个部分的 y 值,就像这个库一样:https://github.com/fabiomsr/MoneyTextView/tree/master/moneytextview/src/main/res/values

【讨论】:

【参考方案3】:

应该使用的顶部对齐类,而不是RelativeSizeSpan(不是附加的):

import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;

public class TopRelativeSizeSpan extends MetricAffectingSpan 

    private final float mProportion;

    public TopRelativeSizeSpan(float proportion) 
        mProportion = proportion;
    

    @Override
    public void updateDrawState(TextPaint ds) 
        ds.baselineShift += (mProportion - 1) * (ds.getTextSize() - ds.descent());
        ds.setTextSize(ds.getTextSize() * mProportion);
    

    @Override
    public void updateMeasureState(TextPaint ds) 
        updateDrawState(ds);
    

及用法:

spannableString.setSpan(new TopRelativeSizeSpan(0.50f), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString, TextView.BufferType.SPANNABLE);

【讨论】:

【参考方案4】:

我已经在我的一个应用程序中实现了这一点。

 <TextView
    android:id="@+id/txt_formatted_value"
    android:layout_
    android:layout_
    android:layout_gravity="center"
    android:textColor="#000000"       
    android:textSize="28dp" />

在Activity/Fragment.class中

 myTextView = (TextView) view.findViewById(R.id.txt_formatted_value);

硬编码用于测试目的,

String numberValue = "123.456";    
myTextView.setText(UtilityClass.getFormattedSpannedString("RM"+numberValue,
     numberValue.length(),0));

在你的包中添加这个类,

public class SuperscriptSpanAdjuster extends MetricAffectingSpan 
double ratio = 0.5;

public SuperscriptSpanAdjuster() 


public SuperscriptSpanAdjuster(double ratio) 
    this.ratio = ratio;


@Override
public void updateDrawState(TextPaint paint) 
    paint.baselineShift += (int) (paint.ascent() * ratio);


@Override
public void updateMeasureState(TextPaint paint) 
    paint.baselineShift += (int) (paint.ascent() * ratio);

在 UntilityClass.class 中创建格式方法

 public static SpannableString getFormattedSpannedString(String value, int mostSignificantLength, int leastSignificantLength)

    SpannableString  spanString = new SpannableString(value);
    /* To show the text in top aligned(Normal)*/
    spanString.setSpan(new SuperscriptSpanAdjuster(0.7), 0,spanString.length()-mostSignificantLength-leastSignificantLength, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    /* Show the number of characters is normal size (Normal)*/
    spanString.setSpan(new RelativeSizeSpan(1.3f), 0,spanString.length()-mostSignificantLength-leastSignificantLength, 0);
    /*To set the text style as bold(MostSignificant)*/
    //spanString.setSpan(new StyleSpan(Typeface.BOLD), spanString.length()-mostSignificantLength-leastSignificantLength, spanString.length()-leastSignificantLength, 0);
    /*To set the text color as WHITE(MostSignificant)*/
    //spanString.setSpan(new ForegroundColorSpan(Color.WHITE), spanString.length()-mostSignificantLength-leastSignificantLength,  spanString.length()-leastSignificantLength, 0);
    /*Show the number of characters as most significant value(MostSignificant)*/
    spanString.setSpan(new RelativeSizeSpan(2.3f), spanString.length()-mostSignificantLength-leastSignificantLength, spanString.length()-leastSignificantLength, 0);

    /* To show the text in top aligned(LestSignificant)*/
    spanString.setSpan(new SuperscriptSpanAdjuster(1.2), spanString.length()-leastSignificantLength, spanString.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    /*To set the text style as bold(LestSignificant)*/
    //spanString.setSpan(new StyleSpan(Typeface.BOLD), spanString.length()-leastSignificantLength, spanString.length(), 0);
    /*Show the number of characters as most significant value(LestSignificant)*/
    spanString.setSpan(new RelativeSizeSpan(0.8f), spanString.length()-leastSignificantLength, spanString.length(), 0);

    return spanString;

使用这个方法你可以做更多的马戏,比如改变文字样式,为上标单独颜色。 左右两边都可以加上标(这里我把所有的代码都注释掉了,如果你想试试...)

【讨论】:

@Srivivasan 我无法从 getFormattedSpannedString 中找到最后一种格式(蓝色),我已经尝试了所有这些格式,但没有一个给出相同的结果。 @RonakMehta 我评论了该代码。您必须设置 ForegroundColorSpan 值(在示例中添加了 WHITE)。您可以将其更改为 BLUE。 这是唯一一个可以与任何字体和任何文本大小正确对齐到顶部的解决方案。【参考方案5】:

您可以通过创建自定义 MetricAffectingSpan 类来实现最高重力

这是自定义类的代码:

public class CustomCharacterSpan extends MetricAffectingSpan 
    double ratio = 0.5;

    public CustomCharacterSpan() 
    

    public CustomCharacterSpan(double ratio) 
        this.ratio = ratio;
    

    @Override
    public void updateDrawState(TextPaint paint) 
        paint.baselineShift += (int) (paint.ascent() * ratio);
    

    @Override
    public void updateMeasureState(TextPaint paint) 
        paint.baselineShift += (int) (paint.ascent() * ratio);
    

应用跨度:

spannableString.setSpan(new RelativeSizeSpan(0.50f), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new CustomCharacterSpan(), 0, index,
                SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString, TextView.BufferType.SPANNABLE);

输出:

有关 MetricAffectingSpan 的更多信息:http://developer.android.com/reference/android/text/style/MetricAffectingSpan.html

自定义 MetricAffectingSpan 逻辑引用自:Two different styles in a single textview with different gravity and hieght

【讨论】:

【参考方案6】:

下标和上标必须使用下面的html标签。它的作用就像魅力。

 ((TextView) findViewById(R.id.text)).setText(Html.fromHtml("<sup><small>2</small></sup>X"));

您也可以使用以下代码:

String titleFirst = "Insert GoTechTM device into the vehicle\'s OBDII port.";
SpannableStringBuilder cs = new SpannableStringBuilder(titleFirst);
cs.setSpan(new SuperscriptSpan(), titleFirst.indexOf("TM"), titleFirst.indexOf("TM")+2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
cs.setSpan(new RelativeSizeSpan((float)0.50), titleFirst.indexOf("TM"), titleFirst.indexOf("TM")+2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);        
txtPairInstructionFirst.setText(cs);

【讨论】:

【参考方案7】:

但是我是这样做的:

activity_main.xml

<TextView
    android:id="@+id/txtView"
    android:layout_
    android:layout_
    android:layout_marginTop="50dp"
    android:textSize="26sp" />

MainActivity.java

TextView txtView = (TextView) findViewById(R.id.txtView);

SpannableString spannableString = new SpannableString("RM123.456");
spannableString.setSpan( new TopAlignSuperscriptSpan( (float)0.35 ), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE );
txtView.setText(spannableString);

TopAlignSuperscriptSpan.java

private class TopAlignSuperscriptSpan extends SuperscriptSpan 
        //divide superscript by this number
        protected int fontScale = 2;

        //shift value, 0 to 1.0
        protected float shiftPercentage = 0;

        //doesn't shift
        TopAlignSuperscriptSpan() 

        //sets the shift percentage
        TopAlignSuperscriptSpan( float shiftPercentage ) 
            if( shiftPercentage > 0.0 && shiftPercentage < 1.0 )
                this.shiftPercentage = shiftPercentage;
        

        @Override
        public void updateDrawState( TextPaint tp ) 
            //original ascent
            float ascent = tp.ascent();

            //scale down the font
            tp.setTextSize( tp.getTextSize() / fontScale );

            //get the new font ascent
            float newAscent = tp.getFontMetrics().ascent;

            //move baseline to top of old font, then move down size of new font
            //adjust for errors with shift percentage
            tp.baselineShift += ( ascent - ascent * shiftPercentage )
                    - (newAscent - newAscent * shiftPercentage );
        

        @Override
        public void updateMeasureState( TextPaint tp ) 
            updateDrawState( tp );
        
    

希望这会对你有所帮助。

【讨论】:

但是,仍然没有对齐。您可以看到“RM”的位置高于“123.456” @CheokYanCheng,你能把你想要的截图发上来吗? @CheokYanCheng,我已经编辑了我的答案,请检查。 嗨@HirenPatel,请问您如何选择0.35 作为shiftPercentage?是通过试错法吗? @CheokYanCheng,与其他字符相比,前两个字符将占据 0.35 % 的大小。【参考方案8】:

我查看了RelativeSizeSpan,发现了一个相当简单的实现。所以你可以为你的目的实现你自己的RelativeSizeSpan。这里唯一的区别是它没有实现ParcelableSpan,因为这仅适用于框架代码。 AntiRelativeSizeSpan 只是一个快速破解,当然没有太多测试,但它似乎工作正常。它完全依赖Paint.getTextBounds()baselineShift 找到最佳价值,但也许会有更好的方法。

public class AntiRelativeSizeSpan extends MetricAffectingSpan 
    private final float mProportion;

    public AntiRelativeSizeSpan(float proportion) 
        mProportion = proportion;
    

    public float getSizeChange() 
        return mProportion;
    

    @Override
    public void updateDrawState(TextPaint ds) 
        updateAnyState(ds);
    

    @Override
    public void updateMeasureState(TextPaint ds) 
        updateAnyState(ds);
    

    private void updateAnyState(TextPaint ds) 
        Rect bounds = new Rect();
        ds.getTextBounds("1A", 0, 2, bounds);
        int shift = bounds.top - bounds.bottom;
        ds.setTextSize(ds.getTextSize() * mProportion);
        ds.getTextBounds("1A", 0, 2, bounds);
        shift += bounds.bottom - bounds.top;
        ds.baselineShift += shift;
    

【讨论】:

【参考方案9】:

最好的解决方案是使用 html。

我更喜欢这个解决方案,它支持所有安卓版本以及设备。

这里是示例,与您想要的文本相同

<p><sup>TM</sup> 123.456.</p>

我在 android 中得到结果

TM 123.456.

您可以使用

在 android 的 Textview 中轻松显示文本

Html.fromText("YOUR_STRING_INHTML");

希望对你有帮助。

【讨论】:

也许你的意思是Html.fromHtml(...)而不是Html.fromText(...)

以上是关于如何使 RelativeSizeSpan 对齐到顶部的主要内容,如果未能解决你的问题,请参考以下文章

如何将 TextView 容器内的文本对齐到顶部? [复制]

即使大于高度,如何将 VStack 对齐到顶部?

如何从底部到顶部更改 Android Snackbar 的初始对齐方式?

如何将html中的文本对齐到顶部?

iOS:将 UIImageView 对齐到顶部

在 UILabel 中将文本垂直对齐到顶部