【问题标题】:How to fade out the end of the last line in a TextView?如何淡出TextView中最后一行的末尾?
【发布时间】:2018-03-15 09:21:13
【问题描述】:

我们如何在 TextView 的最后一行实现淡出效果,就像在 Play 商店应用中的“最新消息”部分中一样?

【问题讨论】:

    标签: android android-layout textview


    【解决方案1】:

    这种淡入淡出效果可以通过继承TextView 类来拦截它的绘制,并执行类似于View 类的方式来淡出边缘,但仅限于最后文本行的最后一段。

    在这个例子中,我们创建了一个从透明到纯黑色的单位水平线性渐变。当我们准备绘制时,这个单位梯度被缩放到一个长度,计算为TextView的最终线长度的一个简单分数,然后相应地定位。

    创建了一个屏幕外缓冲区,我们让TextView 将其内容绘制到该缓冲区。然后,我们使用PorterDuff.Mode.DST_OUT 的传输模式在其上绘制渐变渐变,这基本上将底层内容清除到与给定点的渐变不透明度相关的程度。无论背景是什么,在屏幕上绘制该缓冲区都会导致所需的淡入淡出。

    public class FadingTextView extends AppCompatTextView {
    
        private static final float FADE_LENGTH_FACTOR = .4f;
    
        private final RectF drawRect = new RectF();
        private final Rect realRect = new Rect();
        private final Path selection = new Path();
        private final Matrix matrix = new Matrix();
        private final Paint paint = new Paint();
        private final Shader shader =
                new LinearGradient(0f, 0f, 1f, 0f, 0x00000000, 0xFF000000, Shader.TileMode.CLAMP);
    
        public FadingTextView(Context context) {
            this(context, null);
        }
    
        public FadingTextView(Context context, AttributeSet attrs) {
            this(context, attrs, android.R.attr.textViewStyle);
        }
    
        public FadingTextView(Context context, AttributeSet attrs, int defStyleAttribute) {
            super(context, attrs, defStyleAttribute);
    
            paint.setShader(shader);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            // Locals
            final RectF drawBounds = drawRect;
            final Rect realBounds = realRect;
            final Path selectionPath = selection;
            final Layout layout = getLayout();
    
            // Figure last line index, and text offsets there
            final int lastLineIndex = getLineCount() - 1;
            final int lastLineStart = layout.getLineStart(lastLineIndex);
            final int lastLineEnd = layout.getLineEnd(lastLineIndex);
    
            // Let the Layout figure a Path that'd cover the last line text
            layout.getSelectionPath(lastLineStart, lastLineEnd, selectionPath);
            // Convert that Path to a RectF, which we can more easily modify
            selectionPath.computeBounds(drawBounds, false);
    
            // Naive text direction determination; may need refinement
            boolean isRtl =
                    layout.getParagraphDirection(lastLineIndex) == Layout.DIR_RIGHT_TO_LEFT;
    
            // Narrow the bounds to just the fade length
            if (isRtl) {
                drawBounds.right = drawBounds.left + drawBounds.width() * FADE_LENGTH_FACTOR;
            } else {
                drawBounds.left = drawBounds.right - drawBounds.width() * FADE_LENGTH_FACTOR;
            }
            // Adjust for drawables and paddings
            drawBounds.offset(getTotalPaddingLeft(), getTotalPaddingTop());
    
            // Convert drawing bounds to real bounds to determine
            // if we need to do the fade, or a regular draw
            drawBounds.round(realBounds);
            realBounds.offset(-getScrollX(), -getScrollY());
            boolean needToFade = realBounds.intersects(getTotalPaddingLeft(), getTotalPaddingTop(),
                    getWidth() - getTotalPaddingRight(), getHeight() - getTotalPaddingBottom());
    
            if (needToFade) {
                // Adjust and set the Shader Matrix
                final Matrix shaderMatrix = matrix;
                shaderMatrix.reset();
                shaderMatrix.setScale(drawBounds.width(), 1f);
                if (isRtl) {
                    shaderMatrix.postRotate(180f, drawBounds.width() / 2f, 0f);
                }
                shaderMatrix.postTranslate(drawBounds.left, drawBounds.top);
                shader.setLocalMatrix(shaderMatrix);
    
                // Save, and start drawing to an off-screen buffer
                final int saveCount;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    saveCount = canvas.saveLayer(null, null);
                } else {
                    saveCount = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
                }
    
                // Let TextView draw itself to the buffer
                super.onDraw(canvas);
    
                // Draw the fade to the buffer, over the TextView content
                canvas.drawRect(drawBounds, paint);
    
                // Restore, and draw the buffer back to the Canvas
                canvas.restoreToCount(saveCount);
            } else {
                // Regular draw
                super.onDraw(canvas);
            }
        }
    }
    

    这是TextView 的直接替代品,您可以类似地在布局中使用它。

    <com.example.app.FadingTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#e2f3eb"
        android:textColor="#0b8043"
        android:lineSpacingMultiplier="1.2"
        android:text="@string/umang" />
    


    注意事项:

    • 淡入淡出长度的计算基于最后一行文本长度的恒定分数,此处由FADE_LENGTH_FACTOR 确定。这似乎与 Play Store 组件的基本方法相同,因为淡入淡出的绝对长度似乎随行长而变化。 FADE_LENGTH_FACTOR 值可以根据需要更改。

    • FadingTextView 当前扩展了AppCompatTextView,但如果您需要它,它可以作为普通的TextView 工作得很好。我认为它也可以用作MaterialTextView,尽管我还没有彻底测试过。

    • 这个例子主要面向相对简单的使用;即,作为一个简单的包装静态标签。虽然我已经尝试解释和测试每个 TextView 设置,但我能想到的可能会影响这一点——例如,复合可绘制对象、填充、可选文本、滚动、文本方向和对齐方式等——我不能保证我什么都想好了。

    【讨论】:

      猜你喜欢
      • 2019-07-06
      • 2017-04-29
      • 1970-01-01
      • 2011-12-16
      • 1970-01-01
      • 2018-05-01
      • 1970-01-01
      • 2016-01-20
      • 1970-01-01
      相关资源
      最近更新 更多