Android 将跨距转换为大写

Android 将跨距转换为大写,android,Android,我有一个span实例,它是由Html.fromHtml()返回的,我想将文本转换为大写。与String不同,其他安卓CharSequence实现没有toUpperCase()方法。显然,调用toString()会导致所有链接和其他格式丢失 在Html.fromHtml()方法中,您将发送字符串,因此您可以将该字符串转换为大写,并从Html.fromHtml中获取跨度 例子 Html.fromHtml(lSampleString..toUpperCase();在Html.fromHtml()

我有一个
span
实例,它是由
Html.fromHtml()
返回的,我想将文本转换为大写。与
String
不同,其他安卓
CharSequence
实现没有
toUpperCase()
方法。显然,调用
toString()
会导致所有链接和其他格式丢失

在Html.fromHtml()方法中,您将发送字符串,因此您可以将该字符串转换为大写,并从Html.fromHtml中获取跨度

例子
Html.fromHtml(lSampleString..toUpperCase();

在Html.fromHtml()方法中,您将发送字符串,因此您可以将该字符串转换为大写,并从Html.fromHtml中获取跨度

例子

Html.fromHtml(lSampleString..toUpperCase();

您可以从原始字符序列创建一个新字符串,对其进行大小写,然后重新应用所有原始跨距。这对我很有用:

public static Spanned csToUpperCase(@NonNull Spanned s) {
    Object[] spans = s.getSpans(0,
            s.length(), Object.class);
    SpannableString spannableString = new SpannableString(s.toString().toUpperCase());

    // reapply the spans to the now uppercase string
    for (Object span : spans) {
        spannableString.setSpan(span,
                s.getSpanStart(span),
                s.getSpanEnd(span),
                0);
    }

    return spannableString;
}

您可以从原始CharSequence创建一个新字符串,对其进行大小写转换,然后将所有原始跨距重新应用于该字符串。这对我很有用:

public static Spanned csToUpperCase(@NonNull Spanned s) {
    Object[] spans = s.getSpans(0,
            s.length(), Object.class);
    SpannableString spannableString = new SpannableString(s.toString().toUpperCase());

    // reapply the spans to the now uppercase string
    for (Object span : spans) {
        spannableString.setSpan(span,
                s.getSpanStart(span),
                s.getSpanEnd(span),
                0);
    }

    return spannableString;
}

万一有人还在找这个。我找到了一个很好的解决办法。。。 您有一个名为
ReplacementSpan
Span
,它有一个方法可以为您提供所需的字符序列

abstract void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)
继承该类并将您的
Span
设置为该类。您将完全控制字符串的运行情况。以下是一个示例:

class HeadingSpan : ReplacementSpan() {

    override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int =
            paint.measureText(text, start, end).roundToInt()

    override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        canvas.drawText(text.toString().toUpperCase(), start, end, x, y.toFloat(), paint)
    }

}

万一有人还在找这个。我找到了一个很好的解决办法。。。 您有一个名为
ReplacementSpan
Span
,它有一个方法可以为您提供所需的字符序列

abstract void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)
继承该类并将您的
Span
设置为该类。您将完全控制字符串的运行情况。以下是一个示例:

class HeadingSpan : ReplacementSpan() {

    override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int =
            paint.measureText(text, start, end).roundToInt()

    override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        canvas.drawText(text.toString().toUpperCase(), start, end, x, y.toFloat(), paint)
    }

}

仅使用
.toUpperCase()
并重新应用跨距是不够的。如果您有
ß
这样的字符,跨距长度将是错误的。当使用大写字母时,
ß
将变为
SS

可能有一些语言在大写时将多个字符转换为一个字符,但我不知道有什么例子。我们可以通过逐个大写字符来完全避免这种情况

下面是一个简单的解决方案,可以处理大多数边缘情况:

private fun toUpperCase(locale: Locale, source: CharSequence, copySpans: Boolean): CharSequence{
    if(!copySpans) return source.toString().toUpperCase(locale)
    val upcased: List<String> = source.map { c ->
       c.toString().toUpperCase(locale)
    }
    val spanned = source as Spanned
    val spans: Array<Any> = source.getSpans(0, source.length, Any::class.java)
    val sb = SpannableStringBuilder(upcased.joinToString(""))
    for(span in spans){
        val spanStart = spanned.getSpanStart(span)
        val spanEnd = spanned.getSpanEnd(span)
        val upcasedEndLength: Int = upcased.slice(spanStart until spanEnd).fold(0){ a,b -> a+b.length}
        val upcasedStartLength: Int = if(spanStart == 0) 0 else upcased.slice(0 until spanStart).fold(0){ a,b -> a+b.length}
        val destStart = upcasedStartLength
        val destEnd = destStart + upcasedEndLength

        val flags = spanned.getSpanFlags(span)
        sb.setSpan(span,destStart,destEnd,flags)
    }
    return sb
}
private-fun-toUpperCase(locale:locale,source:CharSequence,copySpans:Boolean):CharSequence{
if(!copySpands)返回source.toString().toUpperCase(区域设置)
val upcased:List=source.map{c->
c、 toString().toUpperCase(区域设置)
}
val量距=源为量距
val span:Array=source.getspan(0,source.length,Any::class.java)
val sb=SpannableStringBuilder(上壳连接字符串(“”)
用于(跨度中的跨度){
val spanStart=span。获取spanStart(span)
val spanEnd=已跨距。获取spanEnd(span)
val upcasedEndLength:Int=upcased.slice(从开始到结束)。折叠(0){a,b->a+b.length}
val upcasedstartength:Int=if(spanStart==0)0 else upcased.slice(0到spanStart.slice)。折叠(0){a,b->a+b.length}
val destStart=upcasedstartength
val destEnd=destStart+upcasedEndLength
val flags=span.getSpanFlags(span)
sb.设置范围(范围、开始、结束、标志)
}
归还某人
}
这在SDK29 btw中不是必需的,因为那里的AllCapsTransformation方法可以完美地处理跨度。
您可以导入
com.ibm.icu:icu4j:67.1
,并从中使用所需的方法,它与SDK29中的方法相同,但会大大增加应用程序的大小。

仅使用
.toUpperCase()
和重新应用跨距不够好。如果您有像
ß
这样的字符,跨距长度将是错误的。当
ß
变为
SS
时,大写

可能有一些语言在大写时将多个字符转换为一个字符,但我不知道有什么例子。我们可以通过逐个大写字符来完全避免这种情况

下面是一个简单的解决方案,可以处理大多数边缘情况:

private fun toUpperCase(locale: Locale, source: CharSequence, copySpans: Boolean): CharSequence{
    if(!copySpans) return source.toString().toUpperCase(locale)
    val upcased: List<String> = source.map { c ->
       c.toString().toUpperCase(locale)
    }
    val spanned = source as Spanned
    val spans: Array<Any> = source.getSpans(0, source.length, Any::class.java)
    val sb = SpannableStringBuilder(upcased.joinToString(""))
    for(span in spans){
        val spanStart = spanned.getSpanStart(span)
        val spanEnd = spanned.getSpanEnd(span)
        val upcasedEndLength: Int = upcased.slice(spanStart until spanEnd).fold(0){ a,b -> a+b.length}
        val upcasedStartLength: Int = if(spanStart == 0) 0 else upcased.slice(0 until spanStart).fold(0){ a,b -> a+b.length}
        val destStart = upcasedStartLength
        val destEnd = destStart + upcasedEndLength

        val flags = spanned.getSpanFlags(span)
        sb.setSpan(span,destStart,destEnd,flags)
    }
    return sb
}
private-fun-toUpperCase(locale:locale,source:CharSequence,copySpans:Boolean):CharSequence{
if(!copySpands)返回source.toString().toUpperCase(区域设置)
val upcased:List=source.map{c->
c、 toString().toUpperCase(区域设置)
}
val量距=源为量距
val span:Array=source.getspan(0,source.length,Any::class.java)
val sb=SpannableStringBuilder(上壳连接字符串(“”)
用于(跨度中的跨度){
val spanStart=span。获取spanStart(span)
val spanEnd=已跨距。获取spanEnd(span)
val upcasedEndLength:Int=upcased.slice(从开始到结束)。折叠(0){a,b->a+b.length}
val upcasedstartength:Int=if(spanStart==0)0 else upcased.slice(0到spanStart.slice)。折叠(0){a,b->a+b.length}
val destStart=upcasedstartength
val destEnd=destStart+upcasedEndLength
val flags=span.getSpanFlags(span)
sb.设置范围(范围、开始、结束、标志)
}
归还某人
}
这在SDK29 btw中不是必需的,因为那里的AllCapsTransformation方法可以完美地处理跨度。
您可以导入
com.ibm.icu:icu4j:67.1
并使用所需的方法,它与SDK29中的方法相同,但会大大增加应用程序的大小。

请参阅TextUtils.copySpans from method谢谢。这是一种比我提出的更优雅的方法。顺便说一句,replace不会保留跨距,因此您的解决方案不起作用ing(至少在v2.2中)请参见,int,java.lang.CharSequence)“如果源代码已被跨距,则源代码中的跨距将保留在可编辑文件中。可编辑文件中完全覆盖替换范围的现有跨距将被保留,但严格在替换范围内的跨距将被删除。”这就解释了为什么它对我仍然有效。我所有的字符串都有涵盖整个内容的跨距。我将编辑我的答案。请参阅TextUtils.copyspans from methodTh