2017-08-11 7 views
8

私は何時間も答えを探していて、解決方法は本当に分かりません。それでは、仕事に取り掛かるてみましょう:画像とTextViewがありフローテキストビューでのイメージ

と私はこのようなImageView周りTextViewを流す必要があります。

enter image description here

まず可能な解決策はhttps://github.com/deano2390/FlowTextViewを使用することwoultが、それはですTextViewを拡張していないので、このライブラリは数多くの理由から私には適していません。

第2の解決策はLeadingMarginSpan.LeadingMarginSpan2スパンを使用することですが、それはに(この答えのように - >How to layout text to flow around an image)テキスト内の各n行のための各段落に影響を与えますので、私はこのようになめらかを得る:

enter image description here

しかし最初のn行だけにマージンを設定したかったのです!その後、LeadingMarginSpan.Standartを実装してカウンタを作成し、それを関数呼び出しのgetLeadingMargin(first: Boolean): Intでインクリメントすることにしました。カウンタが望ましい値に達すると、関数はマージンの幅として0を返します。もう一度失敗します! TextView行を塗りつぶす代わりに、テキストがちょうど左に移動し、ビューの最後に広がっていませんでした!

UPD:はい、私はここに

enter image description here

onGlobalLayoutListenerを使用しましたまあ、私はこの答えhttps://stackoverflow.com/a/27064368/7218592 は[OK]を見つけ、別の解決策をグーグルで、私が説明するように、すべてを行って、コードを実装しました:

  //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のすべての "再構成"を見る必要があり、それは嫌になります!

だから、私の質問は: それはUI上に描かれています前に、TextView最初 n行のためのマージンを作るには?

+0

私はここのWebViewを使用し、そこ –

+0

WebViewの中で画像とテキストを設定します、ここでは異なるアプローチをしようと、私は必要ないくつかのカスタムクリック可能なスパンがあるので、オプションではありません処理する。私はそれらを放棄することはできません:( – koresuniku

+0

[画像の周りを流れるテキストをレイアウトする方法](https://stackoverflow.com/questions/2248759/how-to-layout-text-to-flow-around-画像) –

答えて

0

最後に、私は最良のソリューションを見つけることができました。それは、(単に表示幅からすべての側面のパディングとイメージコンテナの幅をsubstract)まず、私たちのTextViewの幅を計算してみましょうStaticLayout

  1. を使用して、所望の幅のTextViewのモックアップを作成するに基づいていますピクセル単位。
  2. 第2に、イメージコンテナの高さが必要です(ピクセル単位)。練習が示すように第三
  3. 、正常モックアップTextViewレイアウトのためにStaticLayoutは、改行("\n"または"\r")を無視するので、我々は、単一の、非破壊ラインに私たちの文字列を分割する必要があります。 val commentParts = spannable.toString().split("\r")
  4. 次に、次のアルゴリズムは、各行に対してStaticLayoutのインスタンスを作成し、その行がイメージコンテナの高さを超えているかどうかを確認します。それらの場合、最終的にイメージコンテナの右側に隣接する最後の行の最後の文字の位置を得ることができます。

    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を設定します:

    そして、我々はLeadingMarginSpanLayout[end + 1]文字位置から始まる余白を作る停止することを知らせるために、自分で改行を作成する必要があります
    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. 最終的にスパンが正しく設定されています。

    !

関連する問題