Animate MaxLines and Ellipsize

前端 未结 4 1074
误落风尘
误落风尘 2020-12-13 06:51

I have textview which contains a part of a text. When the user clicks the arrow, the textview resizes so the full text is shown. See the images below for an example:

相关标签:
4条回答
  • 2020-12-13 07:37

    You can achieve this using an ObjectAnimator

    ObjectAnimator animation = ObjectAnimator.ofInt(
            tvText,
            "maxLines", 
            25);
    animation.setDuration(4000);
    animation.start();
    

    This will increase the "maxLines" property of the "tvText" TextView from whatever it initially is set to, to 25, over the period of 4000 milliseconds.

    See more here and here.

    0 讨论(0)
  • 2020-12-13 07:38

    All of the other solutions didn't work nicely for me. Animation was choppy or even blinking.

    What I chose to do is animating of the layoutParams height instead. This solution might not fit for every case, but for me it seems to work nicely. Here's a demonstration:

    MainActivity.kt

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val expectedWidthOfTextView = resources.displayMetrics.widthPixels
            val originalMaxLines = textView.maxLines
            if (originalMaxLines < 0 || originalMaxLines == Integer.MAX_VALUE)
                Log.d("AppLog", "already unbounded textView maxLines")
            else {
                textView.maxLines = Integer.MAX_VALUE
                textView.measure(
                    View.MeasureSpec.makeMeasureSpec(expectedWidthOfTextView, View.MeasureSpec.AT_MOST),
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
                )
                val measuredLineCount = textView.lineCount
                val measuredTargetHeight = textView.measuredHeight
                Log.d("AppLog", "lines:$measuredLineCount/$originalMaxLines")
                textView.maxLines = originalMaxLines
                if (measuredLineCount <= originalMaxLines)
                    Log.d("AppLog", "fit in original maxLines")
                else {
                    Log.d("AppLog", "exceeded original maxLines")
                    textView.setOnClickListener {
                        textView.setOnClickListener(null)
                        textView.maxLines = Integer.MAX_VALUE
                        val layoutParams = textView.layoutParams
                        val animation = ValueAnimator.ofInt(textView.height, measuredTargetHeight)
                        animation.addUpdateListener { valueAnimator ->
                            val value: Int = valueAnimator.animatedValue as Int
                            layoutParams.height = value
                            textView.requestLayout()
                        }
                        animation.start()
                        layoutParams.height = textView.height
                    }
                }
            }
    
        }
    }
    

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent" android:orientation="vertical" android:gravity="center_horizontal"
            android:layout_height="match_parent" android:animateLayoutChanges="true"
            tools:context=".MainActivity">
    
        <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content"
                   android:src="@android:drawable/sym_def_app_icon"/>
    
        <TextView
                android:id="@+id/textView" android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:ellipsize="end" android:maxLines="4" android:clickable="true" android:focusable="true"
                android:paddingEnd="16dp" android:paddingStart="16dp"
                android:textColor="#c1000000" android:textSize="14sp"
                android:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."/>
    
        <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content"
                   android:src="@android:drawable/sym_def_app_icon"/>
    </LinearLayout>
    
    0 讨论(0)
  • 2020-12-13 07:41

    While animating maxLines works, the result is a bit choppy since your view height jumps a lot.

    int startHeight = content.getMeasuredHeight();
    content.setMaxLines(Integer.MAX_VALUE);
    content.measure(
                View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.AT_MOST),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    int endHeight = content.getMeasuredHeight();
    

    content is a TextView with maxLines set to 2. Now you can animate the TextView height instead. Edit: TextView scrolls when it can't fit it's content vertically, you'll need workarounds. setMovementMethod(null) disables scrolling, but it also disables link clicking. Because Android.

    0 讨论(0)
  • 2020-12-13 07:47

    The accepted answer is fundamentally wrong, because it forces the call of TextView.setMaxLines() lots of times with the same value without reason, making the resulting animation jerky.

    You could use a simple ValueAnimator with a lastValue flag instead:

        ValueAnimator animator = ValueAnimator.ofInt(fromThisLineCount, toThisLineCount).setDuration(250);
    
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            int lastValue = -1;
    
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
    
                if (value == lastValue) {
                    return;
                }
    
                lastValue = value;
    
                yourTextView.setMaxLines(value);
            }
        });
    
        animator.start();
    

    Remember that the animation adds/removes 1 whole line every time, so it may still look jerky if the animation duration is too high or if it affects just a few lines. The best approach is to create a custom View and make the appropriate measurements onMeasure (see e.g. https://github.com/Manabu-GT/ExpandableTextView).

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