支持仿歌词滚动的TextView

本文介绍了一个自定义TextView组件,用于实现一行文字从一种颜色渐变为另一种颜色的效果,并且通过不断更新渐变位置来达到文字滚动的视觉效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前篇说的是一个背景移动的效果,

(链接:http://blog.youkuaiyun.com/onionomelette/article/details/70287266)

本文说的是实现一行歌词滚动的效果,


要实现的效果类似下图(字体效果除外)


可以看到歌词的红色部分是从左慢慢往右填充的.

实现效果如图:


可以看到"热门项目","热门商品"  两个控件的一些颜色是会从一边移动到另一边,这个效果是通过两个控件同时播放做成的,其实单独分开来看的话,跟上面的歌词滚动效果很类似.

实现方式

因为需要用到TextView的一些功能,所以只能继承TextView

两个textView同时滚动,左边的蓝色被黑色慢慢覆盖,同时右边黑色被蓝色慢慢覆盖,往回的时候相反,同时下面两个listview执行相应的动画,与本文无关这里不说明.

TextView的滚动效果代码(最后附上完整代码):

1.继承TextView,实现构造方法,没啥好说的:

public class ScrollColorTextView extends AppCompatTextView {
2.滚动效果的实现

可以看到在滚动的时候,有若干部分是蓝色的,剩余部分是黑色的,这里用渐变实现;

先看看使用渐变的摘抄资料(LinearGradient):

LinearGradient有两个构造函数;

public LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions,Shader.TileMode tile) 

参数:

float x0: 渐变起始点x坐标

float y0:渐变起始点y坐标

float x1:渐变结束点x坐标

float y1:渐变结束点y坐标

int[] colors:颜色 的int 数组

float[] positions: 相对位置的颜色数组,可为null,  若为null,可为null,颜色沿渐变线均匀分布

Shader.TileMode tile: 渲染器平铺模式


public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,Shader.TileMode tile)

float x0: 渐变起始点x坐标

float y0:渐变起始点y坐标

float x1:渐变结束点x坐标

float y1:渐变结束点y坐标

int color0 : 起始渐变色

int color1: 结束渐变色

Shader.TileMode tile: 渲染器平铺模式


用渐变实现的话,会出现一个很明显的渐变效果,可是上图中并没有出现明显的渐变.这是因为我把渐变效果弱化了,所以看不太出来.

如何弱化?

经测试,用第二种构造方法肯定是不行的,所以只能用第一种方法,可以看到有一个参数:int[] colors,这是将一组颜色数组扔到渐变器去,然后让渐变去渲染数组里面的颜色,这里我定义一个比较大的数组,然后里面只放两种颜色,渲染的时候,因为数组比较大,颜色又是只有一个地方会有突然变化,利用这个特性将渐变效果弱化了.

for (int i = 0; i < COLOR_LIST_SIZE; i++) {
    if (i < percent * COLOR_LIST_SIZE) {
        colorList[i] = startColor;
    } else {
        colorList[i] = endColor;
    }
}
percent 是当前的播放百分比,由刚开始播放与当前时间计算得到 
有了将渐变效果弱化的功能,所以实现一行文字一部分是一种颜色,另一部分是另一种颜色就很简单了.动态的效果就是不断的将百分比扩大就行了.
所以渐变效果弱化是很关键的,当然通过自定义View的实现也是可以的,只不过要实现我需要的TextView的一些功能代码量会很多;
下面贴上完整代码:
Java代码

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;

import com.orhanobut.logger.Logger;

import static com.hoyar.beautyshop.view.ScrollColorTextView.TextColorState.LEFT_IN;
import static com.hoyar.beautyshop.view.ScrollColorTextView.TextColorState.RIGHT_IN;

/**
 * Created by Administrator on 2017/4/21.
 */

public class ScrollColorTextView extends AppCompatTextView {

    public ScrollColorTextView(Context context) {
        super(context);
    }

    public ScrollColorTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollColorTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        startColor = getPaint().getColor();
    }

    private static final int COLOR_LIST_SIZE = 100;
    private final int[] colorList = new int[COLOR_LIST_SIZE];

    @Override
    protected void onDraw(Canvas canvas) {
        if (state == TextColorState.STATIC) {
            getPaint().setColor(endColor);
        } else {
            float percent = (System.currentTimeMillis() - startAnimationTime) * 1.0f / animationTime;//计算百分比
            if (state == RIGHT_IN) {//从右边进来的时候,翻转百分比,使其变成右边进来的效果
                percent = 1.0f - percent;
            }
            setColorSet(percent);
            @SuppressLint("DrawAllocation")
            LinearGradient linearGradient = new LinearGradient(0, getMeasuredHeight()/2, getMeasuredWidth(), getMeasuredHeight()/2, colorList, null, Shader.TileMode.CLAMP);//最后一个参数为边缘拉伸
            getPaint().setShader(linearGradient);
            if (percent < 1.0f) {
//                postInvalidateDelayed(30);
                invalidate();
            } else {
                state = TextColorState.STATIC;
            }

        }
        super.onDraw(canvas);
        Logger.d("绘制了一次");
    }

    /**
     * 设置渐变集合
     *
     * @param percent
     */
    private void setColorSet(float percent) {
        for (int i = 0; i < COLOR_LIST_SIZE; i++) {
            if (i < percent * COLOR_LIST_SIZE) {
                colorList[i] = startColor;
            } else {
                colorList[i] = endColor;
            }
        }

    }

    /**
     * 动画时间
     */
    private int animationTime = 3000;
    /**
     * 动画的开始时间
     */
    private long startAnimationTime = System.currentTimeMillis();
    /**
     * 起始文本颜色
     */
    private int startColor;
    /**
     * 结束的时候的文本颜色
     */
    private int endColor;

    private TextColorState state = TextColorState.STATIC;

    public void startScroll(TextColorState state, int animationTime, int startColor, int endColor) {
        this.state = state;
        this.animationTime = animationTime;
        if (state == LEFT_IN) {
            this.startColor = endColor;
            this.endColor = startColor;
        } else {
            this.startColor = startColor;
            this.endColor = endColor;
        }
        startAnimationTime = System.currentTimeMillis();
        invalidate();
    }

    public enum TextColorState {
        LEFT_IN, RIGHT_IN, STATIC
    }

}

xml用到控件部分代码:
<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true">

    <com.hoyar.beautyshop.view.ScrollColorTextView
        android:id="@+id/fragment_home_tv_hot_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="热门项目"
        android:textColor="@color/home_text_color_select"
        android:textSize="15sp" />

    <com.hoyar.beautyshop.view.ScrollColorTextView
        android:id="@+id/fragment_home_tv_hot_wares"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="25dp"
        android:text="热门商品"
        android:textColor="@color/home_text_color_normal"
        android:textSize="15sp" />
</LinearLayout>

外部调用的Java代码,有一些无关的东西可以忽略,比如listview的切换效果:
/**
 * 热门项目控件
 */
@BindView(R.id.fragment_home_tv_hot_item)
ScrollColorTextView hotItem;
/**
 * 热门商品控件
 */
@BindView(R.id.fragment_home_tv_hot_wares)
ScrollColorTextView hotWares;
/***
 * 热门项目listView
 */
@BindView(R.id.fragment_home_lv_hot_item)
ListView hotItemListView;
/**
 * 热门商品ListView
 */
@BindView(R.id.fragment_home_lv_hot_ware)
ListView hotWareListView;

private final RateFilter filter = new RateFilter(ANIMATION_TIME);

//点击热门项目
@OnClick(R.id.fragment_home_tv_hot_item)
void clickHotItem() {
    //1.过滤与当前相同的状态,避免可以重复点击 2.过滤在执行动画时的点击事件
    if (hotSelectState == HOT_ITEM || !filter.notFilter()) return;
    hotSelectState = HOT_ITEM;
    //设置动画效果
    int colorNormal = getResources().getColor(R.color.home_text_color_normal);
    int colorSelect = getResources().getColor(R.color.home_text_color_select);
    hotItem.startScroll(ScrollColorTextView.TextColorState.RIGHT_IN, ANIMATION_TIME, colorNormal, colorSelect);
    hotWares.startScroll(ScrollColorTextView.TextColorState.RIGHT_IN, ANIMATION_TIME, colorSelect, colorNormal);

    //左边进入的平移动画
    Animation leftInTranslate = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0);
    leftInTranslate.setDuration(ANIMATION_TIME);
    Animation rightOutTranslate = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0);
    rightOutTranslate.setDuration(ANIMATION_TIME);
    hotItemListView.startAnimation(leftInTranslate);
    hotWareListView.startAnimation(rightOutTranslate);

    hotItemListView.setVisibility(View.VISIBLE);
    hotWareListView.setVisibility(View.VISIBLE);

    //在此处延迟设置为不可交互,不在动画监听里实现是因为动画监听代码太长
    hotWareListView.postDelayed(new Runnable() {
        @Override
        public void run() {
            hotWareListView.setVisibility(View.GONE);
        }
    }, ANIMATION_TIME);
}

//状态变量
private HotSelectState hotSelectState = HOT_ITEM;
//动画时间
private static final int ANIMATION_TIME = 500;

//点击热门商品
@OnClick(R.id.fragment_home_tv_hot_wares)
void clickHotWares() {
    //1.过滤与当前相同的状态,避免可以重复点击 2.过滤在执行动画时的点击事件
    if (hotSelectState == HOT_WARES || !filter.notFilter()) return;
    hotSelectState = HOT_WARES;
    //设置动画效果
    int colorNormal = getResources().getColor(R.color.home_text_color_normal);
    int colorSelect = getResources().getColor(R.color.home_text_color_select);
    hotItem.startScroll(ScrollColorTextView.TextColorState.LEFT_IN, ANIMATION_TIME, colorSelect, colorNormal);
    hotWares.startScroll(ScrollColorTextView.TextColorState.LEFT_IN, ANIMATION_TIME, colorNormal, colorSelect);

    Animation leftOutTranslate = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -1, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0);
    leftOutTranslate.setDuration(ANIMATION_TIME);
    Animation rightInTranslate = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0);
    rightInTranslate.setDuration(ANIMATION_TIME);
    hotItemListView.startAnimation(leftOutTranslate);
    hotWareListView.startAnimation(rightInTranslate);

    hotItemListView.setVisibility(View.VISIBLE);
    hotWareListView.setVisibility(View.VISIBLE);

    //在此处延迟设置为不可交互,不在动画监听里实现是因为动画监听代码太长
    hotItemListView.postDelayed(new Runnable() {
        @Override
        public void run() {
            hotItemListView.setVisibility(View.GONE);
        }
    }, ANIMATION_TIME);
}

/**
 * 当前选择的热门状态枚举类
 */
enum HotSelectState {
    /**
     * 选择了热门项目
     */
    HOT_ITEM,
    /**
     * 选择了热门商品
     */
    HOT_WARES
}

关键代码基本就是这些,上方需要用到的颜色自己定义
(完)
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值