You often see this effect on Android:

Are you still using 2 TextViews?

So how do we use TextView in this case?

That is too out, today I will take you to redefine TextView, and encapsulate all of a sudden!

Here’s a look at today’s results:

Take a look at the GIF effect:

Introduce Spannable

Spannable has two implementation classes, SpannableString and SpannableStringBuilder, as shown below

photo

SpannableString and SpannableStringBuilder

  • SpannableString SpannableString

  • SpannableStringBuilder SpannableStringBuilder

The difference is similar to String and StringBuilder

SpannableString is similar to SpannableStringBuilder. This article uses SpannableString

SpannableString method:

SpannableString parameters The return value instructions
charAt(int) char Returns the char value at the specified index.
getSpanEnd(Object ) int Returns terminating data, or -1 if there is no data.
getSpanFlags(Object ) int Returns the flag specified when the specified tag object is attached using Spannable#setSpan, or 0 if the specified object has not been attached.
getSpanStart(Object ) int Returns the beginning of the text, or -1 if there is no data.
removeSpan(Object ) void Delete a specified location
setSpan(Object,int,int,int) void Appends the specified tag object to the beginning of the text… End area, or move the object to that area if it has been attached to another location.

The main method used here is setSpan(). Refer to the official documentation

So much for the theory, let’s put it into practice

SpannableString is simple and useful

Text and load local images:

Let’s start with the code:

The main thing we use here is ImageSpan(), which is then set to the TextView using the spannableString.setspan () method

Here’s an explanation of the arguments to setSpan()

  • Parameter 1: Object
  • Parameter 2: Placeholder position for SpannableString (where placeholder value is located)
  • Parameter 3: Placeholder end position for SpannableString
  • Parameter 4: Image position (I don’t see any effect)

More style:

See other bloggers for more details

Text and load web pictures:

I use Glide to load network pictures

It is used to removeSpan() to remove the local image. SetSpan () is used to set the Drawable obtained by Glide to TextView Can be

Let’s look at the code:

private fun buildImage(url: String, width: Int, height: Int) {
        val ss = SpannableStringBuilder(url)
        val drawable = getDrawableData()

        // Set Drawable image width and height
        drawable.setBounds(5.5, width, height)
        / / placeholder
        val placeholderSpan = ImageSpan(drawable)
        ss.setSpan(placeholderSpan, 0, url.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

        // Add data (first hold a good position, then wait for the image to load before deleting, finally set the network image)
        textView.append(ss)

        // Get the Spannable of Text
        val spannable = textView.text as Spannable

        //Glide loads the network image and gets the Drawable
        Glide.with(this)
            .asDrawable()
            .load(url)
            .into(object : SimpleTarget<Drawable>() {
                override fun onResourceReady(
                    resource: Drawable,
                    transition: Transition<in Drawable>?,
                ) {
                    // Get the starting position
                    val start = spannable.getSpanStart(placeholderSpan)
                    // Get the end position
                    val end = spannable.getSpanEnd(placeholderSpan)

                    // The width and height of the current image is not -1
                    if(start ! = -1&& end ! = -1) {
                        // Set the image width and height
                        resource.setBounds(0.0, width, height)

                        // Delete the placeholder, and then reset the network image
                        spannable.removeSpan(placeholderSpan)
                        
                        // Set the network image
                        spannable.setSpan(ImageSpan(resource),
                            start,
                            end,
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                        )
                    }
                }
            })
    }
Copy the code

Comments are very clear, remember to leave a comment in the comments section!

Through writing so much code, we found that a lot of code is repetitive, and the attributes need to be set one by one, particularly troublesome, and not convenient to use, so we will give him a package.

After observing that the Builder mode works well, let’s go with the Builder mode

Builder mode encapsulation

Take the most commonly used examples, the rest of their own download Demo oh ~

Define the builder pattern specification:

public interface RichBuilder {
    RichBuilder addText(String msg);// Add text
    
    RichBuilder setOnClick(RichTextView.onItemClick onClick);// Click the event

    RichBuilder build(a);/ / sure
}
Copy the code

Implementing the RichBuilder specification:

public class RichTextView extends androidx.appcompat.widget.AppCompatTextView
        implements RichBuilder {
        
        // Necessary constructor
      public RichTextView(@NonNull Context context) {
        super(context, null);
      }

    public RichTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs, 0);
    }

    public RichTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }  
    
    
    private final ArrayList<Object> objList = new ArrayList<>();

    String text = "text";
      
    @Override
    public RichBuilder addText(String msg) {
        text = msg;
        return this;
    }
    
    // Click the event
    @Override
    public RichBuilder setOnClick(onItemClick onClick) {
        objList.add(new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                // Interface callbackonClick.click(); }});// Make it clickable
        setMovementMethod(LinkMovementMethod.getInstance());
        return this;
    }
    
    @Override
    public RichBuilder build(a) {
        SpannableString ss = new SpannableString(text);
        for (Object o : objList) {
            ss.setSpan(o, 0, text.length(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        append(ss);
        
        // Be sure to empty objList
        objList.clear();
        return this;
    }
       
    // Click the event callback
    public interface onItemClick {
        void click(a); }}Copy the code

Use:

            richText
                .addText("100")
                .setOnClick {
                    toast("Hit 100.")
                }
                .build()
                .addText(" m\n\n")
                .build()
                .addText("I am the data of the builder schema")
                .setOnClick {
                    toast("I am the data of the builder schema")
                }
                .build()
Copy the code

Effect:

A mathematical formula

Here’s how the math works:

        tvFormula
            .addText("(X")
            .build()
            .addText("1")
            .setSubscript()     // Set the subscript
            .setSubscriptSize(30)   // Angular size
            .build()
            .addText("+")
            .build()
            .addText("2")
            .setSuperscript()   // Set the superscript
            .setSubscriptSize(30)
            .build()
            .addText(")")
            .build()
Copy the code

The final result is:

Download the rest of the Demo to see ~

The complete code

What do you like?

  • Blogger page

Original is not easy, your praise is the biggest support for me!