package com.c9mj.platform.widget.inicator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import net.lucode.hackware.magicindicator.NavigatorHelper;
import net.lucode.hackware.magicindicator.abs.IPagerNavigator;
import net.lucode.hackware.magicindicator.buildins.ArgbEvaluatorHolder;
import net.lucode.hackware.magicindicator.buildins.UIUtil;
import java.util.ArrayList;
import java.util.List;
/**
* author: liminjie
* date: 2017/2/27
* desc: ScaleCircleNavigator可缩放圆点指示器
*/
public class ScaleCircleNavigator extends View implements IPagerNavigator, NavigatorHelper.OnNavigatorScrollListener {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final List<PointF> mCirclePoints = new ArrayList<>();
private final SparseArray<Float> mCircleRadiusArray = new SparseArray<>();
private final NavigatorHelper mNavigatorHelper = new NavigatorHelper();
private int mMinRadius;
private int mMaxRadius;
private int mNormalCircleColor = Color.parseColor("#bdbdbd");
private int mSelectedCircleColor = Color.parseColor("#ffffff");
private int mCircleSpacing;
private int mCircleCount;
// 事件回调
private boolean mTouchable;
private ScaleCircleNavigator.OnCircleClickListener mCircleClickListener;
private float mDownX;
private float mDownY;
private int mTouchSlop;
private boolean mFollowTouch = true; // 是否跟随手指滑动
private Interpolator mStartInterpolator = new LinearInterpolator();
public ScaleCircleNavigator(Context context) {
super(context);
init(context);
}
private void init(Context context) {
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mMinRadius = UIUtil.dip2px(context, 2);
mMaxRadius = UIUtil.dip2px(context, 3);
mCircleSpacing = UIUtil.dip2px(context, 8);
mNavigatorHelper.setNavigatorScrollListener(this);
mNavigatorHelper.setSkimOver(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
private int measureWidth(int widthMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int result = 0;
switch (mode) {
case MeasureSpec.EXACTLY:
result = width;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
result = (mCircleCount - 1) * mMinRadius * 2 + mMaxRadius * 2 + (mCircleCount - 1) * mCircleSpacing + getPaddingLeft() + getPaddingRight();
break;
default:
break;
}
return result;
}
private int measureHeight(int heightMeasureSpec) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int result = 0;
switch (mode) {
case MeasureSpec.EXACTLY:
result = height;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
result = mMaxRadius * 2 + getPaddingTop() + getPaddingBottom();
break;
default:
break;
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0, j = mCirclePoints.size(); i < j; i++) {
PointF point = mCirclePoints.get(i);
float radius = mCircleRadiusArray.get(i, (float) mMinRadius);
mPaint.setColor(ArgbEvaluatorHolder.eval((radius - mMinRadius) / (mMaxRadius - mMinRadius), mNormalCircleColor, mSelectedCircleColor));
canvas.drawCircle(point.x, getHeight() / 2.0f, radius, mPaint);
}
}
private void prepareCirclePoints() {
mCirclePoints.clear();
if (mCircleCount > 0) {
int y = Math.round(getHeight() / 2.0f);
int centerSpacing = mMinRadius * 2 + mCircleSpacing;
int startX = mMaxRadius + getPaddingLeft();
for (int i = 0; i < mCircleCount; i++) {
PointF pointF = new PointF(startX, y);
mCirclePoints.add(pointF);
startX += centerSpacing;
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mTouchable) {
mDownX = x;
mDownY = y;
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mCircleClickListener != null) {
if (Math.abs(x - mDownX) <= mTouchSlop && Math.abs(y - mDownY) <= mTouchSlop) {
float max = Float.MAX_VALUE;
int index = 0;
for (int i = 0; i < mCirclePoints.size(); i++) {
PointF pointF = mCirclePoints.get(i);
float offset = Math.abs(pointF.x - x);
if (offset < max) {
max = offset;
index = i;
}
}
mCircleClickListener.onClick(index);
}
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mNavigatorHelper.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
mNavigatorHelper.onPageSelected(position);
}
@Override
public void onPageScrollStateChanged(int state) {
mNavigatorHelper.onPageScrollStateChanged(state);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
prepareCirclePoints();
}
@Override
public void notifyDataSetChanged() {
prepareCirclePoints();
invalidate();
}
@Override
public void onAttachToMagicIndicator() {
}
@Override
public void onDetachFromMagicIndicator() {
}
public void setMinRadius(int minRadius) {
mMinRadius = minRadius;
prepareCirclePoints();
invalidate();
}
public void setMaxRadius(int maxRadius) {
mMaxRadius = maxRadius;
prepareCirclePoints();
invalidate();
}
public void setNormalCircleColor(int normalCircleColor) {
mNormalCircleColor = normalCircleColor;
invalidate();
}
public void setSelectedCircleColor(int selectedCircleColor) {
mSelectedCircleColor = selectedCircleColor;
invalidate();
}
public void setCircleSpacing(int circleSpacing) {
mCircleSpacing = circleSpacing;
prepareCirclePoints();
invalidate();
}
public void setStartInterpolator(Interpolator startInterpolator) {
mStartInterpolator = startInterpolator;
if (mStartInterpolator == null) {
mStartInterpolator = new LinearInterpolator();
}
}
public void setCircleCount(int count) {
mCircleCount = count; // 此处不调用invalidate,让外部调用notifyDataSetChanged
mNavigatorHelper.setTotalCount(mCircleCount);
}
public void setTouchable(boolean touchable) {
mTouchable = touchable;
}
public void setFollowTouch(boolean followTouch) {
mFollowTouch = followTouch;
}
public void setSkimOver(boolean skimOver) {
mNavigatorHelper.setSkimOver(skimOver);
}
public void setCircleClickListener(OnCircleClickListener circleClickListener) {
if (!mTouchable) {
mTouchable = true;
}
mCircleClickListener = circleClickListener;
}
@Override
public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) {
if (mFollowTouch) {
float radius = mMinRadius + (mMaxRadius - mMinRadius) * mStartInterpolator.getInterpolation(enterPercent);
mCircleRadiusArray.put(index, radius);
invalidate();
}
}
@Override
public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) {
if (mFollowTouch) {
float radius = mMaxRadius + (mMinRadius - mMaxRadius) * mStartInterpolator.getInterpolation(leavePercent);
mCircleRadiusArray.put(index, radius);
invalidate();
}
}
@Override
public void onSelected(int index, int totalCount) {
if (!mFollowTouch) {
mCircleRadiusArray.put(index, (float) mMaxRadius);
invalidate();
}
}
@Override
public void onDeselected(int index, int totalCount) {
if (!mFollowTouch) {
mCircleRadiusArray.put(index, (float) mMinRadius);
invalidate();
}
}
public interface OnCircleClickListener {
void onClick(int index);
}
}