package com.example.myapplication;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Nullable;
public class ModeSwitcher extends View {
private int mSelectedItemWidth;
private int mSelectedItemHeight;
private int mSelectedToNormalMargin;
private int mNormalItemRadius;
private int mNormalToNormalMargin;
private int mItemBgColor;
private Paint mPaint;
private Bitmap[] mItemIcons;
private int mTargetSelPosition;
private int mLastSelPosition;
private OnItemChangeListener mItemChangeListener;
private ValueAnimator mSwitchAnim;
private final AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLastSelPosition = mTargetSelPosition;
invalidate();
if (mItemChangeListener != null) {
mItemChangeListener.afterItemChange(mLastSelPosition, mTargetSelPosition);
}
}
@Override
public void onAnimationStart(Animator animation) {
if (mItemChangeListener != null) {
mItemChangeListener.beforeItemChange(mLastSelPosition, mTargetSelPosition);
}
}
};
public ModeSwitcher(Context context) {
this(context, null);
}
public ModeSwitcher(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ModeSwitcher(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
Resources resources = getResources();
mSelectedItemWidth = resources.getDimensionPixelSize(R.dimen.mode_switch_sel_item_w);
mSelectedItemHeight = resources.getDimensionPixelSize(R.dimen.mode_switch_sel_item_h);
mSelectedToNormalMargin = resources.getDimensionPixelSize(R.dimen.mode_switch_sel_to_normal_margin);
mNormalItemRadius = resources.getDimensionPixelSize(R.dimen.mode_switch_normal_item_r);
mNormalToNormalMargin = resources.getDimensionPixelSize(R.dimen.mode_switch_normal_to_normal_margin);
mItemBgColor = resources.getColor(R.color.mode_switcher_item_bg);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mSwitchAnim = ValueAnimator.ofFloat(0, 1);
mSwitchAnim.setInterpolator(new LinearInterpolator());
mSwitchAnim.setDuration(200);
mSwitchAnim.addUpdateListener(animation -> invalidate());
mSwitchAnim.addListener(mAnimatorListenerAdapter);
mTargetSelPosition = 0;
mLastSelPosition = mTargetSelPosition;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (checkItemsIfEmpty()) {
return;
}
final int count = mItemIcons.length;
for (int i = 0; i < count; i++) {
drawItems(canvas, i);
}
}
private void drawItems(Canvas canvas, int position) {
final float halfW = getWidth() / 2f;
final float halfH = getHeight() / 2f;
final float cx = getPaddingLeft() + (getWidth() - getPaddingLeft() - getPaddingRight()) / 2f;
final float cy = getPaddingTop() + (getHeight() - getPaddingTop() - getPaddingBottom()) / 2f;
final Bitmap itemIcon = mItemIcons[position];
float itemCx;
int alpha;
boolean isNextMode = mTargetSelPosition > mLastSelPosition;
int positionOffset = position - mTargetSelPosition;
float fraction = mSwitchAnim.isStarted() ? mSwitchAnim.getAnimatedFraction() : 1;
float halfBgWidth;
float halfBgHeight;
if (positionOffset < 0) {
if (!isNextMode) {
itemCx = cx - mSelectedItemWidth / 2f - mSelectedToNormalMargin - mNormalItemRadius
- ((Math.abs(positionOffset) - 1) * (mNormalItemRadius * 2 + mNormalToNormalMargin))
- (mNormalItemRadius * 2 + mNormalToNormalMargin) * (1 - fraction);
halfBgWidth = mNormalItemRadius;
halfBgHeight = mNormalItemRadius;
alpha = 0;
} else {
if (position == mLastSelPosition) {
itemCx = cx - (mSelectedItemWidth / 2f + mSelectedToNormalMargin + mNormalItemRadius) * fraction;
halfBgWidth = mNormalItemRadius + (mSelectedItemWidth / 2f - mNormalItemRadius) * (1 - fraction);
halfBgHeight = mNormalItemRadius + (mSelectedItemHeight / 2f - mNormalItemRadius) * (1 - fraction);
alpha = (int) (255 * (1 - fraction));
} else {
itemCx = cx - mSelectedItemWidth / 2f - mSelectedToNormalMargin - mNormalItemRadius
- ((Math.abs(positionOffset) - 1) * (mNormalItemRadius * 2 + mNormalToNormalMargin))
+ (mNormalItemRadius * 2 + mNormalToNormalMargin) * (1 - fraction);
halfBgWidth = mNormalItemRadius;
halfBgHeight = mNormalItemRadius;
alpha = 0;
}
}
} else if (positionOffset > 0) {
if (isNextMode) {
itemCx = cx + mSelectedItemWidth / 2f + mSelectedToNormalMargin + mNormalItemRadius
+ ((Math.abs(positionOffset) - 1) * (mNormalItemRadius * 2 + mNormalToNormalMargin))
+ (mNormalItemRadius * 2 + mNormalToNormalMargin) * (1 - fraction);
halfBgWidth = mNormalItemRadius;
halfBgHeight = mNormalItemRadius;
alpha = 0;
} else {
if (position == mLastSelPosition) {
itemCx = cx + (mSelectedItemWidth / 2f + mSelectedToNormalMargin + mNormalItemRadius) * fraction;
halfBgWidth = mNormalItemRadius + (mSelectedItemWidth / 2f - mNormalItemRadius) * (1 - fraction);
halfBgHeight = mNormalItemRadius + (mSelectedItemHeight / 2f - mNormalItemRadius) * (1 - fraction);
alpha = (int) (255 * (1 - fraction));
} else {
itemCx = cx + mSelectedItemWidth / 2f + mSelectedToNormalMargin + mNormalItemRadius
+ ((Math.abs(positionOffset) - 1) * (mNormalItemRadius * 2 + mNormalToNormalMargin))
- (mNormalItemRadius * 2 + mNormalToNormalMargin) * (1 - fraction);
halfBgWidth = mNormalItemRadius;
halfBgHeight = mNormalItemRadius;
alpha = 0;
}
}
} else {
float cxOffset = (mSelectedItemWidth / 2f + mSelectedToNormalMargin + mNormalItemRadius) * (1 - fraction);
if (isNextMode) {
itemCx = cx + cxOffset;
} else {
itemCx = cx - cxOffset;
}
halfBgWidth = mNormalItemRadius + (mSelectedItemWidth / 2f - mNormalItemRadius) * fraction;
halfBgHeight = mNormalItemRadius + (mSelectedItemHeight / 2f - mNormalItemRadius) * fraction;
alpha = (int) (255 * fraction);
}
canvas.save();
canvas.translate(itemCx, cy);
// 1.draw item background
mPaint.setColor(mItemBgColor);
canvas.drawRoundRect(-halfBgWidth, -halfBgHeight, halfBgWidth, halfBgHeight, halfBgHeight, halfBgHeight, mPaint);
// 2.draw item icon
int layerId = canvas.saveLayerAlpha(-halfW, -halfH, halfW, halfH, alpha);
float scale = Math.min(halfBgWidth * 2f / mSelectedItemWidth, halfBgHeight * 2f / mSelectedItemHeight);
canvas.scale(scale, scale);
mPaint.setColor(Color.WHITE);
canvas.drawBitmap(itemIcon, -itemIcon.getWidth() / 2f, -itemIcon.getHeight() / 2f, mPaint);
canvas.restoreToCount(layerId);
canvas.restore();
}
private boolean checkItemsIfEmpty() {
return mItemIcons == null || mItemIcons.length == 0;
}
private Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bm = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
drawable.setBounds(0, 0, bm.getWidth(), bm.getHeight());
drawable.draw(canvas);
return bm;
}
public void setSelectedPosition(int selPosition, boolean isAnimate) {
if (checkItemsIfEmpty()) {
return;
}
if (mTargetSelPosition != selPosition && selPosition >= 0 && selPosition < mItemIcons.length) {
if (mSwitchAnim.isStarted()) {
mSwitchAnim.removeAllListeners();
mSwitchAnim.cancel();
mSwitchAnim.addListener(mAnimatorListenerAdapter);
}
mTargetSelPosition = selPosition;
if (isAnimate) {
mSwitchAnim.start();
} else {
mLastSelPosition = mTargetSelPosition;
if (mItemChangeListener != null) {
mItemChangeListener.beforeItemChange(mLastSelPosition, mTargetSelPosition);
mItemChangeListener.afterItemChange(mLastSelPosition, mTargetSelPosition);
}
}
invalidate();
}
}
public void setItemIcons(int... itemIconIds) {
if (mSwitchAnim.isStarted()) {
mSwitchAnim.removeAllListeners();
mSwitchAnim.cancel();
mSwitchAnim.addListener(mAnimatorListenerAdapter);
}
mTargetSelPosition = 0;
mLastSelPosition = mTargetSelPosition;
if (itemIconIds == null) {
mItemIcons = null;
invalidate();
return;
}
mItemIcons = new Bitmap[itemIconIds.length];
int count = mItemIcons.length;
for (int i = 0; i < count; i++) {
mItemIcons[i] = drawableToBitmap(getResources().getDrawable(itemIconIds[i]));
}
invalidate();
if (mItemChangeListener != null) {
mItemChangeListener.beforeItemChange(mLastSelPosition, mTargetSelPosition);
mItemChangeListener.afterItemChange(mLastSelPosition, mTargetSelPosition);
}
}
public boolean isModeSwitching() {
return mSwitchAnim.isStarted();
}
public int getSelectedPosition() {
return mTargetSelPosition;
}
public void setOnItemChangeListener(OnItemChangeListener listener) {
mItemChangeListener = listener;
}
public interface OnItemChangeListener {
void beforeItemChange(int lastSelPosition, int currentSelPosition);
void afterItemChange(int lastSelPosition, int currentSelPosition);
}
}
/* 计算得到图片的高度 /
/ 这里需要主意,如果你需要更高的精度来保证图片不变形的话,需要自己进行一下数学运算 /
int height = options.outHeight * 200 / options.outWidth;
options.outWidth = 200;
options.outHeight = height;
/ 这样才能真正的返回一个Bitmap给你 */
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
image.setImageBitmap(bmp);