How to use custom ellipsis in Android TextView

前端 未结 5 1777
陌清茗
陌清茗 2020-12-31 09:55

I have a TextView with maxlines=3 and I would like to use my own ellipsis, instead of

\"Lore ipsum ...\"

I need

\"Lore ips         


        
相关标签:
5条回答
  • 2020-12-31 10:22

    Here is a solution for Kotlin.

    The yourTextView.post{} is necessary because the textview won't be ellipsized until after it is rendered.

      val customSuffix = "... [See more]"
      yourTextView.post {
        if (yourTextView.layout.getEllipsisStart(-1) != -1) {
          val newText = yourTextView.text.removeRange(
              yourTextView.layout.getEllipsisStart(-1) - customSuffix.length, yourTextView.text.length
          )
          yourTextView.text = String.format("%s%s", newText, customSuffix)
        }
      }
    
    0 讨论(0)
  • 2020-12-31 10:27

    @George @jmhostalet i was doing this in my recycler view and it degraded the whole performance. `

    ViewTreeObserver vto = previewContent.getViewTreeObserver();
        vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    try {
                        Layout layout = previewContent.getLayout();
                        int index1 = layout.getLineStart(previewContent.getMaxLines());
                        if (index1 > 10 && index1 < ab.getPreviewContent().length()) {
                            String s =
                                    previewContent.getText().toString().substring(0, index1 - 10);
                            previewContent
                                    .setText(Html.fromHtml(
                                            s + "<font color='#DC5530'>...और पढ़ें</font>"));
                        }
                        return true;
                    }catch (Exception e)
                    {
                        Crashlytics.logException(e);
                    }
                    return true;
                }
            });` 
    
    0 讨论(0)
  • 2020-12-31 10:31

    I've finally managed it in this way (may be not the best one):

    private void setLabelAfterEllipsis(TextView textView, int labelId, int maxLines){
    
        if(textView.getLayout().getEllipsisCount(maxLines-1)==0) {
            return; // Nothing to do
        }
    
        int start = textView.getLayout().getLineStart(0);
        int end = textView.getLayout().getLineEnd(textView.getLineCount() - 1);
        String displayed = textView.getText().toString().substring(start, end);
        int displayedWidth = getTextWidth(displayed, textView.getTextSize());
    
        String strLabel = textView.getContext().getResources().getString(labelId);
        String ellipsis = "...";
        String suffix = ellipsis + strLabel;
    
        int textWidth;
        String newText = displayed;
        textWidth = getTextWidth(newText + suffix, textView.getTextSize());
    
        while(textWidth>displayedWidth){
            newText = newText.substring(0, newText.length()-1).trim();
            textWidth = getTextWidth(newText + suffix, textView.getTextSize());
        }
    
        textView.setText(newText + suffix);
    }
    
    private int getTextWidth(String text, float textSize){
        Rect bounds = new Rect();
        Paint paint = new Paint();
        paint.setTextSize(textSize);
        paint.getTextBounds(text, 0, text.length(), bounds);
    
        int width = (int) Math.ceil( bounds.width());
        return width;
    }
    
    0 讨论(0)
  • 2020-12-31 10:31

    I think the answer from @jmhostalet will degrade the performance (especially when dealing with lists and lots of TextViews) because the TextView draws the text more than once. I've created a custom TextView that solves this in the onMeasure() and therefore only draws the text once.

    I've originally posted my answer here: https://stackoverflow.com/a/52589927/1680301

    And here's the link to the repo: https://github.com/TheCodeYard/EllipsizedTextView

    0 讨论(0)
  • 2020-12-31 10:36

    Here's a nice way to do it with a Kotlin extension. Note that we need to wait for the view to layout before we can measure and append the suffix.

    In TextViewExtensions.kt

    fun TextView.setEllipsizedSuffix(maxLines: Int, suffix: String) {
        addOnLayoutChangeListener(object: View.OnLayoutChangeListener {
            override fun onLayoutChange(v: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom:     Int) {
    
                val allText = text.toString()
                var newText = allText
                val tvWidth = width
                val textSize = textSize
    
                if(!TextUtil.textHasEllipsized(newText, tvWidth, textSize, maxLines)) return
    
                while (TextUtil.textHasEllipsized(newText, tvWidth, textSize, maxLines)) {
                    newText = newText.substring(0, newText.length - 1).trim()
                }
    
                //now replace the last few chars with the suffix if we can
                val endIndex = newText.length - suffix.length - 1 //minus 1 just to make sure we have enough room
                if(endIndex > 0) {
                    newText = "${newText.substring(0, endIndex).trim()}$suffix"
                }
    
                text = newText
    
                removeOnLayoutChangeListener(this)
            }
        })
    }
    

    In TextUtil.kt

    fun textHasEllipsized(text: String, tvWidth: Int, textSize: Float, maxLines: Int): Boolean {
        val paint = Paint()
        paint.textSize = textSize
        val size = paint.measureText(text).toInt()
    
        return size > tvWidth * maxLines
    }
    

    Then actually using it like this myTextView.setEllipsizedSuffix(2, "...See more")


    Note: if your text comes from a server and may have new line characters, then you can use this method to determine if the text has ellipsized.

    fun textHasEllipsized(text: String, tvWidth: Int, textSize: Float, maxLines: Int): Boolean {
        val paint = Paint()
        paint.textSize = textSize
        val size = paint.measureText(text).toInt()
        val newLineChars = StringUtils.countMatches(text, "\n")
    
        return size > tvWidth * maxLines || newLineChars >= maxLines
    }
    

    StringUtils is from implementation 'org.apache.commons:commons-lang3:3.4'

    0 讨论(0)
提交回复
热议问题