1. 效果展示和思路分析
如效果图,当从page左右滑动时,文字的颜色会根据page移动的距离,移动变色。此时文字是有两种颜色的,使用系统提供的普通的TextView只能设置一种颜色,所以需要自定义一个控件。这个自定义的控件可以继承自View或者TextView,因为我们需要的效果,相当于TextView上添加两种颜色显示,所以使用TextView会更加容易一些(可以少些代码,如TextSize)。
那如何实现两种颜色呢,这就要用到clipRect()
方法,他可以把文字裁剪为需要的大小。这里以一半红色,一半灰色为例,提供一些思路,这里主要以第一种思路来写。
a、画一个canvas布局,裁剪左边一半,然后绘制用于显示。同样方法,画另一半。
b、画一个canvas布局,绘制,然后裁剪左边一半用于显示。同样方法,画另一半。
c、画一个canvas布局,整个绘制,不裁剪。再画另外一个canvas布局,裁剪右边一半,再绘制显示。
···
// 画一个canvas布局,裁剪左边一半,然后绘制用于显示。同样方法,画另一半。
1. 自定义控件ColorTrackTextView继承TextView
2. 重写onDraw方法
3. 设置监听-动画
2. 一个字体两种颜色
2.1 自定义控件
2.1.1 自定义控件属性
<!-- /CustomView/View4/app/src/main/res/values/attrs.xml -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- name最好是自定义控件的名字,originColor为原来字体颜色,changeColor为变化为的颜色 -->
<declare-styleable name="ColorTrackTextView">
<attr name="originColor" format="color"/>
<attr name="changeColor" format="color"/>
</declare-styleable>
</resources>
2.1.2 自定义控件ColorTrackTextView继承TextView
<!-- /CustomView/View4/app/src/main/res/layout/activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.view4.ColorTrackTextView
android:id="@+id/colorText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:text="Hello World!"
app:originColor="@color/gray"
app:changeColor="@color/red"/>
<Button
android:id="@+id/leftToRight"
android:text="@string/left_to_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/rightToLeft"
android:text="@string/right_to_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
// /CustomView/View4/app/src/main/java/com/example/view4/ColorTrackTextView.java
@SuppressLint("AppCompatCustomView")
public class ColorTrackTextView extends TextView {
private Paint mOriginPaint, mChangePaint;
private String mText;
private Float mCurrentProgress = 0.5f;
public ColorTrackTextView(Context context) {
this(context, null);
}
public ColorTrackTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public ColorTrackTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mText = getText().toString();
initPaint(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
// 不使用父类的Draw,我们需要自定义
// 绘制变色
int middle = (int)(mCurrentProgress * getWidth());
drawText(canvas, mChangePaint, 0, middle);
// 绘制不变色
drawText(canvas, mOriginPaint, middle, getWidth());
}
/**
* 按照指定区域裁剪画布,并绘制
* @param canvas 画布
* @param paint 画笔
* @param start 裁剪的开始区域
* @param end 裁剪的结束区域
*/
private void drawText(Canvas canvas, Paint paint, int start , int end) {
canvas.save();
// 裁剪画布显示
Rect rect = new Rect(start,0,end,getHeight());
canvas.clipRect(rect);
// 获取画布x起点
Rect bounds = new Rect();
paint.getTextBounds(mText, 0, mText.length(),bounds);
int x = getWidth()/2 - bounds.width()/2;
// 获取画布基线
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
int baseline = getHeight()/2 + (fontMetricsInt.bottom -fontMetricsInt.top)/2 - fontMetricsInt.bottom;
// 在裁剪后的画布上绘制
canvas.drawText(mText, 0, baseline, paint);
canvas.restore();
}
/**
* 初始化画笔
*/
private void initPaint(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ColorTrackTextView);
int originColor = array.getColor(R.styleable.ColorTrackTextView_originColor, getTextColors().getDefaultColor());
int changeColor = array.getColor(R.styleable.ColorTrackTextView_changeColor, getTextColors().getDefaultColor());
mOriginPaint = getPaintByColor(originColor);
mChangePaint = getPaintByColor(changeColor);
// 回收
array.recycle();
}
/**
* 设置画笔
*/
private Paint getPaintByColor(int color) {
Paint paint = new Paint();
paint.setColor(color); // 设置颜色
paint.setAntiAlias(true); // 设置抗锯齿
paint.setDither(true); // 设置放抖动
paint.setTextSize(getTextSize()); // 设置文字的大小,就是xml设置的大小
return paint;
}
}
3. ColorTrackTextView监听-动画
实现监听-动画,实际上就是动态的给出currentProgress
值,并调用invalidate()
不断地绘制。
// /CustomView/View4/app/src/main/java/com/example/view4/ColorTrackTextView.java
public synchronized void setStep(float currentProgress) {
this.mCurrentProgress = currentProgress;
//不断绘制
invalidate();
}
public synchronized void setAnimatorStep(int currentProgress, int duration) {
// 属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0,currentProgress); // 从0变化到currentProgress
valueAnimator.setDuration(duration); // 设置动画duration/1000秒
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
float currentStep = (float) animation.getAnimatedValue();
setStep(currentStep);
}
});
valueAnimator.start();
}
// /CustomView/View4/app/src/main/java/com/example/view4/MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ColorTrackTextView colorTrackText = (ColorTrackTextView) findViewById(R.id.colorText);
colorTrackText.setAnimatorStep(1, 3000);
}
}
4. 实现不同朝向
设置点击事件,根据点击不同按钮,传送不同的字符,在字体绘制时,根据字符,判断绘制方向。
// /CustomView/View4/app/src/main/java/com/example/view4/MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ColorTrackTextView colorTrackText = (ColorTrackTextView) findViewById(R.id.colorText);
final Button leftToRight = (Button) findViewById(R.id.leftToRight);
leftToRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
colorTrackText.setAnimatorStep(1, 3000, "LEFT_TO_RIGHT");
}
});
final Button rightToLeft = (Button) findViewById(R.id.rightToLeft);
rightToLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
colorTrackText.setAnimatorStep(1, 3000, "RIGHT_TO_LEFT");
}
});
}
// /CustomView/View4/app/src/main/java/com/example/view4/ColorTrackTextView.java
private String mDirection = "";
@Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw: "+mDirection);
// super.onDraw(canvas);
// 不使用父类的Draw,我们需要自定义
int middle = (int) (mCurrentProgress * getWidth());
if (Objects.equals(mDirection, "")) drawText(canvas, mOriginPaint, 0, getWidth()); // 在mDirection没赋值之前,会走一遍
switch (mDirection) {
case "LEFT_TO_RIGHT":
// 绘制变色
drawText(canvas, mChangePaint, 0, middle);
// 绘制不变色
drawText(canvas, mOriginPaint, middle, getWidth());
break;
case "RIGHT_TO_LEFT":
// 绘制变色
drawText(canvas, mChangePaint, getWidth() - middle, getWidth());
// 绘制不变色
drawText(canvas, mOriginPaint, 0, getWidth() - middle);
break;
default:
break;
}
}
public synchronized void setStep(float currentProgress, String direction) {
this.mCurrentProgress = currentProgress;
this.mDirection = direction;
//不断绘制
invalidate();
}
public synchronized void setAnimatorStep(int currentProgress, int duration, String direction) {
// 属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, currentProgress); // 从0变化到currentProgress
valueAnimator.setDuration(duration); // 设置动画duration/1000秒
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
float currentStep = (float) animation.getAnimatedValue();
setStep(currentStep, direction);
}
});
valueAnimator.start();
}
5. 结合ViewPager
5.1 创建ViewPager
新建ViewPageActivity
5.1.1 新建activity_view_pager
里面就是一个LinearLayout和ViewPager。
<!-- /CustomView/View4/app/src/main/res/layout/activity_view_pager.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/indicator_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:orientation="horizontal" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
5.1.2 新建fragment_item
当做上面ViewPager的内容
<!-- /CustomView/View4/app/src/main/res/layout/fragment_item.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="页面详情"/>
</LinearLayout>
5.1.3 新建ItemFragment
继承Fragment,将item填充到ViewPager中。
// /CustomView/View4/app/src/main/java/com/example/view4/ItemFragment.java
public class ItemFragment extends Fragment {
public static ItemFragment newInstance(String item) {
ItemFragment itemFragment = new ItemFragment();
Bundle bundle = new Bundle();
bundle.putString("title", item);
itemFragment.setArguments(bundle);
return itemFragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_item,null);
TextView tv = (TextView) view.findViewById(R.id.text_item);
Bundle bundle = getArguments();
tv.setText(bundle.getString("title"));
return view;
}
}
5.1.4 新建ViewPageActivity
将需要显示的内容动态填充进去,做完这个,就差滑动变色了。
// /CustomView/View4/app/src/main/java/com/example/view4/ViewPageActivity.java
public class ViewPageActivity extends AppCompatActivity {
private String[] items = {
"直播","推荐","视频","图片","段子","精华"};
private LinearLayout mIndicatorContainer; // 变成通用的
private List<ColorTrackTextView> mIndicators;
private ViewPager mViewPager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
mIndicators = new ArrayList<>();
mIndicatorContainer = (LinearLayout)findViewById(R.id.indicator_view);
mViewPager = (ViewPager) findViewById(R.id.view_pager);
initIndicator();
initViewPager();
}
/**
* 初始化可变色的指示器:动态地向一个LinearLayout容器(mIndicatorContainer)中
* 添加多个自定义的TextView(这里称为ColorTrackTextView),并为每个TextView设置一些属性。
*/
private void initIndicator() {
for (int i = 0; i< items.length; i++) {
// 动态添加颜色追踪的TextView
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.weight = 1;
ColorTrackTextView colorTrackTextView = new ColorTrackTextView(this);
// 设置字体内容、大小、颜色、占比
colorTrackTextView.setText(items[i]);
colorTrackTextView.setTextSize(20);
colorTrackTextView.setOriginColor(Color.BLACK);
colorTrackTextView.setChangeColor(Color.RED);
colorTrackTextView.setLayoutParams(params);
// 把新的colorTrackTextView加入LinearLayout容器用于显示
mIndicatorContainer.addView(colorTrackTextView);
// 加入mIndicators集合,为了更方便的操作这些colorTrackTextView
mIndicators.add(colorTrackTextView);
}
}
/**
* 初始化ViewPager
*/
private void initViewPager() {
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@NonNull
@Override
public Fragment getItem(int position) {
return ItemFragment.newInstance(items[position]);
}
@Override
public int getCount() {
return items.length;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
}
});
}
}
// /CustomView/View4/app/src/main/java/com/example/view4/ColorTrackTextView.java
public class ColorTrackTextView extends TextView {
...
public void setOriginColor(int originColor) {
this.mOriginPaint.setColor(originColor);
}
public void setChangeColor(int changeColor) {
this.mChangePaint.setColor(changeColor);
}
}
5.1.5 更改AndroidManifest
将MainActivity改为ViewPageActivity
<!-- /CustomView/View4/app/src/main/AndroidManifest.xml -->
<activity
android:name=".ViewPageActivity"
运行看看效果:
5.2 添加滑动变色效果
// /CustomView/View4/app/src/main/java/com/example/view4/ViewPageActivity.java
private void initViewPager() {
...
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// position 代表当前的位置,positionOffset 代表滚动的 0-1 百分比
try {
ColorTrackTextView left = mIndicators.get(position);
left.setStep(1 - positionOffset, "RIGHT_TO_LEFT");
ColorTrackTextView right = mIndicators.get(position + 1);
right.setStep(positionOffset, "LEFT_TO_RIGHT");
} catch (Exception e) {
// 滑到精华的时候会角标溢出导致闪退
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
// /CustomView/View4/app/src/main/java/com/example/view4/ColorTrackTextView.java
private void drawText(Canvas canvas, Paint paint, int start, int end) {
mText = getText().toString(); // ViewPageActivity传过来的text在构造函数的时候还没拿到,需要在这里再赋值一遍
...
6. 任务布置
- 打造炫酷的进度条
- 58同城数据加载效果(一秒切换一次)