Intereting Posts

Текстовое изображение потока вокруг изображения

Я много часов искал ответа и понятия не имею, как его решить. Итак, давайте приступим к делу:

Существует изображение и TextView и мне нужно TextView вокруг ImageView следующим образом:

Введите описание изображения здесь

Первое возможное решение – использовать https://github.com/deano2390/FlowTextView, но оно не расширяет TextView поэтому эта библиотека не подходит для меня по ряду причин.

Второе решение – использовать LeadingMarginSpan.LeadingMarginSpan2 span, но он влияет на каждый абзац для каждой n строк внутри текста (например, в этом ответе -> Как текст макета обтекать изображение ), поэтому я получаю smth:

Введите описание изображения здесь

Но я хотел установить маржу только для первых n строк! Затем я решил реализовать LeadingMarginSpan.Standart и создать счетчик и getLeadingMargin(first: Boolean): Int его в getLeadingMargin(first: Boolean): Int вызов функции getLeadingMargin(first: Boolean): Int . Когда счетчик достигает желаемого значения, функция возвращает 0 в качестве ширины поля. И снова сбой! Вместо того, чтобы заполнять строки TextView , текст просто перемещался влево и не распространялся до конца представления!

UPD: Да, я использовал onGlobalLayoutListener здесь

Введите описание изображения здесь

Ну, googling для другого решения я нашел этот ответ https://stackoverflow.com/a/27064368/7218592 Хорошо, я сделал все, как описано и реализовал код:

  //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())))))` 

Мой OnGlobalLayoutListener выглядит так: `

 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 } } 

`

Ну, это работает. Но как это ужасно! Поскольку я использую шаблон владельца приложения, я должен держать переменную в слушателе и удалять, если она не была вызвана и успешно удалена, потому onGlobalLayout функция onGlobalLayout не была вызвана вовремя! И это называется слишком поздно, поэтому вам нужно подождать около 300 мс, а затем посмотреть всю «реконструкцию» TextView и это выглядит отвратительно!

Итак, мой вопрос: как сделать поля для первых n строк в TextView , прежде чем он будет нарисован в пользовательском интерфейсе?

Solutions Collecting From Web of "Текстовое изображение потока вокруг изображения"

Наконец, мне удалось найти лучшее решение. Он основан на создании макета TextView требуемой ширины, используя StaticLayout

  1. Во-первых, давайте вычислим ширину нашего TextView (просто вычитаем все боковые paddings и ширину контейнера изображения из ширины экрана) в пикселях.
  2. Во-вторых, нам понадобится высота контейнера изображения (в пикселях).
  3. В-третьих, как показывает практика, StaticLayout игнорирует разрывы строк ( "\n" или "\r" ), поэтому нам нужно разбить нашу строку на одиночные неразрывные строки, чтобы успешно макетировать макет TextView : val commentParts = spannable.toString().split("\r")
  4. Затем следующий алгоритм создает экземпляры StaticLayout для каждой строки и проверяет, превышают ли строки высоту контейнера изображения. В случае, если они это сделают, мы, наконец, можем получить позицию последнего символа последней строки, расположенной справа от контейнера изображения. И тогда нам нужно создать разрыв строки самостоятельно, чтобы LeadingMarginSpanLayout знал, чтобы прекратить делать поля, начиная с позиции [end + 1] char:

      var endReached: Boolean = false; commentParts.forEach { if (endReached) return@forEach val layout: StaticLayout = StaticLayout(it, holder.mCommentTextView!!.paint, textViewWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, false) if (layout.lineCount > 0) { var localHeight: Int for (lineIndex in 0..layout.lineCount - 1) { localHeight = layout.getLineBottom(lineIndex) if (localHeight + totalHeightOfLines > imageContainerHeight) { endReached = true end = layout.getLineEnd(lineIndex) val spannableStringBuilder = SpannableStringBuilder(spannable) if (spannable.substring(end - 1, end) != "\n" && spannable.substring(end - 1, end) != "\r") { if (spannable.substring(end - 1, end) == " ") { spannableStringBuilder.replace(end - 1, end, "\n") } else { spannableStringBuilder.insert(end, "\n") } } spannable = SpannableString(spannableStringBuilder) break } } totalHeightOfLines += layout.lineCount * holder.mCommentTextView!!.lineHeight } } 
  5. И последнее, установите LeadingMarginSpan2 на растянутую строку:

     spannable.setSpan(CommentLeadingMarginSpan2( CommentLeadingMarginSpan2.calculateLeadingMarginWidthInPx(holder)), 0, if (end == 0) spannable.length else end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) holder.mCommentTextView!!.text = spannable holder.mCommentTextView!!.requestLayout() 
  6. В конце концов, диапазон установлен правильно!

    !