今天给大家分享的是今日头条顶部文字变色效果的实现,首先我们来看一下,实际中和我们最终要做出来的效果是什么样的。
效果呢就是像上面这样的,那我们怎么样来实现呢?
既然是文字,为了省却麻烦,我们就直接写我们自己的自定义View去继承自系统的TextView,这样也会省去很多的代码。
我们先为其定义两个属性,普通颜色和变色的颜色。
定义我们的属性文件
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ChangeTextColor">
<!--普通颜色-->
<attr name="normalColor" format="color"/>
<!--变化的颜色-->
<attr name="changeColor" format="color"/>
</declare-styleable>
</resources>
主要代码:
// 普通字体的画笔
private Paint mNormalPaint;
// 变色字体的画笔
private Paint mChangePaint;
// 当前的进度
private float mCurrentProgress = 0.0f;
private Direction mDirection = FROME_LEFT_TO_RIGHT;
//方向
public enum Direction {
FROME_LEFT_TO_RIGHT, FROME_RIGHT_TO_LEFT
}
public ChangeTextColor(Context context) {
this(context, null);
}
public ChangeTextColor(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ChangeTextColor(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ChangeTextColor);
int mNormalColor = array.getColor(R.styleable.ChangeTextColor_normalColor, getTextColors().getDefaultColor());
int mChangeColor = array.getColor(R.styleable.ChangeTextColor_changeColor, getTextColors().getDefaultColor());
array.recycle();
//设置普通颜色画笔
mNormalPaint = new Paint();
mNormalPaint.setAntiAlias(true);//抗锯齿
mNormalPaint.setColor(mNormalColor);
mNormalPaint.setDither(true);// 防抖动
mNormalPaint.setTextSize(getTextSize());
//设置变化颜色画笔
mChangePaint = new Paint();
mChangePaint.setAntiAlias(true);//抗锯齿
mChangePaint.setColor(mChangeColor);
mChangePaint.setDither(true);// 防抖动
mChangePaint.setTextSize(getTextSize());
}
@Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);不要用系统的 我们自己画
//定一个中间值去画
int midleValue = (int) (mCurrentProgress * getWidth());
if (mDirection == FROME_LEFT_TO_RIGHT) {
drawText(canvas, mChangePaint, 0, midleValue);
drawText(canvas, mNormalPaint, midleValue, getWidth());
} else {
drawText(canvas, mChangePaint, getWidth() - midleValue, getWidth());
drawText(canvas, mNormalPaint, 0, getWidth() - midleValue);
}
}
/**
* 绘制text
*
* @param canvas
* @param paint
* @param start
* @param end
*/
private void drawText(Canvas canvas, Paint paint, int start, int end) {
canvas.save();
Rect react = new Rect(start, 0, end, getHeight());
canvas.clipRect(react);
String text = getText().toString();
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
//从什么地方开始画文字
int x = getWidth() / 2 - bounds.width() / 2;
//获得基线位置
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(text, x, baseLine, paint);
canvas.restore();
}
/**
* 设置方向
*
* @param direction
*/
public void setDirection(Direction direction) {
this.mDirection = direction;
}
/**
* 设置进度
*
* @param currentProgress
*/
public void setCurrentProgress(float currentProgress) {
this.mCurrentProgress = currentProgress;
invalidate();
}
public void setChangeColor(int changeColor) {
this.mChangePaint.setColor(changeColor);
}
public void setNormaleColor(int normalColor) {
this.mNormalPaint.setColor(normalColor);
}
Rect react = new Rect(start, 0, end, getHeight());
canvas.clipRect(react);
在这里我给大家说一下这个属性,就是我们画出的文字,是通过这个来裁剪的,这也是我们实现该效果的很重要的一步,在这里还比较绕
我们在正常情况下在屏幕上画出文字是完整的,那么使用canvas.clipRect()方法是怎样的呢,首先我们需要一个裁剪区域React
下面给大家展示一下我们位置设置的不同会导致什么样的结果,通过图来说也许会更明白
Rect react = new Rect(left, top, right, bottom);
Rect react = new Rect(0, 0, 10, getHeight());//right为10
Rect react = new Rect(0, 0, 50, getHeight());//right为50
Rect react = new Rect(0, 0, 90, getHeight());//right为90
Rect react = new Rect(0, 0, 130, getHeight());//right为130
看到这里我们的思路就是这样的,用两只画笔去话我们的文字,分别去裁剪我们的文字,左边裁一定的比例,右边裁一定的比例,这样正好就拼出整个完整的文字,然后我们只要不断地去改变我们裁剪区域的比例,那么就可以实现我们的文字变色,在代码中已经写得比较详细了,这里就不赘述了。
使用:
public class HomePageActivity extends AppCompatActivity {
private String[] items = {"关注","推荐", "历史", "美食", "热点", "视频", "社会"};
private List<ChangeTextColor> mIndicators;
private ViewPager mViewPager;
private LinearLayout linearLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_page);
linearLayout = (LinearLayout) findViewById(R.id.ll_top);
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mIndicators = new ArrayList<>();
initIndicator();
initViewPager();
}
private void initViewPager() {
//设置adapter
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return FragmentContent.newInstance(items[position]);
}
@Override
public int getCount() {
return items.length;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
});
mViewPager.setCurrentItem(1);
//设置viewpager的监听
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
ChangeTextColor leftDirection = mIndicators.get(position);
leftDirection.setDirection(ChangeTextColor.Direction.FROME_RIGHT_TO_LEFT);
leftDirection.setCurrentProgress(1-positionOffset);
try {
ChangeTextColor rightDirection = mIndicators.get(position+1);
rightDirection.setDirection(ChangeTextColor.Direction.FROME_LEFT_TO_RIGHT);
rightDirection.setCurrentProgress(positionOffset);
}catch (Exception e){
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void initIndicator() {
for (int i = 0; i < items.length; i++) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.weight = 1;
ChangeTextColor changeTextColor = new ChangeTextColor(this);
// 设置颜色
changeTextColor.setTextSize(25);
changeTextColor.setChangeColor(Color.RED);
changeTextColor.setText(items[i]);
changeTextColor.setLayoutParams(params);
// 把新的加入LinearLayout容器
linearLayout.addView(changeTextColor);
// 加入集合
mIndicators.add(changeTextColor);
}
}
}
布局文件就很简单了,viewpager+linearlayout
Fragment就是一些内容显示了,代码可以自己down,欢迎朋友们提出意见。
github地址:点击打开链接