最近按照需求做了一个花里胡哨的首页底部菜单切换,我不会做GIF,传两张图看一下效果
点击不同的位置,底部Bar的凹陷位置会移动,同事选中的图标会上升,非选中的图标会下降,同时,图标的背景圆圈会做东升西落的动画效果。
实现原理:这个效果其实可以分解为三个动画,1、底部凹陷位置的移动。2、图标的上升与下降。3、背景圆圈的东升西落
图标上升下降的代码和背景运动的代码如下
private void iconUpAnim(ImageView ivIcon) {
initLocation(ivIcon);
llyCircleAnim.addView(waterDropNewItem.getImageView());//把动画图片添加到动画
//抛物线动画,原理:两个位移动画,一个横向匀速移动,一个纵向变速移动,两个动画同时执行,就有了抛物线的效果。
ObjectAnimator translateAnimationX = ObjectAnimator.ofFloat(waterDropNewItem.getImageView(), "translationX", 0, waterDropNewItem.getTranslationUpX());
translateAnimationX.setInterpolator(new AccelerateInterpolator());
ObjectAnimator translateAnimationY = ObjectAnimator.ofFloat(waterDropNewItem.getImageView(), "translationY", 0, waterDropNewItem.getTranslationUpY());
translateAnimationY.setInterpolator(new LinearInterpolator());
translateAnimationY.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
waterDropOldItem = new WaterDropItem();
waterDropOldItem.copy(waterDropNewItem);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
ObjectAnimator translationX = new ObjectAnimator().ofFloat(ivIcon, "translationX", 0, 0);
ObjectAnimator translationY = new ObjectAnimator().ofFloat(ivIcon, "translationY", 0, -waterDropAnimUpDistance);
AnimatorSet animatorSet = new AnimatorSet(); //组合动画
animatorSet.playTogether(translationX, translationY, translateAnimationX, translateAnimationY); //设置动画
animatorSet.setDuration(800); //设置动画时间
animatorSet.start(); //启动
}
private void iconDownAnim(ImageView ivIcon) {
//抛物线动画,原理:两个位移动画,一个横向匀速移动,一个纵向变速移动,两个动画同时执行,就有了抛物线的效果。
ObjectAnimator translateAnimationX = ObjectAnimator.ofFloat(waterDropOldItem.getImageView(), "translationX", waterDropOldItem.getTranslationUpX(), waterDropOldItem.getTranslationDownX());
translateAnimationX.setInterpolator(new LinearInterpolator());
ObjectAnimator translateAnimationY = ObjectAnimator.ofFloat(waterDropOldItem.getImageView(), "translationY", waterDropOldItem.getTranslationUpY(), waterDropOldItem.getTranslationDownY());
translateAnimationY.setInterpolator(new AccelerateInterpolator());
translateAnimationY.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
waterDropOldItem.finish();
// llyCircleAnim.removeView(waterDropOldItem.getImageView()); //动画结束后移除动画图片
llyCircleAnim.removeViewAt(0);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
ObjectAnimator translationX = new ObjectAnimator().ofFloat(ivIcon, "translationX", 0, 0);
ObjectAnimator translationY = new ObjectAnimator().ofFloat(ivIcon, "translationY", -waterDropAnimUpDistance, 0f);
AnimatorSet animatorSet = new AnimatorSet(); //组合动画
// animatorSet.playTogether(translationX, translationY); //设置动画
animatorSet.playTogether(translationX, translationY, translateAnimationX, translateAnimationY); //设置动画
animatorSet.setDuration(800); //设置动画时间
animatorSet.start(); //启动
}
凹陷位置的移动代码不是动画,是自定义VIew通过不断的重绘完成的
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 从canvas层面去除锯齿
canvas.setDrawFilter(mDrawFilter);
canvas.drawColor(Color.TRANSPARENT);
/*
* 将绘制操作保存到新的图层
*/
int sc = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, null, Canvas.ALL_SAVE_FLAG);
// 设定要绘制的部分
mSrcRect.set(mCurrentImgLeft, 0, mCurrentImgLeft + screenWidth, mTotalHeight);
// 绘纹部分
canvas.drawBitmap(mSrcBitmap, mSrcRect, mDestRect, mBitmapPaint);
// 设置图像的混合模式
mBitmapPaint.setXfermode(mPorterDuffXfermode);
mBitmapPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
// 初始化画笔paint
private void initPaint() {
mBitmapPaint = new Paint();
// 防抖动
mBitmapPaint.setDither(true);
// 开启图像过滤
mBitmapPaint.setFilterBitmap(true);
mPicPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPicPaint.setDither(true);
mPicPaint.setColor(Color.RED);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mTotalWidth = w;
mTotalHeight = h;
mSrcRect = new Rect();
mDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight);
}
public void setPosition(int position) {
if (isSliding) {
return;
}
isSliding = true;
final int targetImgLeft = mStartImgLeft - itemHalfWidth * position * 2;
int a = Math.abs(targetImgLeft - mCurrentImgLeft) / (itemHalfWidth - 1) - 1;
if (a <= 0) {
isSliding = false;
return;
}
// mSpeed = a * mSpeed;//根据距离大小改变速度
new Thread() {
public void run() {
while (true) {
if (targetImgLeft < mCurrentImgLeft) {
//向右
mCurrentImgLeft -= mSpeed;
} else if (targetImgLeft > mCurrentImgLeft) {
//向左
mCurrentImgLeft += mSpeed;
} else {
//不变
return;
}
try {
// 为了保证效果的同时,尽可能将cpu空出来,供其他部分使用
Thread.sleep(30);
} catch (InterruptedException e) {
}
if (Math.abs(mCurrentImgLeft - targetImgLeft) <= mSpeed) {
// 动画停止
mSpeed = 40;
mCurrentImgLeft = targetImgLeft;
isSliding = false;
postInvalidate();
return;
}
postInvalidate();
}
}
}.start();
}
完整DEMO