2017-08-11 54 views
8

Tôi đã dành hàng giờ tìm kiếm câu trả lời và thực sự không có ý tưởng làm thế nào để giải quyết nó. Vì vậy, hãy bắt tay vào kinh doanh:Xem văn bản dòng xung quanh hình ảnh

Có một hình ảnh và một TextView và tôi cần phải chảy các TextView quanh ImageView như thế này:

enter image description here

giải pháp đầu tiên có thể woult được sử dụng https://github.com/deano2390/FlowTextView nhưng nó không mở rộng TextView để thư viện này không phù hợp với tôi vì nhiều lý do.

giải pháp thứ hai sẽ được sử dụng LeadingMarginSpan.LeadingMarginSpan2 tuổi nhưng nó ảnh hưởng trên mỗi đoạn cho mỗi n dòng trong văn bản (như trong câu trả lời này ->How to layout text to flow around an image), vì vậy tôi có được smth như thế này:

enter image description here

Nhưng tôi muốn đặt lề chỉ cho n dòng đầu tiên! Sau đó, tôi quyết định triển khai LeadingMarginSpan.Standart và tạo bộ đếm và tăng số lượt truy cập trong yêu cầu hàm getLeadingMargin(first: Boolean): Int. Khi bộ đếm đạt giá trị mong muốn, hàm trả về 0 dưới dạng chiều rộng lề. Và có một lần nữa thất bại! Thay vì điền các dòng TextView, văn bản vừa di chuyển sang trái và không lan sang cuối chế độ xem!

UPD: Vâng, tôi đã sử dụng onGlobalLayoutListener ở đây

enter image description here

Vâng, googling cho một giải pháp tôi đã tìm thấy câu trả lời này https://stackoverflow.com/a/27064368/7218592 Ok, tôi đã làm mọi thứ như mô tả và thực hiện các mã:

  //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 của tôi trông như thế này: `

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

`

Vâng, nó hoạt động. Nhưng nó hoạt động thế nào! Vì tôi đang sử dụng mẫu trình giữ chế độ xem, tôi phải giữ một biến cho người nghe và xóa nếu không được gọi và loại bỏ thành công vì chức năng onGlobalLayout không được gọi đúng lúc! Và nó được gọi là quá muộn, vì vậy bạn cần phải chờ khoảng 300 ms và sau đó xem tất cả các "tái thiết" của TextView và có vẻ ghê tởm!

Vì vậy, câu hỏi của tôi là: Làm thế nào để làm cho lợi nhuận cho đầu tiên n dòng trong TextView, trước khi nó được vẽ trên giao diện người dùng?

+0

tôi sẽ cố gắng một cách tiếp cận khác nhau ở đây, tôi sẽ sử dụng một webview đây và thiết lập hình ảnh và văn bản trong đó –

+0

Webview không phải là một lựa chọn vì có một số tùy chỉnh có thể click kéo dài mà tôi cần giải quyết. Tôi không thể từ bỏ chúng :( – koresuniku

+0

Bản sao có thể có của [Cách bố cục văn bản để xoay quanh hình ảnh] (https://stackoverflow.com/questions/2248759/how-to-layout-text-to-flow-around- một tấm ảnh) –

Trả lời

0

Cuối cùng, tôi đã cố gắng tìm ra giải pháp tốt nhất. Nó dựa trên việc tạo ra một bản mock-up của TextView chiều rộng mong muốn, sử dụng StaticLayout

  1. Trước hết, chúng ta hãy tính toán độ rộng của chúng tôi TextView (chỉ trừ tất cả các miếng đệm bên và chứa hình ảnh chiều rộng từ chiều rộng hiển thị) tính bằng pixel.
  2. Thứ hai, chúng tôi sẽ cần chiều cao của vùng chứa hình ảnh (tính bằng pixel).
  3. Thứ ba, như các chương trình thực tế, StaticLayout bỏ qua các dấu ngắt dòng ("\n" hoặc "\r"), vì vậy chúng ta cần phải tách chuỗi của chúng tôi vào duy nhất, dòng không bị phá hủy để thành công mô hình lên TextView bố trí: val commentParts = spannable.toString().split("\r")
  4. Sau đó, thuật toán sau tạo ra các trường hợp StaticLayout cho mỗi dòng và kiểm tra xem các dòng có vượt quá chiều cao vùng chứa hình ảnh hay không. Trong trường hợp họ làm, cuối cùng chúng tôi có thể nhận được vị trí của char cuối cùng của dòng cuối cùng, bên cạnh bên phải của thùng chứa hình ảnh. Và sau đó chúng ta cần phải tạo ra một ngắt dòng bằng chính mình, để cho LeadingMarginSpanLayout biết ngừng sản xuất lợi nhuận bắt đầu với [end + 1] char mục:

    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. Và điều cuối cùng, thiết lập LeadingMarginSpan2 trên chuỗi spannable:

    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. Cuối cùng, khoảng được đặt chính xác!

    !

Các vấn đề liên quan