ShimmerTextView
本文来自网易云社区
作者:孙有军
产品中有一个需求,要求TextView的文字有一个高亮的效果,高亮的同时有跑马灯效果!
本来想在网上找一个现成的用用,比如Facebook出的Shimmer,还有很多,但是都感觉代码太多,因此撸了一个简单版的,talk is cheap,show me you code。
实现
我们知道TextView的文字的颜色是由Paint根据Color控制的,我们可以设置Paint的Shader来实现该效果,这样在TextView绘制的时候Paint会从对应的Shader获取color来实现绘制。既然TextView要高亮,说明文字颜色不一致,这里我们可以设置一线性渐变shader,这样就可以设置不同部分的文字不同颜色。至于跑马灯那就一直水平改变Shader就可以实现。
属性定义
虽然是简单的,还是要通用,比如颜色可以自定义,方向可以设置,是否自动开始,这里我们先定义几个属性:
<declare-styleable name="ShimmerTextView"> <attr name="auto_start" format="boolean"></attr> <attr name="start_color" format="reference|color"></attr> <attr name="end_color" format="reference|color"></attr> <attr name="direction" format="boolean"></attr></declare-styleable>
这里我们分别控制了是否自动开始,开始颜色,结束颜色,方向
代码实现
这里我们实现一个继承自AppCompatTextView的自定义TextView。
public class ShimmerTextView extends android.support.v7.widget.AppCompatTextView { public static final int OFFSET_ONE_TIME = 15; private Paint paint; private LinearGradient gradient; private Matrix matrix; private int w, h; private boolean horizontal; private boolean autoStart; private int startColor; private int endColor; private static final int DEFAULT_START_COLOR = 0xFFFF50ED; private static final int DEFAULT_END_COLOR = 0xFF3455FF; private float offset = 0; private ValueAnimator animator; public ShimmerTextView(Context context) { super(context); init(context, null); } public ShimmerTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context, attrs); } public ShimmerTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { paint = getPaint(); matrix = new Matrix(); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ShimmerTextView); autoStart = array.getBoolean(R.styleable.ShimmerTextView_auto_start, false); startColor = array.getColor(R.styleable.ShimmerTextView_start_color, DEFAULT_START_COLOR); endColor = array.getColor(R.styleable.ShimmerTextView_end_color, DEFAULT_END_COLOR); horizontal = array.getBoolean(R.styleable.ShimmerTextView_direction, true); array.recycle(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); this.w = w; this.h = h; setGradient(); } private void setGradient() { if (horizontal) { gradient = new LinearGradient(0, 0, w, 0, new int[]{startColor, endColor, startColor}, new float[]{0, 0.5f, 1.0f}, Shader.TileMode.CLAMP); } else { gradient = new LinearGradient(0, 0, 0, h, new int[]{startColor, endColor, startColor}, new float[]{0, 0.5f, 1.0f}, Shader.TileMode.CLAMP); } paint.setShader(gradient); invalidate(); if (autoStart) { play(); } } public void play() { ValueAnimator animator = getAnimator(); if (animator.isRunning()) { return; } animator.start(); } @NonNull private ValueAnimator getAnimator() { if (animator == null) { animator = ValueAnimator.ofFloat(0.0f, 1.0f); animator.setDuration(500); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setRepeatMode(ValueAnimator.RESTART); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { offset += OFFSET_ONE_TIME; if (horizontal) { if (offset > w) { offset = -w; } } else { if (offset > h) { offset -= h; } } invalidate(); } }); } return animator; } public void stop() { if (animator != null) { animator.cancel(); } } public void reset() { if (animator != null) { animator.cancel(); animator = null; } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); reset(); } @Override protected void onDraw(Canvas canvas) { matrix.setTranslate(offset, 0); gradient.setLocalMatrix(matrix); super.onDraw(canvas); } }
上面的代码主要先解析了自定义属性,之后设置了一个线性渐变来改变文字的颜色,之后采用了ValueAnimator来水平移动线性渐变,同时重新绘制内容。
ValueAnimator 我们设置了一直重复动画,ValueAnimator就是一个数值发生器,其实可以用Handler与post来实现相同的效果。只要按一定速率改变线性渐变就可以
需要注意的是当距离大于整个View的宽度或者高度时,需要重新开始
效果
最后我们在界面中使用该控件:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.demo.example.activity.ShimmerViewActivity"> <com.demo.example.widget.ShimmerTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginBottom="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" android:gravity="center" android:text="我爱北京天安门" android:textSize="25sp" app:auto_start="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="0.502" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.148"/> </android.support.constraint.ConstraintLayout>
在Activity中设置该控件,运行效果。
网易云免费体验馆,0成本体验20+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区
相关文章:
【推荐】 abtest-system后台系统设计与搭建
【推荐】 责任链模式的使用-Netty ChannelPipeline和Mina IoFilterChain分析
【推荐】 4月第3周业务风控关注 | 文化部再次审查直播和游戏产品,已下架4939款直播应用