最近利用空闲时间,通过贝塞尔曲线写了一个Indicator,效果如下(视频转换gif效果不好)
完整代码传送门
对于原点的绘制及变化,需要对贝塞尔曲线及通过三阶贝塞尔曲线绘制圆形有初步了解,请参考以下文章
Bézier curve;Composite Bézier curve;Approximate a circle with cubic Bézier curves
对于贝塞尔曲线理论,没有较为扎实的数学基础还是很难理解的,笔者的高等数学早已把我抛弃了,很多符号都不记得,断断续续写了一个月才完成编写,主要是在分裂与粘合效果这碰了壁,很是汗颜~~~,好了直接上代码吧!
public class CustomIndicator extends View {
private static final double factor = 0.55191502449;
public static final int INDICATOR_TYPE_SCALE = 0;
public static final int INDICATOR_TYPE_GRADUAL = 1;
public static final int INDICATOR_TYPE_SPLIT = 2;
public static final int INDICATOR_TYPE_SCALE_AND_GRADUAL = 3;
private static final int DEFAULT_NORMAL_POINT_RADIUS = 8;
private static final int DEFAULT_SELECTED_POINT_RADIUS = 12;
private int heightMeasureSpec;
private Paint normalPointPaint;
private Paint selectedPointPaint;
private Paint targetPointPaint;
private float normalPointRadius;
private float selectedPointRadius;
private int pointInterval;
private int normalPointColor;
private int selectedPointColor;
private int indicatorType;
private int pointCount;
private List relativeControlPoints;
// private int selectedPagePosition;
private int currentPagePosition;
private int targetPagePosition;
private int width;
private int height;
private Path arcPath;
private Path splitArcPath;
private float translationFactor;
private PagerAdapter adapter;
private DataSetObserver dataSetObserver;
private static final int SPLIT_OFFSET = DisplayUtil.dp2px(10);
private static final float SPLIT_RADIUS_FACTOR = 1.4f;
@IntDef({INDICATOR_TYPE_SCALE, INDICATOR_TYPE_GRADUAL, INDICATOR_TYPE_SCALE_AND_GRADUAL, INDICATOR_TYPE_SPLIT})
@Retention(RetentionPolicy.SOURCE)
private @interface IndicatorType {
}
public CustomIndicator(Context context) {
this(context, null);
}
public CustomIndicator(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomIndicator);
indicatorType = typedArray.getInt(R.styleable.CustomIndicator_indicatorType, 0);
normalPointRadius = typedArray.getDimensionPixelSize(R.styleable.CustomIndicator_normalPointRadius, DEFAULT_NORMAL_POINT_RADIUS);
selectedPointRadius = typedArray.getDimensionPixelSize(R.styleable.CustomIndicator_selectedPointRadius, indicatorType == INDICATOR_TYPE_GRADUAL ? DEFAULT_NORMAL_POINT_RADIUS : DEFAULT_SELECTED_POINT_RADIUS);
pointInterval = typedArray.getDimensionPixelSize(R.styleable.CustomIndicator_pointInterval, 20);
normalPointColor = typedArray.getColor(R.styleable.CustomIndicator_normalPointColor, Color.parseColor("#FFFFFF"));
selectedPointColor = typedArray.getColor(R.styleable.CustomIndicator_selectedPointColor, Color.parseColor("#11EEEE"));
typedArray.recycle();
normalPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
normalPointPaint.setStyle(Paint.Style.FILL);
normalPointPaint.setColor(normalPointColor);
if (indicatorType == INDICATOR_TYPE_GRADUAL || indicatorType == INDICATOR_TYPE_SCALE_AND_GRADUAL) {
selectedPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
selectedPointPaint.setStyle(Paint.Style.FILL);
selectedPointPaint.setColor(selectedPointColor);
targetPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
targetPointPaint.setStyle(Paint.Style.FILL);
targetPointPaint.setColor(selectedPointColor);
} else if (indicatorType == INDICATOR_TYPE_SPLIT) {
if (selectedPointRadius < normalPointRadius * SPLIT_RADIUS_FACTOR) {
selectedPointRadius = (int) (normalPointRadius * SPLIT_RADIUS_FACTOR);
}
if (pointInterval < SPLIT_RADIUS_FACTOR * normalPointRadius * 2 + SPLIT_OFFSET) {
pointInterval = (int) (SPLIT_RADIUS_FACTOR * normalPointRadius * 2 + SPLIT_OFFSET);
}
}
arcPath = new Path