請尊重個人勞動成果,轉載註明出處,謝謝!
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);
}
}
});
}
}