请尊重个人劳动成果,转载注明出处,谢谢!
http://blog.csdn.net/xiaxiazaizai01/article/details/52442300
自定义View我也写了好几篇了,今天那就再来一篇,主要的技术点在前几篇都详细介绍过,不明真相的吃瓜群众该吐槽小编了,能不能写一些风格尺度相对大点的,哈哈。。。小编目前处于学习阶段,跟大牛比还有很大很大很大。。。你们无形中戳到了小编的痛点,然。。咱也是在一点一点积累的。一直以来小编都有一个疑问始终不明白,为啥每次开头都要说半天废话呢。。估计是小编觉着这样才不会显得太low吧,哈哈,言归正传,今天我们要实现的效果就是仿微信小视频加载时与用户交互的那个圆形加载效果。老规矩,先来张效果图
上面已经说过了,其实实现这样的效果并不难,主要的技术点在前面几篇博客中已经详细的说明了,想更好的理解小编的思路,请查看之前的几篇自定义view相关的内容。本篇博客就不再详细注释了,相信只要你看过之前的几篇,这一篇读起来肯定相当轻松,因为本篇相对于之前写的还算简单一点的。
首先创建view
(1)自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomCircleLoading">
<attr name="outer_layout_circle_color" format="color"/>
<attr name="outer_layout_circle_stroke_width" format="dimension"/>
<attr name="triangle_color" format="color"/>
<attr name="radius" format="dimension"/>
</declare-styleable>
</resources>
(2)获取我们的自定义属性
public CustomCircleLoading(Context context) {
this(context,null);
}
public CustomCircleLoading(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomCircleLoading(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.CustomCircleLoading);
int indexCount = array.getIndexCount();
for(int i = 0;i < indexCount;i++){
int attr = array.getIndex(i);
switch (attr){
case R.styleable.CustomCircleLoading_outer_layout_circle_color:
outerCircleColor = array.getColor(attr, OUTER_LAYOUT_CIRCLE_COLOR);
break;
case R.styleable.CustomCircleLoading_outer_layout_circle_stroke_width:
outerCircleStrokeWidth = (int) array.getDimension(attr, outerCircleStrokeWidth);
break;
case R.styleable.CustomCircleLoading_triangle_color:
mTriangleColor = array.getColor(attr, mTriangleColor);
break;
case R.styleable.CustomCircleLoading_radius:
mRadius = (int) array.getDimension(attr, mRadius);
break;
}
}
//回收
array.recycle();
mDistance = (float) (mRadius * 0.06);
mTriangleLength = mRadius;
//设置画笔
setPaint();
//画三角形
mPath = new Path();
float mFirstPointX = (float) (mRadius - Math.sqrt(3.0) / 4 * mRadius);//勾股定理
float mNiceFirstPointX = (float) (mFirstPointX + mFirstPointX * 0.2);
float mFirstPointY = mRadius - mTriangleLength / 2;
mPath.moveTo(mNiceFirstPointX,mFirstPointY);
mPath.lineTo(mNiceFirstPointX, mRadius+mTriangleLength / 2);
mPath.lineTo((float) (mNiceFirstPointX+Math.sqrt(3.0) / 2 * mRadius), mRadius);
mPath.lineTo(mNiceFirstPointX, mFirstPointY);
}
测量View,即onMeasure( )
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width;
int height;
if(widthMode != MeasureSpec.EXACTLY){
width = getPaddingLeft() + mRadius*2 + outerCircleStrokeWidth*2 + getPaddingRight();
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
if(heightMode != MeasureSpec.EXACTLY){
height = getPaddingTop() + mRadius*2 + outerCircleStrokeWidth*2 + getPaddingBottom();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
绘制View,即onDraw()
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(getPaddingLeft(),getPaddingTop());
//画圆
canvas.drawCircle(mRadius,mRadius,mRadius,outerCirclePaint);
if(mStatus == Status.End){
//画三角形
canvas.drawPath(mPath, mTrianglePaint);
}else{//正在进行状态
//画扇形
canvas.drawArc(new RectF(0 + mDistance,0 + mDistance,mRadius*2 - mDistance,mRadius*2 - mDistance), -90, 360*mArcAngle, true, mArcPaint);
}
canvas.restore();
}
接下来就是与用户的交互
我们用属性动画来实现模拟扇形的加载数据
public void animatorAngle(){
setClickable(false);
ValueAnimator animator = ValueAnimator.ofFloat(0, 1.0f);
animator.setDuration(6000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mArcAngle = (float) animation.getAnimatedValue();
//刷新View
invalidate();
}
});
//开启动画
animator.start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
//动画结束后修改状态变化
mStatus = Status.End;
setClickable(true);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
最后是我们的MainActivity去调用
public class MainActivity extends AppCompatActivity {
private CustomCircleLoading circleLoading;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == MESSAGE_SUCCESS){
circleLoading.animatorAngle();
}
}
};
public static final int MESSAGE_SUCCESS = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
circleLoading = (CustomCircleLoading) findViewById(R.id.circleLoading);
circleLoading.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(circleLoading.getStatus() == CustomCircleLoading.Status.End){
circleLoading.setStatus(CustomCircleLoading.Status.Starting);
Message message = Message.obtain();
message.what = MESSAGE_SUCCESS;
handler.sendMessage(message);
}else{
circleLoading.setStatus(CustomCircleLoading.Status.End);
handler.removeMessages(MESSAGE_SUCCESS);
}
}
});
}
}