项目需求:带有动画的选择按钮
先上图:
1.分析结构:只有左右两个选择条目,使用LinearLayout
public class SelectTabView extends LinearLayout{
//这里使用xml,所以构造方法使用第二个即可
public SelectTabView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
this.context = context;
setWillNotDraw(false);//需要调用这个方法来清除flag,使之可以重写onDraw();
setClipChildren(false);
setClipToPadding(false);
addTab();
}
}
注:clipToPadding就是说控件的绘制区域是否在padding里面的,true的情况下如果你设置了padding那么绘制的区域就往里 缩,clipChildren是指子控件是否超过padding区域,这两个属性默认是true的。
private void addTab() {
//这里是得到两个tab,当然你也可以用new TextView的方法,我这里方便省事
View leftView = LayoutInflater.from(context).inflate(R.layout.layout_tab_selector, null);
View rightView = LayoutInflater.from(context).inflate(R.layout.layout_tab_selector, null);
TextView leftText = (TextView) leftView.findViewById(R.id.tv_tab_title);
TextView rightText = (TextView) rightView.findViewById(R.id.tv_tab_title);
leftView.setTag(0);
rightView.setTag(1);
leftText.setTextColor(selectorTabColor);
rightText.setTextColor(noSelectorTabColor);
leftText.setText(leftTab);
rightText.setText(rightTab);
setOrientation(HORIZONTAL);
LayoutParams layoutParams = new LayoutParams(0, FrameLayout.LayoutParams.MATCH_PARENT, 1.0f);
addView(leftView, 0, layoutParams);
addView(rightView, 1, layoutParams);
leftView.setOnClickListener(this);
rightView.setOnClickListener(this);
views.add(leftView);
views.add(rightView);
}
2.初始化完成后,执行onDraw()的方法;
//绘制矩形,圆角矩形等图形的时候,使用GradientDrawable比较方便;
private GradientDrawable mIndicatorDrawable = new GradientDrawable();//绘制Indicator
private GradientDrawable mRectDrawable = new GradientDrawable();//绘制按钮外形
private Rect mIndicatorRect = new Rect();//Indicator的显示区域
private int currentItem = 0;//默认初始化左边
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
height = getHeight();//得到控件的高度
width = getWidth();
int paddingLeft = getPaddingLeft();
//draw rect
mRectDrawable.setColor(ContextCompat.getColor(context, android.R.color.white));
mRectDrawable.setStroke(strokeWidth, strokeColor);
mRectDrawable.setCornerRadius(getHeight() / 2);
//设置外形的范围
mRectDrawable.setBounds(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
mRectDrawable.draw(canvas);
if (mIsFirstDraw) {//判断是否是第一次加载(如果不执行动画,不需要判断)
mIsFirstDraw = false;
calcIndicatorRect();
}
//draw indicator
mIndicatorDrawable.setColor(indicatorColor);
//这里面的2是线宽
mIndicatorDrawable.setBounds(paddingLeft + 2 + mIndicatorRect.left,
getPaddingTop() + 2, (int) (paddingLeft + mIndicatorRect.right - 2),
(getHeight() - getPaddingBottom() - 2));
mIndicatorDrawable.setCornerRadii(mRadiusArr);
mIndicatorDrawable.draw(canvas);
}
private void calcIndicatorRect() {
//计算indicator的大小
View currentTabView = getChildAt(currentItem);
float left = currentTabView.getLeft();
float right = currentTabView.getRight();
mIndicatorRect.left = (int) left;
mIndicatorRect.right = (int) right;
int radio = height / 2;
//使用GradientDrawable ,绘制圆角矩形的时候,需要设置上下左右四个角的大小
mRadiusArr[0] = radio;
mRadiusArr[1] = radio;
mRadiusArr[2] = radio;
mRadiusArr[3] = radio;
mRadiusArr[4] = radio;
mRadiusArr[5] = radio;
mRadiusArr[6] = radio;
mRadiusArr[7] = radio;
}
以上的代码,显示了一个静态的画面,下面需要显示点击事件了
@Override
public void onClick(View view) {
//currentItem = position;这个是没有动画,直接交换位置
//如果不需要动画的时,直接设置显示区域,绘制右边部分即可,这里不做介绍了,下面是点击动画显示
int position = (Integer) view.getTag();
//防止重复点击相同位置
if (mCurrentTab != position) {
setCurrentTab(position);
}
//选择监听事件
if (onCheckedChangeListener != null)
switch (((Integer) view.getTag())) {
case 0:
onCheckedChangeListener.onCheckedChanged(leftTab, true);
break;
case 1:
onCheckedChangeListener.onCheckedChanged(rightTab, false);
break;
}
}
下面的是更换显示区域:
private void setCurrentTab(int position) {
//因为是两个,交换下一次的position
int mLastTab = mCurrentTab;
mCurrentTab = position;
//所点击的View,记录下当前的位置信息(即动画的到达位置)
View clickView = getChildAt(position);
initTextViewColor(position);//根据position,变换字体的颜色
mCurrentP.left = clickView .getLeft();
mCurrentP.right = clickView .getRight();
//上一次的显示的View,记录位置信息(即动画的开始位置)
View lastTabView = getChildAt(mLastTab);
mLastP.left = lastTab.getLeft();
mLastP.right = lastTab.getRight();
//点击的时候执行动画
mValueAnimator.setObjectValues(mLastP, mCurrentP);
mValueAnimator.setDuration(260);
mValueAnimator.start();
}
上文中的mCurrentP和mLastP的来源:
class IndicatorPoint {
public float left;
public float right;
}
private IndicatorPoint mCurrentP = new IndicatorPoint();
private IndicatorPoint mLastP = new IndicatorPoint();
注:为什么要创建这个类呢?原因:动画执行时,没有具体的对象,即所画的Indicator不是对象
下面开始执行动画了:
1.创建动画被执行的对象
在这里需要使用到动画的 TypeEvaluator 估值器这个类,目的:动画过程的平滑过渡。
如果想要的动画类型是Android系统所未知的,那么通过实现TypeEvaluator接口就能够创建自己的估值器。
(Android系统已知的类型是int、float或颜色(color),分别有IntEvaluator、FloatEvaluator和ArgbEvaluator类型的评价器所支持)
/**
* fraction 从起始值到结束值的分数,即为根据时间变化的浮点数,从0到1
*/
class PointEvaluator implements TypeEvaluator<IndicatorPoint> {
@Override
public IndicatorPoint evaluate(float fraction, IndicatorPoint startValue, IndicatorPoint endValue) {
float left = startValue.left + fraction * (endValue.left - startValue.left);
float right = startValue.right + fraction * (endValue.right - startValue.right);
IndicatorPoint point = new IndicatorPoint();
point.left = left;
point.right = right;
return point;
}
}
注:当ValueAnimator对 象(或ObjectAnimator对象)运行时,它会计算当前的动画播放比例(一个0到1之间的值),然后根据你所使用的插值类型来计算一个要插入的动 画的版本。插值比例是由TypeEvaluator对象通过fraction参数接收来的,因此在计算动画值的时候,不需要考虑插值。
2.创建动画
(这一步在构造函数中新建最好)
ValueAnimator mValueAnimator = ValueAnimator.ofObject(new PointEvaluator(), mLastP, mCurrentP);
mValueAnimator.addUpdateListener(this);
重写抽象方法:
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
IndicatorPoint p = (IndicatorPoint) valueAnimator.getAnimatedValue();
mIndicatorRect.left = (int) p.left;
mIndicatorRect.right = (int) p.right;
invalidate();
}
完成
以上就是带有动画的选择按钮(如果有不正确的地方,帮忙指正)
最后参考源:FlycoTabLayout