【问题标题】:How to make RelativeSizeSpan align to top如何使 RelativeSizeSpan 对齐到顶部
【发布时间】: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 解决方案。

【问题讨论】:

标签: android android-studio spannablestring


【解决方案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_width="wrap_content"
            android:layout_height="wrap_content"
            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_width="wrap_content"
                android:layout_height="wrap_content"
                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(...)
              猜你喜欢
              • 1970-01-01
              • 2010-10-06
              • 2015-04-02
              • 2011-10-13
              • 2010-12-17
              • 2014-07-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多