Android自定义view线性图表
public class CharterLine extends CharterBase {
public static final int INDICATOR_TYPE_CIRCLE = 0;//原型指示器
public static final int INDICATOR_TYPE_SQUARE = 1;//直角指示器
public static final int INDICATOR_STYLE_FILL = 0;//
public static final int INDICATOR_STYLE_STROKE = 1;
private static final int DEFAULT_INDICATOR_TYPE = INDICATOR_TYPE_CIRCLE;
private static final int DEFAULT_INDICATOR_STYLE = INDICATOR_STYLE_STROKE;
private static final boolean DEFAULT_INDICATOR_VISIBLE = true;
private static final float DEFAULT_SMOOTHNESS = 0.1f;//设置弧度
private static final boolean DEFAULT_FULL_WIDTH = false;
public boolean fullWidth;//全部充满宽度
private Paint paintLine;//画线w
private Paint paintFill;//
private Paint paintIndicator;
private Paint paintLineText;
private Paint verticalLinePaint;
private Paint paintTextBg;
private Path path;
private int lineColor;
private int chartFillColor;
private int defaultBackgroundColor;
private int chartBackgroundColor;
private float strokeSize;
private float smoothness; //弯曲系数
private float indicatorSize;
private boolean indicatorVisible;
private int indicatorType;
private int indicatorColor;
private int indicatorStyle;
private float indicatorStrokeSize;
private float radius;
private Context mContext;
//dash line
private float[] dashPts;
private float lineLength = 10;
private float dashLength = 4;
private float dashStartY;
private Paint dashPaint;
private int dashLineColor = 0xFFFF6532;
private boolean dashLineVisible;
private float dashLineWidth = 0.2f;
//bottom dash line
private Paint bottomDashPaint;
private float dashStartX;
//gradient
private int gradientStartColor = 0xFFF6CEC5;
private int gradientEndColor = 0xFFFFFFFF;
//text padding
private int paddingLr = 26;
private int paddingTb = 16;
private List<PointF> besselPoints = new ArrayList<>();
public CharterLine(Context context) {
this(context, null, 0);
}
public CharterLine(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CharterLine(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CharterLine(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(final Context context, final AttributeSet attrs) {
mContext = context;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Charter);
fullWidth = typedArray.getBoolean(R.styleable.Charter_c_fullWidth, DEFAULT_FULL_WIDTH);
lineColor = typedArray.getColor(R.styleable.Charter_c_lineColor,
getResources().getColor(R.color.default_lineColor));
chartFillColor = typedArray.getColor(R.styleable.Charter_c_chartFillColor,
getResources().getColor(R.color.default_chartFillColor));
indicatorVisible =
typedArray.getBoolean(R.styleable.Charter_c_indicatorVisible, DEFAULT_INDICATOR_VISIBLE);
indicatorType = typedArray.getInt(R.styleable.Charter_c_indicatorType, DEFAULT_INDICATOR_TYPE);
indicatorSize = typedArray.getDimension(R.styleable.Charter_c_indicatorSize,
getResources().getDimension(R.dimen.default_indicatorSize));
indicatorStrokeSize = typedArray.getDimension(R.styleable.Charter_c_indicatorStrokeSize,
getResources().getDimension(R.dimen.default_indicatorStrokeSize));
indicatorColor = typedArray.getColor(R.styleable.Charter_c_indicatorColor,
getResources().getColor(R.color.default_indicatorColor));
indicatorStyle =
typedArray.getInt(R.styleable.Charter_c_indicatorStyle, DEFAULT_INDICATOR_STYLE);
strokeSize = typedArray.getDimension(R.styleable.Charter_c_strokeSize,
getResources().getDimension(R.dimen.default_strokeSize));
smoothness = typedArray.getFloat(R.styleable.Charter_c_smoothness, DEFAULT_SMOOTHNESS);
anim = typedArray.getBoolean(R.styleable.Charter_c_anim, DEFAULT_ANIM);
animDuration =
typedArray.getInt(R.styleable.Charter_c_animDuration, (int) DEFAULT_ANIM_DURATION);
dashLineColor = typedArray.getColor(R.styleable.Charter_c_dashLineColor, dashLineColor);
dashLineVisible = typedArray.getBoolean(R.styleable.Charter_c_dashLineVisible, true);
dashLineWidth = typedArray.getDimension(R.styleable.Charter_c_dashLineWidth, dashLineWidth);
gradientStartColor = typedArray.getColor(R.styleable.Charter_c_gradientStartColor, gradientStartColor);
gradientEndColor = typedArray.getColor(R.styleable.Charter_c_gradientEndColor, gradientEndColor);
setWillNotDraw(!typedArray.getBoolean(R.styleable.Charter_c_autoShow, DEFAULT_AUTOSHOW));
typedArray.recycle();
paintLine = new Paint();
paintLine.setAntiAlias(true);
paintLine.setStrokeWidth(strokeSize);
paintLine.setColor(lineColor);
paintLine.setStyle(Paint.Style.STROKE);
paintFill = new Paint();
paintFill.setAntiAlias(true);
paintFill.setColor(chartFillColor);
paintFill.setStyle(Paint.Style.FILL);
paintIndicator = new Paint();
paintIndicator.setAntiAlias(true);
paintIndicator.setStrokeWidth(indicatorStrokeSize);
defaultBackgroundColor = getResources().getColor(R.color.default_chartBackgroundColor);
chartBackgroundColor = defaultBackgroundColor;
paintLineText = new Paint();
paintLineText.setAntiAlias(true);
paintLineText.setTextSize(25f);
paintLineText.setColor(Color.WHITE);
verticalLinePaint = new Paint();
verticalLinePaint.setAntiAlias(true);
paintTextBg = new Paint();
paintTextBg.setAntiAlias(true);
paintTextBg.setColor(getResources().getColor(R.color.main_theme_red));
dashPaint = new Paint();
dashPaint.setColor(dashLineColor);
dashPaint.setStrokeWidth(dashLineWidth);
dashPaint.setStyle(Paint.Style.STROKE);
dashPaint.setAntiAlias(true);
bottomDashPaint = new Paint();
bottomDashPaint.setColor(0xFF838388);
bottomDashPaint.setStrokeWidth(dashLineWidth);
bottomDashPaint.setStyle(Paint.Style.STROKE);
bottomDashPaint.setAntiAlias(true);
path = new Path();
radius = getResources().getDimension(R.dimen.dp_8);
}
public Paint getPaintLine() {
return paintLine;
}
public void setPaintLine(Paint paintLine) {
this.paintLine = paintLine;
invalidate();
}
public Paint getPaintFill() {
return paintFill;
}
public void setPaintFill(Paint paintFill) {
this.paintFill = paintFill;
invalidate();
}
public Paint getPaintIndicator() {
return paintIndicator;
}
public void setPaintIndicator(Paint paintIndicator) {
this.paintIndicator = paintIndicator;
invalidate();
}
public float getIndicatorStrokeSize() {
return indicatorStrokeSize;
}
public void setIndicatorStrokeSize(float indicatorStrokeSize) {
paintIndicator.setStrokeWidth(indicatorStrokeSize);
this.indicatorStrokeSize = indicatorStrokeSize;
invalidate();
}
public int getIndicatorStyle() {
return indicatorStyle;
}
public void setIndicatorStyle(@IndicatorStyle int indicatorStyle) {
this.indicatorStyle = indicatorStyle;
invalidate();
}
public int getIndicatorColor() {
return indicatorColor;
}
public void setIndicatorColor(@ColorInt int indicatorColor) {
paintIndicator.setColor(indicatorColor);
this.indicatorColor = indicatorColor;
invalidate();
}
public int getIndicatorType() {
return indicatorType;
}
public void setIndicatorType(@IndicatorType int indicatorType) {
this.indicatorType = indicatorType;
invalidate();
}
public int getLineColor() {
return lineColor;
}
public void setLineColor(@ColorInt int color) {
paintLine.setColor(lineColor);
lineColor = color;
invalidate();
}
public float getIndicatorSize() {
return indicatorSize;
}
public void setIndicatorSize(float indicatorSize) {
this.indicatorSize = indicatorSize;
invalidate();
}
public float getStrokeSize() {
return strokeSize;
}
public void setStrokeSize(float strokeSize) {
paintLine.setStrokeWidth(strokeSize);
this.strokeSize = strokeSize;
invalidate();
}
public int getChartFillColor() {
return chartFillColor;
}
public void setChartFillColor(@ColorInt int chartFillColor) {
paintFill.setColor(chartFillColor);
this.chartFillColor = chartFillColor;
invalidate();
}
public boolean isIndicatorVisible() {
return indicatorVisible;
}
public void setIndicatorVisible(boolean indicatorVisible) {
this.indicatorVisible = indicatorVisible;
invalidate();
}
public float getSmoothness() {
return smoothness;
}
public void setSmoothness(@FloatRange(from = 0.0, to = 0.5) float smoothness) {
this.smoothness = smoothness;
invalidate();
}
public boolean isFullWidth() {
return fullWidth;
}
public void setFullWidth(boolean fullWidth) {
this.fullWidth = fullWidth;
invalidate();
}
public void draw(Canvas canvas) {
super.draw(canvas);
if (values == null || values.length == 0) {
return;
}
if (anim) {
calculateNextAnimStep();
} else {
valuesTransition = values.clone();
}
//get dot drawable
Bitmap dot = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_chart_dot);
int dWidth = dot.getWidth();
int dHeight = dot.getHeight();
indicatorSize = dWidth; //给两边留出边界
float fullWidthCorrectionX;
final int valuesLength = valuesTransition.length;
final float border = strokeSize + indicatorSize;
final float height = getMeasuredHeight() - border;
fullWidthCorrectionX = fullWidth ? 0 : border;
final float width = getMeasuredWidth() - fullWidthCorrectionX;
final float dX = valuesLength > 1 ? valuesLength - 1 : 2;
final float dY = maxY - minY > 0 ? maxY - minY : 2;
path.reset();
// calculate point coordinates
List<PointF> points = new ArrayList<>(valuesLength);
fullWidthCorrectionX = fullWidth ? 0 : (border / 2);
for (int i = 0; i < valuesLength; i++) {
float x = fullWidthCorrectionX + i * width / dX;
float pointBorder = !indicatorVisible && valuesTransition[i] == minY ? border : border / 2;
float y = pointBorder + height - (valuesTransition[i] - minY) * height / dY;
points.add(new PointF(x, y));
}
//draw line
// drawBesselLine(canvas,points);
// drawline(canvas,points);
// drawCurveLine(canvas, points); //画曲线
drawStraightLine(canvas, points); //画直线
// fill area
if (valuesLength > 0) {
int layerId = canvas.save(Canvas.ALL_SAVE_FLAG);
fullWidthCorrectionX = !fullWidth ? 0 : (border / 2);
path.lineTo(points.get(valuesLength - 1).x + fullWidthCorrectionX, height + border);
path.lineTo(points.get(0).x - fullWidthCorrectionX, height + border);
path.close();
canvas.drawPath(path, paintFill);
//draw gradient
LinearGradient linearGradient = new LinearGradient(0, 0, 0,
getMeasuredHeight(), gradientStartColor, gradientEndColor,
Shader.TileMode.CLAMP);
paintFill.setShader(linearGradient);
paintFill.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); //在相交的地方绘制目标图像,不相交绘制源图像
canvas.drawRect(0, getMeasuredHeight(), 5000, 5000, paintFill);
canvas.restoreToCount(layerId);
}
//draw bottom dash line
canvas.save();
canvas.translate(lineLength / 2 + dWidth / 2, 0);
dashStartX = 0;
dashPts = new float[]{0, getMeasuredHeight() - dHeight / 2, lineLength, getMeasuredHeight() - dHeight / 2};
while (dashStartX < width) {
if (dashStartX + lineLength > width) {
float leftLength = width - dashStartX - bottomDashPaint.getStrokeWidth() - 2;
if (leftLength <= 0) leftLength = 1;
dashPts = new float[]{0, getMeasuredHeight() - dHeight / 2, leftLength, getMeasuredHeight() - dHeight / 2};
canvas.drawLines(dashPts, bottomDashPaint);
break;
}
canvas.drawLines(dashPts, bottomDashPaint);
canvas.translate(lineLength + dashLength, 0);
dashStartX += lineLength + dashLength;
}
canvas.restore();
// draw indicator
if (indicatorVisible) {
for (int i = 0; i < points.size(); i++) {
RectF rectF = new RectF();
float x = points.get(i).x;
float y = points.get(i).y;
//draw dash line
if (dashLineVisible) {
canvas.save();
canvas.translate(0, lineLength / 2);
dashStartY = y;
dashPts = new float[]{x, y, x, y + lineLength};
while (dashStartY <= getMeasuredHeight() - dHeight / 2) {
if ((dashStartY + lineLength) >= getMeasuredHeight() - dHeight / 2) {
float leftLength = getMeasuredHeight() - dHeight / 2 - dashStartY - dashPaint.getStrokeWidth() - 2; //left gap between dash line and bottom line for 2
if (leftLength <= 0) leftLength = 1;
dashPts = new float[]{x, y, x, y + leftLength};
canvas.drawLines(dashPts, dashPaint);
break;
}
canvas.drawLines(dashPts, dashPaint);
canvas.translate(0, lineLength + dashLength);
dashStartY += lineLength + dashLength;
}
canvas.restore();
}
//text width height
String text = (int) values[i] + "";
Rect textBound = new Rect();
paintLineText.getTextBounds(text, 0, text.length(), textBound);
int textW = textBound.width();
int textH = textBound.height();
int realH = textH + paddingTb;
RectF oval2 = new RectF();// 设置个新的长方形,扫描测量
paintIndicator.setColor(indicatorColor);
paintIndicator.setStyle(Paint.Style.FILL_AND_STROKE);
if (indicatorType == INDICATOR_TYPE_CIRCLE) {
// canvas.drawCircle(x, y, indicatorSize / 2, paintIndicator); //draw circle
canvas.drawBitmap(dot, x - dWidth / 2, y - dHeight / 2, new Paint()); //draw a self-define image
} else {
rectF.left = x - (indicatorSize / 2);
rectF.top = y - (indicatorSize / 2);
rectF.right = x + (indicatorSize / 2);
rectF.bottom = y + (indicatorSize / 2);
canvas.drawRect(rectF.left, rectF.top, rectF.right, rectF.bottom, paintIndicator);
}
//draw text ,handle situation that text can not be seen completely
if (i == 0) {
if (y - realH - 20 < 0) {
float offset = dWidth / 2 + 4;
oval2.set(x + offset, y / 2, x + textW + paddingLr + offset, y / 2 + realH);
canvas.drawRoundRect(oval2, radius, radius, paintTextBg);
canvas.drawText(text, x + paddingLr / 2 + offset, y / 2 + realH / 2 + textH / 2, paintLineText);
} else {
oval2.set(x, y - realH - 20, x + textW + paddingLr, y - 20);
canvas.drawRoundRect(oval2, radius, radius, paintTextBg);
canvas.drawText(text, x + paddingLr / 2, y - 20 - realH / 2 + textH / 2, paintLineText);
}
} else if (i == points.size() - 1) {
if (y - realH - 20 < 0) {
float offset = dWidth / 2 + 4;
oval2.set(x - textW - paddingLr - offset, y / 2, x - offset, y / 2 + realH);
canvas.drawRoundRect(oval2, radius, radius, paintTextBg);
canvas.drawText(text, x - paddingLr / 2 - textW - offset, y / 2 + realH / 2 + textH / 2, paintLineText);
} else {
oval2.set(x - textW - paddingLr, y - realH - 20, x, y - 20);
canvas.drawRoundRect(oval2, radius, radius, paintTextBg);
canvas.drawText(text, x - paddingLr / 2 - textW, y - 20 - realH / 2 + textH / 2, paintLineText);
}
} else {
if (y - realH - 20 < 0) {
float offset = dWidth / 2 + textW / 2 + paddingLr / 2 + 4;
oval2.set(x - textW / 2 - paddingLr / 2 + offset, y / 2, x + textW / 2 + paddingLr / 2 + offset, y / 2 + realH);
canvas.drawRoundRect(oval2, radius, radius, paintTextBg);
canvas.drawText(text, x - textW / 2 + offset, y / 2 + realH / 2 + textH / 2, paintLineText);
} else {
oval2.set(x - textW / 2 - paddingLr / 2, y - realH - 20, x + textW / 2 + paddingLr / 2, y - 20);
canvas.drawRoundRect(oval2, radius, radius, paintTextBg);
canvas.drawText(text, x - textW / 2, y - 20 - realH / 2 + textH / 2, paintLineText);
}
}
/* if (indicatorStyle == INDICATOR_STYLE_STROKE) {
paintIndicator.setColor(chartBackgroundColor);
paintIndicator.setStyle(Paint.Style.FILL);
if (indicatorType == INDICATOR_TYPE_CIRCLE) {
canvas.drawCircle(x, y, (indicatorSize - indicatorStrokeSize) / 2, paintIndicator);
} else {
rectF.left = x - (indicatorSize / 2) + indicatorStrokeSize;
rectF.top = y - (indicatorSize / 2) + indicatorStrokeSize;
rectF.right = x + (indicatorSize / 2) - indicatorStrokeSize;
rectF.bottom = y + (indicatorSize / 2) - indicatorStrokeSize;
canvas.drawRect(rectF.left, rectF.top, rectF.right, rectF.bottom, paintIndicator);
}
}*/
}
}
if (anim && !animFinished) {
handlerAnim.postDelayed(doNextAnimStep, ANIM_DELAY_MILLIS);
}
}
//绘制贝塞尔曲线
private void drawBesselLine(Canvas canvas,List<PointF> points){
List<PointF> realPoints=new ArrayList<>();
for (PointF point : points) {
if (point.y>0) realPoints.add(point);
}
int count = points.size();
if (count < 2)
return;
besselPoints.clear();
for (int i = 0; i < count; i++) {
if (i == 0 || i == count - 1) {
computeUnMonotonePoints(i, realPoints, besselPoints);
} else {
PointF p0 = points.get(i - 1);
PointF p1 = points.get(i);
PointF p2 = points.get(i + 1);
if ((p1.y - p0.y) * (p1.y - p2.y) >= 0) {// 极值点
computeUnMonotonePoints(i, realPoints, besselPoints);
} else {
computeMonotonePoints(i, realPoints, besselPoints);
}
}
}
for (int i=0;i<besselPoints.size();i=i+3){
if (i==0) path.moveTo(besselPoints.get(i).x,besselPoints.get(i).y);
else {
path.cubicTo(besselPoints.get(i - 2).x, besselPoints.get(i - 2).y, besselPoints.get(i - 1).x, besselPoints.get(i - 1).y, besselPoints.get(i).x, besselPoints.get(i).y);
}
}
canvas.drawPath(path,paintLine);
}
/** 计算非单调情况的贝塞尔结点 */
private void computeUnMonotonePoints(int i, List<PointF> points, List<PointF> besselPoints) {
if (i == 0) {
PointF p1 = points.get(0);
PointF p2 = points.get(1);
besselPoints.add(p1);
besselPoints.add(new PointF(p1.x + (p2.x - p1.x) * smoothness, p1.y));
} else if (i == points.size() - 1) {
PointF p0 = points.get(i - 1);
PointF p1 = points.get(i);
besselPoints.add(new PointF(p1.x - (p1.x - p0.x) * smoothness, p1.y));
besselPoints.add(p1);
} else {
PointF p0 = points.get(i - 1);
PointF p1 = points.get(i);
PointF p2 = points.get(i + 1);
besselPoints.add(new PointF(p1.x - (p1.x - p0.x) * smoothness, p1.y));
besselPoints.add(p1);
besselPoints.add(new PointF(p1.x + (p2.x - p1.x) * smoothness, p1.y));
}
}
/**
* 计算单调情况的贝塞尔结点
*
* @param i
* @param points
* @param besselPoints
*/
private void computeMonotonePoints(int i, List<PointF> points, List<PointF> besselPoints) {
PointF p0 = points.get(i - 1);
PointF p1 = points.get(i);
PointF p2 = points.get(i + 1);
float k = (p2.y - p0.y) / (p2.x - p0.x);
float b = p1.y - k * p1.x;
PointF p01 = new PointF();
p01.x = p1.x - (p1.x - (p0.y - b) / k) * smoothness;
p01.y = k * p01.x + b;
besselPoints.add(p01);
besselPoints.add(p1);
PointF p11 = new PointF();
p11.x = p1.x + (p2.x - p1.x) * smoothness;
p11.y = k * p11.x + b;
besselPoints.add(p11);
}
private void drawline(Canvas canvas,List<PointF> points){
for (int i=0;i<points.size();i++){
if (i==0){
path.moveTo(points.get(i).x,points.get(i).y);
continue;
}
PointF currentPoint=points.get(i);
PointF previousPoint=points.get(i-1);
float controlX=(currentPoint.x+previousPoint.x)/2;
path.cubicTo(controlX,previousPoint.y,controlX,currentPoint.y,currentPoint.x,currentPoint.y);
}
canvas.drawPath(path,paintLine);
}
private void drawStraightLine(Canvas canvas, List<PointF> points) {
for (int i = 0; i < points.size(); i++) {
if (i == 0) path.moveTo(points.get(i).x, points.get(i).y);
else path.lineTo(points.get(i).x, points.get(i).y);
}
canvas.drawPath(path, paintLine);
}
private void drawCurveLine(Canvas canvas, List<PointF> points) {
float prePreviousPointX = Float.NaN;
float prePreviousPointY = Float.NaN;
float previousPointX = Float.NaN;
float previousPointY = Float.NaN;
float currentPointX = Float.NaN;
float currentPointY = Float.NaN;
float nextPointX;
float nextPointY;
for (int i = 0; i < points.size(); i++) {
if (Float.isNaN(currentPointX)) {
PointF pointF = points.get(i);
currentPointX = pointF.x;
currentPointY = pointF.y;
}
if (Float.isNaN(previousPointX)) {
//check if the first one
if (i > 0) {
PointF pointF = points.get(i - 1);
previousPointX = pointF.x;
previousPointY = pointF.y;
} else {
previousPointX = currentPointX;
previousPointY = currentPointY;
}
}
if (Float.isNaN(prePreviousPointX)) {
//check if the second one
if (i > 1) {
PointF pointF = points.get(i - 2);
prePreviousPointX = pointF.x;
prePreviousPointY = pointF.y;
} else {
prePreviousPointX = previousPointX;
prePreviousPointY = previousPointY;
}
}
//check if the last one
if (i < points.size() - 1) {
PointF pointF = points.get(i + 1);
nextPointX = pointF.x;
nextPointY = pointF.y;
} else {
nextPointX = currentPointX;
nextPointY = currentPointY;
}
//link line
if (i == 0) {
path.moveTo(currentPointX, currentPointY);
} else {
//calculate the control position
float firstControlX = previousPointX + (currentPointX - prePreviousPointX) * smoothness;
float firstControlY = previousPointY + (currentPointY - prePreviousPointY) * smoothness;
float secondControlX = currentPointX - (nextPointX - previousPointX) * smoothness;
float secondControlY = currentPointY - (nextPointY - previousPointY) * smoothness;
path.cubicTo(firstControlX, firstControlY, secondControlX, secondControlY, currentPointX, currentPointY);
}
prePreviousPointX = previousPointX;
prePreviousPointY = previousPointY;
previousPointX = currentPointX;
previousPointY = currentPointY;
currentPointX = nextPointX;
currentPointY = nextPointY;
}
canvas.drawPath(path, paintLine);
}
@Override
public void setBackgroundColor(int color) {
super.setBackgroundColor(color);
chartBackgroundColor = color;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void setBackground(Drawable background) {
super.setBackground(background);
chartBackgroundColor = defaultBackgroundColor;
Drawable drawable = getBackground();
if (drawable instanceof ColorDrawable) {
chartBackgroundColor = ((ColorDrawable) drawable).getColor();
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({INDICATOR_STYLE_FILL, INDICATOR_STYLE_STROKE})
public @interface IndicatorType {
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({INDICATOR_TYPE_CIRCLE, INDICATOR_TYPE_SQUARE})
public @interface IndicatorStyle {
}
}