本文来自网易云社区

作者:孙有军

产品中有一个需求,要求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款直播应用

ShimmerTextView的更多相关文章

  1. 更强的微光闪烁效果--第三方开源--Shimmer-android

    Shimmer-android在github上的项目主页是:https://github.com/RomainPiel/Shimmer-android Shimmer-android干脆在Androi ...

  2. Android Tools 开发工具库开源项目总结

    在Android开发中,我们不免会遇到使用一些工具库来简化我们的工具代码的编写,以下是本人之前star的开源项目,供大家参考: 一.android_testsuite 项目地址:https://git ...

  3. 模拟QQ分组(具有伸缩功能) (添加开源框架的光闪烁效果)SimpleExpandableListAdapter 适配器的用法,并且可添加组及其组内数据。

    package com.lixu.qqfenzu; import java.util.ArrayList; import java.util.HashMap; import java.util.Lis ...

  4. 认识一下Kotlin语言,Android平台的Swift

    今天在CSDN首页偶然看到一个贴子JetBrains正式公布Kotlin 1.0:JVM和Android上更好用的语言 看完后,感觉Kotlin语法非常简洁,有一系列动态语言的特点,Lambda表达式 ...

  5. LinearGradient线型渐变效果

    public LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, TileM ...

随机推荐

  1. Django 创建模型 激活模型 简单的使用模型

    创建模型: 在你的开发环境中,已经有一个“项目” —— 已经建立起来,你将开始在上面做一些东西. 你编写的每个Django应用都是一个遵循特定约定的Python包. Django自带一个工具,它可以自 ...

  2. SOJ3266 Birthday

    Time Limit: 1000MS Memory Limit: 65536 K Description Today is Windy's birthday. What can I say? Inpu ...

  3. 练习:HPH 数据

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. 使用 PHP Curl 做数据中转

    流程 收集头部信息 收集请求数据 转换头部信息为 CURL 头部请求格式 使用 Curl 进行转发 收集 HTTP 头信息 function getAllHeaders() { $headers = ...

  5. 2.spring:集合属性

    1.list  配置java.util.List类型的属性,需要指定<list>标签,在标签里面包含有一些元素,这些标签 可以通过<value>指定简单的常量值,通过<r ...

  6. javascript中数组的22种方法 (转载)

    前面的话 数组总共有22种方法,本文将其分为对象继承方法.数组转换方法.栈和队列方法.数组排序方法.数组拼接方法.创建子数组方法.数组删改方法.数组位置方法.数组归并方法和数组迭代方法共10类来进行详 ...

  7. PAT——1009. 说反话

    给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出. 输入格式:测试输入包含一个测试用例,在一行内给出总长度不超过80的字符串.字符串由若干单词和若干空格组成,其中单词是由英文字母(大小写有区 ...

  8. Jquery Mobile通过超链接跳转后CSS样式不起作用的解决办法

    Jquery Mobile中的超链接默认是采用AJAX跳转的,ajax获取到页面的内容之后,就直接替换当前页面的内容了,它只是单纯的获取页面的HTML代码,并不会再去下载引用的CSS代码和JS代码,因 ...

  9. (一 、上)搭建简单的SpringBoot + java + maven + mysql + Mybatis+通用Mapper 《附项目源码》

    最近公司一直使用 springBoot 作为后端项目框架, 也负责搭建了几个新项目的后端框架.在使用了一段时间springBoot 后,感觉写代码 比spring 更加简洁了(是非常简洁),整合工具也 ...

  10. 使用WIn10自带的Linux子系统

    最近一直有安装虚拟机的想法,今天刚刚知道win10有自带的Linux子系统,就准备试一下: 首先要保证自己的电脑处于开发者选项: 然后就要在控制面板的程序和功能页面点击“启用或者关闭WIndows功能 ...