最近因为项目的需求做的一个加载中圆,刚好记录一下
/**
* 自定义加载中的圆,主要代码
*/
public class LoadingSearchView extends View {
private Paint mCirclePaint;
private Paint mInnerPaint;
private float mCircleRadius;
private final static float RADIUS = 180f;
private final static float INITIAL_ANGLE = 90f;
/**
* 旋转角度
*/
private float mRotationAngle = 0;
/**
* 实心半圆最初的位置
*/
private float mCircleStartAngle;
/**
* 实心半圆应该绘制多大的
*/
private float mCircleSweepAngle;
/**
* 文字的画笔
*/
private Paint mTextPaint;
/**
* 外圈圆的宽
*/
private float mLoadingRoundWidth;
/**
* 外圈圆与内圆的间距
*/
private float mSpacingOfCircles;
/**
* 内圆的颜色
*/
private int mInnerCircleColor;
/**
* 设置几个默认的值
*/
private float mInitialDefaultSize, mLoadingRoundDefaultWidth, mSpacingOfDefaultCircles;
public LoadingSearchView(Context context) {
this(context, null);
}
public LoadingSearchView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadingSearchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initDefaultValue();
initAttributeSet(attrs);
setCirclePaint();
initTextPaint();
}
/**
* 初始化一些默认值
*/
private void initDefaultValue() {
mCircleStartAngle = INITIAL_ANGLE;
mLoadingRoundDefaultWidth = dp2px(getContext(), 8);
mInitialDefaultSize = sp2px(getContext(), 50);
mSpacingOfDefaultCircles = dp2px(getContext(), 5);
}
/**
* 初始化画笔的颜色
*/
private void initTextPaint() {
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(sp2px(getContext(), 16));
mTextPaint.setColor(Color.WHITE);
}
/**
* 初始化圆的画笔
*/
private void setCirclePaint() {
mCirclePaint = initCircle();
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mLoadingRoundWidth);
mInnerPaint = initCircle();
mInnerPaint.setStyle(Paint.Style.FILL);
mInnerPaint.setColor(mInnerCircleColor);
}
private Paint initCircle() {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
return paint;
}
private void initAttributeSet(AttributeSet attrs) {
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.LoadingSearchView);
mLoadingRoundWidth = array.getDimension(R.styleable.LoadingSearchView_loading_round_width, mLoadingRoundDefaultWidth);
mSpacingOfCircles = array.getDimension(R.styleable.LoadingSearchView_spacing_of_circles, mSpacingOfDefaultCircles);
float defaultCircle = mLoadingRoundDefaultWidth + mSpacingOfDefaultCircles + mInitialDefaultSize;
mCircleRadius = array.getDimension(R.styleable.LoadingSearchView_loading_circle_radius, defaultCircle);
mInnerCircleColor = array.getColor(R.styleable.LoadingSearchView_inner_circle_color, Color.parseColor("#00d8c9"));
array.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int measuredWidth = getMeasuredWidth();
float digitalDifference = (measuredWidth - mCircleRadius * 2) / 2;
if (digitalDifference < 0) {
setMeasuredDimension((int) (mCircleRadius * 2), (int) (mCircleRadius * 2));
}
drawOuterCircle(digitalDifference, canvas);
drawInnerCircle(digitalDifference, canvas);
drawSuccessAnimator(digitalDifference, canvas);
}
private void drawSuccessAnimator(float digitalDifference, Canvas canvas) {
if (mCircleSweepAngle >= 360) {
String success = "成功";
Rect bounds = new Rect();
mTextPaint.getTextBounds(success, 0, success.length(), bounds);
float startX = mCircleRadius - bounds.right / 2;
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.top - fontMetricsInt.bottom) / 2 - fontMetricsInt.top;
float baseLine = mCircleRadius + dy;
canvas.drawText(success, startX + digitalDifference, baseLine + digitalDifference, mTextPaint);
}
}
private void drawInnerCircle(float digitalDifference, Canvas canvas) {
RectF circleRectF = new RectF(mLoadingRoundWidth + mSpacingOfCircles + digitalDifference, mLoadingRoundWidth + mSpacingOfCircles + digitalDifference,
(mCircleRadius * 2 - (mLoadingRoundWidth + mSpacingOfCircles)) + digitalDifference, (mCircleRadius * 2 - (mLoadingRoundWidth + mSpacingOfCircles)) + digitalDifference);
canvas.drawArc(circleRectF, mCircleStartAngle, mCircleSweepAngle, false, mInnerPaint);
}
/**
* 画外圆
*
* @param digitalDifference
* @param canvas
*/
private void drawOuterCircle(float digitalDifference, Canvas canvas) {
RectF rectF = new RectF(mLoadingRoundWidth / 2 + digitalDifference, mLoadingRoundWidth / 2 + digitalDifference,
(mCircleRadius * 2 - mLoadingRoundWidth / 2) + digitalDifference, (mCircleRadius * 2 - mLoadingRoundWidth / 2) + digitalDifference);
mCirclePaint.setColor(Color.parseColor("#00d8c9"));
canvas.drawArc(rectF, mRotationAngle, RADIUS, false, mCirclePaint);
mCirclePaint.setColor(Color.parseColor("#64e27f"));
float rotationAngle = mRotationAngle + RADIUS;
canvas.drawArc(rectF, rotationAngle, RADIUS, false, mCirclePaint);
}
public void setRotationAngle(float rotationAngle) {
mRotationAngle = mRotationAngle + 3;
float angle = RADIUS * rotationAngle;
mCircleStartAngle = INITIAL_ANGLE - angle;
mCircleSweepAngle = angle * 2;
invalidate();
}
/**
* dp转px
*/
public float dp2px(Context context, float dpVal) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getApplicationContext().getResources().getDisplayMetrics());
}
/**
* sp转px
*/
public int sp2px(Context context, float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.getApplicationContext().getResources().getDisplayMetrics());
}
}
自定义的属性如下,在values文件中创建文件attrs.xml:
<declare-styleable name="LoadingSearchView">
<attr name="loading_circle_radius" format="dimension"/>
<attr name="loading_round_width" format="dimension"/>
<attr name="spacing_of_circles" format="dimension"/>
<attr name="inner_circle_color" format="color"/>
</declare-styleable>
activity页面的布局也是只有一个加载的view,如下:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context=".MainActivity">
<com.lift.ajun.testapplication.LoadingSearchView
android:id="@+id/test_view"
android:layout_width="200dp"
android:layout_height="200dp"
app:loading_round_width="10dp"/>
</LinearLayout>
MainActivity里面的代码如下:
public class MainActivity extends AppCompatActivity {
private LoadingSearchView mTestView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTestView = findViewById(R.id.test_view);
mTestView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建一个属性动画,来启动圆的动画
ObjectAnimator animator = ObjectAnimator.ofFloat(mTestView, "rotationAngle", 1f);
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(4000);
animator.start();
}
});
}
}