10

I've spent hours looking for answer and have really no idea how to solve it. So let's get down to business:

There is an image and a TextView and I need to flow the TextView around the ImageView like this:

enter image description here

First possible solution woult be to use https://github.com/deano2390/FlowTextView but it's not extending TextView so this library is not suitable for me for number of reasons.

Second solution would be to use LeadingMarginSpan.LeadingMarginSpan2 span but it affects on each paragraph for each n lines inside the text (like in this answer -> How to layout text to flow around an image), so I get smth like this:

enter image description here

But I wanted to set margin only for first n lines! Then I decided to implement LeadingMarginSpan.Standart and create a counter and increment it in getLeadingMargin(first: Boolean): Int function invocation. When the counter reach the desirable value, the function returns 0 as a margin width. And there is a fail again! Instead of filling the TextView lines, the text just moved left and didn't spread to the end of the view!

UPD: Yes, I've used onGlobalLayoutListener in here

enter image description here

Well, googling for another solution I found this answer https://stackoverflow.com/a/27064368/7218592 Ok, I've done everything as described and implemented the code:

            //set left margin of desirable width
            val params: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            params.leftMargin = holder.imageContainerHeight!!
            params.addRule(RelativeLayout.BELOW, holder.mNumberAndTimeInfo!!.id)
            holder.mCommentTextView!!.layoutParams = params
            if (holder.commentTextViewOnGlobalLayoutListener != null)
                holder.mCommentTextView!!.viewTreeObserver.removeOnGlobalLayoutListener(
                        holder.commentTextViewOnGlobalLayoutListener)

            //add onGlobalLayoutListener
            holder.mCommentTextView!!.viewTreeObserver.addOnGlobalLayoutListener(
                    if (holder.commentTextViewOnGlobalLayoutListener != null)
                        holder.commentTextViewOnGlobalLayoutListener
                    else CommentTextViewOnGlobalLayoutListener(holder,
                            SpannableString(HtmlCompat.fromHtml(
                            mView.getActivity(), commentDocument.html(), 0,
                            null, SpanTagHandlerCompat(mView.getActivity())))))`

My OnGlobalLayoutListener looks like this: `

class CommentTextViewOnGlobalLayoutListener(
            val holder: CommentAndFilesListViewViewHolder, val commentSpannable: Spannable) :
            ViewTreeObserver.OnGlobalLayoutListener {
        val LOG_TAG: String = CommentTextViewOnGlobalLayoutListener::class.java.simpleName

 override fun onGlobalLayout() {
        holder.mCommentTextView!!.viewTreeObserver.removeGlobalOnLayoutListener(this)

        //when textview layout is drawn, get the line end to spanify only the needed text
        val charCount = holder.mCommentTextView!!.layout.getLineEnd(Math.min(
                holder.mCommentTextView!!.layout.lineCount - 1,
                CommentLeadingMarginSpan.computeLinesToBeSpanned(holder)))
        if (charCount <= commentSpannable.length) {
            commentSpannable.setSpan(CommentLeadingMarginSpan(holder),
                    0, charCount, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        }

        //set the left margin back to zero
        (holder.mCommentTextView!!.layoutParams as RelativeLayout.LayoutParams).leftMargin = 0
        holder.mCommentTextView!!.text = commentSpannable
    }
}

`

Well, it works. But how terrible it works! As I'm using view holder pattern I have to hold a variable to the listener and remove if it is not been called and successfully removed because onGlobalLayout function wasn't called in time! And it is called too late, so you need to wait about 300 ms and then watch all the "reconstruction" of the TextView and it looks disgustingly!

So, my question is: How to make margins for first n lines in TextView, before it's been drawn on UI?

krsnk
  • 267
  • 1
  • 10
  • I would try a different approach here, I would use a webview here and set the image and text in there – Nick Asher Aug 11 '17 at 05:02
  • Webview is not an option because there are several custom clickable spans that I need to handle. I can't abandon them :( – krsnk Aug 11 '17 at 05:10
  • 3
    Possible duplicate of [How to layout text to flow around an image](https://stackoverflow.com/questions/2248759/how-to-layout-text-to-flow-around-an-image) – Dmytro Rostopira Aug 11 '17 at 10:29
  • I think issue was introduced [here](https://stackoverflow.com/questions/11494158/how-to-align-textview-around-an-imageview) , possible duplication. – Ahmed Nezhi May 26 '21 at 15:43

1 Answers1

0

This is more a suggestion that will only work with a little trial and error
This code uses a multi line Edit Text

        btnPrint.setOnClickListener {
        val str = """
        One
        Two
        Three
        Now click Action Button Custom SB
        """.trimIndent()
        etNews.setText(str)
    }

Play with the One Two values indent and trimIndent has other properties available

Vector
  • 3,066
  • 5
  • 27
  • 54