noties / Markwon

Android markdown library (no WebView)
https://noties.io/Markwon/
Apache License 2.0
2.77k stars 313 forks source link

Formatted text clipped with wrap content textviews #101

Closed sandboiii closed 5 years ago

sandboiii commented 5 years ago

Android has a bug, when italic text being clipped inside wrap content textviews. It also relates to code blocks and maybe other things. People usually suggest to add whitespaces to prevent clipping. Can this issue be solved somehow?
img_20190307_034959

sandboiii commented 5 years ago

Issue appears both on 2.x and 3.x version
Here is also my messages layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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="wrap_content"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:paddingEnd="15dp"
    android:paddingStart="60dp"
    android:clipToPadding="false">

    <TextView
        android:id="@+id/message_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginTop="8dp"
        android:background="@drawable/message_out_bubble"
        android:padding="10dp"
        android:textColor="#000"
        android:textSize="18sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="How are you doing? This is a long message that should probably wrap." />

</android.support.constraint.ConstraintLayout>
noties commented 5 years ago

Hello @Sandmonster !

It seems that TextView does not respect textPaint.setTextSkewX setting (which is used by the EmphasisSpan in Markwon). Adding a space would certanly help, but this is not flexible solution (it still can happen that measurement will be off, and additional space at the end doesn't seem like a perfect solution). Instead, and I've tested it, a custom typeface can be used for the Emphasis, which will result in proper rendering, for example:

final SpannableConfiguration configuration = SpannableConfiguration.builder(this)
        .factory(new SpannableFactoryDef() {
            @Override
            public Object emphasis() {
                return new CustomTypefaceSpan(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC));
            }
        })
        .build();

Markwon.setMarkdown(textView, configuration, markdown);

Please mind that this is for 2.x.x version in 3.x.x. it will be a bit different:

final Markwon markwon = Markwon.builder(context)
        .usePlugin(new AbstractMarkwonPlugin() {
            @Override
            public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
                builder.setFactory(
                        Emphasis.class,
                        (configuration, props) ->
                                new CustomTypefaceSpan(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)));
            }
        });

markwon.setMarkdown(textView, markdown);

Of cause, it's better to create this Typeface only once (during configuration or whatever) and use it single instance with all emphasis nodes (instead of creating a new instance for each span).

And, of cause, you have to create own implementation of CustomTypefaceSpan:

public class CustomTypefaceSpan extends MetricAffectingSpan {

    private final Typeface typeface;

    public CustomTypefaceSpan(@NonNull Typeface typeface) {
        this.typeface = typeface;
    }

    @Override
    public void updateMeasureState(@NonNull TextPaint textPaint) {
        textPaint.setTypeface(typeface);
    }

    @Override
    public void updateDrawState(TextPaint tp) {
        tp.setTypeface(typeface);
    }
}

This span appears from time to time and I think it's the time to include in with the library (even though it's not used right now internally for any of markdown nodes).

Hope this helps! 🙌

sandboiii commented 5 years ago

@noties thank you! This solved the problem with italic displaying. At least in 3.x version. I close the issue now though it might be useful to check that other formatting types are displayed correctly and correct it if it's wrong

shahimclt commented 9 months ago

If anyone else comes here looking for this, here is what worked for me: Use this instead of the CustomTypeFaceSpan:

class Italic : MetricAffectingSpan() {
    override fun updateDrawState(tp: TextPaint) = update(tp)
    override fun updateMeasureState(textPaint: TextPaint) = update(textPaint)
    private fun update(paint: TextPaint) {
        paint.setTypeface(Typeface.create(paint.typeface, Typeface.ITALIC));
    }
}

like so:

override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) {
    builder.setFactory(Emphasis::class.java) { configuration: MarkwonConfiguration?, props: RenderProps? ->
        Italic()
    }
}