前篇说的是一个背景移动的效果,
(链接: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 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 }
关键代码基本就是这些,上方需要用到的颜色自己定义
(完)