2017-08-11 30 views
8

我花了数小时寻找答案,并且真的不知道如何解决它。因此,让我们言归正传:在图像周围流动textview

有一个图像和TextView,我需要流动TextViewImageView周围像这样:

enter image description here

第一个可能的解决方案woult是使用https://github.com/deano2390/FlowTextView但它不扩展TextView所以这个库不适合我的原因数量。

第二种解决方案是使用LeadingMarginSpan.LeadingMarginSpan2跨度,但它影响每个段落文本内的每个n行(像这样的答案 - >How to layout text to flow around an image),所以我得到水木清华这样的:

enter image description here

但我想只为前n行设置保证金!然后我决定执行LeadingMarginSpan.Standart并创建一个计数器并在getLeadingMargin(first: Boolean): Int函数调用中增加它。当计数器达到期望值时,函数返回0作为边距宽度。再次失败!而不是填充TextView行,文本只是左移,并没有传播到视图的尽头!

UPD:是的,我在这里使用onGlobalLayoutListener

enter image description here

那么,谷歌搜索另一种解决方案,我发现这个答案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函数未被及时调用!它被称为太晚了,所以你需要等待约300毫秒,然后观看TextView的所有“重建”,它看起来令人作呕!

所以,我的问题是: 如何使利润率第一 n行的TextView,它被画在UI之前?

+0

我会尝试不同的方法在这里,我将在这里使用网页视图,设置图像和文字在那里 –

+0

Webview是不是因为有一个选项,几个自定义可点击的跨越,我需要处理。我不能放弃他们:( – koresuniku

+0

可能重复[如何布局文本流动图像周围](https://stackoverflow.com/questions/2248759/how-to-layout-text-to-flow-around-一个图像) –

回答

0

最后,我设法找到了最好的解决方案。它是基于创建所需宽度的TextView的实体模型,使用StaticLayout

  1. 首先,让我们计算我们TextView的宽度(只是。减去所有的侧面填充和图像容器宽度从显示宽度)以像素为单位
  2. 其次,我们需要图像容器的高度(以像素为单位)。
  3. 第三,实践表明,StaticLayout忽略换行符("\n""\r"),所以我们需要把我们的字符串分割成单个非破线,以便成功地模拟了TextView布局: val commentParts = spannable.toString().split("\r")
  4. 然后,下面的算法为每一行创建StaticLayout实例,并检查这些行是否超过图像容器高度。如果他们这样做,我们终于可以获得最后一行的最后一个字符的位置,与图像容器的右侧相邻。然后,我们需要创建我们自己换行,为了让LeadingMarginSpanLayout知道停止生产开始[end + 1]焦炭页边距:

    var endReached: Boolean = false; 
          commentParts.forEach { 
           if (endReached) [email protected] 
    
           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. 和最后一件事,在spannable字符串设定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. 最终,跨度设置正确!

    !