ShimmerTextView

本文來自網易雲社區android

做者:孫有軍git


產品中有一個需求,要求TextView的文字有一個高亮的效果,高亮的同時有跑馬燈效果!github

原本想在網上找一個現成的用用,好比Facebook出的Shimmer,還有不少,可是都感受代碼太多,所以擼了一個簡單版的,talk is cheap,show me you code。canvas


實現

咱們知道TextView的文字的顏色是由Paint根據Color控制的,咱們能夠設置Paint的Shader來實現該效果,這樣在TextView繪製的時候Paint會從對應的Shader獲取color來實現繪製。既然TextView要高亮,說明文字顏色不一致,這裏咱們能夠設置一線性漸變shader,這樣就能夠設置不一樣部分的文字不一樣顏色。至於跑馬燈那就一直水平改變Shader就能夠實現。app


屬性定義

雖然是簡單的,仍是要通用,好比顏色能夠自定義,方向能夠設置,是否自動開始,這裏咱們先定義幾個屬性:ide

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

這裏咱們分別控制了是否自動開始,開始顏色,結束顏色,方向  post


代碼實現

這裏咱們實現一個繼承自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來水平移動線性漸變,同時從新繪製內容。this

ValueAnimator 咱們設置了一直重複動畫,ValueAnimator就是一個數值發生器,其實能夠用Handler與post來實現相同的效果。只要按必定速率改變線性漸變就能夠code

  • 須要注意的是當距離大於整個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+款雲產品! 

更多網易研發、產品、運營經驗分享請訪問網易雲社區


相關文章:
【推薦】 網易雲易盾朱浩齊:視聽行業步入強監管和智能時代

本站公眾號
   歡迎關注本站公眾號,獲取更多信息