雅虎新闻摘要加载

效果图请看这个帖子写的,跟着写的 https://www.jianshu.com/p/923513a70d15

image.png

看完别人的效果图,第一步不是赶紧往下看,而是自己脑子里想下,自己写的话该咋写。
首先画6个小球,旋转, 然后往中心收缩,这都很常见,最后的扩散,我第一想到的是中心裁剪掉,也就是用clip方法了,也尝试自己写完看效果还行,然后跑去看作者的,发现作者很高明,通过修改paint的strokewidth来实现,又get到一个方法,不错。

裁剪代码如下,150就是中间那空心的半径,从0开始变大

path.reset();
        path.addCircle(0, 0, 150, Direction.CW);;
        canvas.clipPath(path, Region.Op.DIFFERENCE);

clip用的也不多,所以没研究过Region.Op的几种都啥效果,就看着名字蒙了一个是对的。完事赶紧百度下,找到下边的几个帖子,可以学习下
这里有clip那个属性的用法,看完这个,就赶紧画了个八卦图~~
http://blog.csdn.net/richiezhu/article/details/52956163
这里是介绍op的,感觉说的还不错,图文并茂
http://www.runoob.com/w3cnote/android-tutorial-canvas-api2.html

完整代码如下,简单说明下,因为不知道这种动画的具体要求,是不是小球一直转,等到加载完数据才手动调用方法来执行收缩和扩散动画,我就让它旋转一圈,监听动画结束完事开启收缩动画和扩散动画的,有需求再按照需求来吧,这里就随意了。

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path.Direction;
import android.graphics.Path;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.LinearInterpolator;

public class DotRotate extends View {

    public DotRotate(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    int[] colors = { Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN, Color.GRAY, Color.parseColor("#215980") };
    float startAngle = 0;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint paintBG = new Paint(Paint.ANTI_ALIAS_FLAG);
    Path path=new Path();
    private void initPaint() {
        paint.setStyle(Style.FILL);
        paintBG.setColor(Color.WHITE);
        paintBG.setStyle(Style.STROKE);
    }

    int radius = 0;//原始几个小球的距离中心点的距离
    float radiusFactor = 1;//小球距离中心点的比例,收缩用
    
    int radiusMax;//扩散圆的最大值,用的宽高一半的
    float radiusTrans;//透密空心大小
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width=getMeasuredWidth()/2;
        int height=getMeasuredHeight()/2;
        radius =Math.min(width, height) * 2 / 3;//留点边界,所以就取2/3大小好啦
        radiusMax = (int) Math.sqrt(width*width+ height*height);
    }
    @Override
    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        if (radius == 0) {
            return;
        }
        
        float intervalAngle = (float) (2 * Math.PI / colors.length);
        canvas.save();
        canvas.translate(getWidth() / 2, getHeight()/2);
        
        if (radiusTrans > 0) {//开始中心扩散的时候,就不用画小球拉
            //通过裁剪实现
            path.reset();
            path.addCircle(0, 0, radiusTrans, Direction.CW);;
            canvas.clipPath(path, Region.Op.DIFFERENCE);
            canvas.drawColor(Color.WHITE);
            //或者用下边的代码
//          paintBG.setStrokeWidth(radiusMax-radiusTrans);//画笔的宽就是减去透明部分的其他部分
            //举例说下,比如半径为40,画笔宽为10,那么最终线条是画在40-10/2,和40+10/2,就是说画笔宽度是平分在中心点的两边的,
            //所以下边就是计算中心点的坐标的代码拉
//          canvas.drawCircle(0, 0, radiusTrans+(radiusMax-radiusTrans)/2, paintBG);
        } else {
            canvas.drawColor(Color.WHITE);
            for (int i = 0; i < colors.length; i++) {
                double angle = startAngle + i * intervalAngle;
                float x = (float) (radius * radiusFactor * Math.cos(angle));
                float y = (float) (radius * radiusFactor * Math.sin(angle));
                paint.setColor(colors[i]);
                canvas.drawCircle(x, y, 10, paint);
            }
        }

        canvas.restore();
    }

    ValueAnimator animator;

    public void startAnimation() {
        animator = ValueAnimator.ofFloat(0, (float) (2 * Math.PI));
        animator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                startAngle = (Float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                startShrink();

            }
        });
        animator.setRepeatCount(0);
        animator.setDuration(2000);
        animator.setInterpolator(new LinearInterpolator());
        animator.start();
    }

    private void startShrink() {
        animator = ValueAnimator.ofFloat(1, 0.0f);//小球的半径缩放因子,从最大值到0
        animator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                radiusFactor = (Float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.setDuration(1000);
        animator.setInterpolator(new AnticipateInterpolator());//有一个向外弹的动作,就靠这个加速器的
        animator.start();
        startEnd();
    }

    public void startEnd() {
        animator = ValueAnimator.ofFloat(10,radiusMax);
        animator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                radiusTrans =  (Float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                cancelAnimation();
                setVisibility(GONE);
            }
        });
        animator.setDuration(1000);
        animator.setInterpolator(new AccelerateInterpolator());//用的加速插值器
        animator.setStartDelay(900);
        animator.start();
    }

    public void cancelAnimation() {
        if (animator != null) {
            animator.cancel();
            animator = null;
        }
    }
    
    
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        startAnimation();
    }
    @Override
    protected void onDetachedFromWindow() {
        if (animator != null) {
            animator.cancel();
            animator = null;
        }
        super.onDetachedFromWindow();
    }
}

附上kotlin版本

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.graphics.Paint.ANTI_ALIAS_FLAG
import android.view.animation.AnticipateInterpolator

/**
 * Created by charlie.song on 2018/6/4.
 */
class LoadingView:View{
    constructor(context: Context?) : super(context){
        initPaint()
    }
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs){
        initPaint()
    }
    var colors = intArrayOf(Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN, Color.GRAY, Color.parseColor("#215980"))//6个小球的颜色
    var startAngle = 0f //旋转的角度,弧度表示
    var paint = Paint(ANTI_ALIAS_FLAG) //小球用
    var paintBG = Paint(Paint.ANTI_ALIAS_FLAG)//画背景
    var paintClear=Paint() //裁剪中心用
    private fun initPaint() {
        paint.setStyle(Paint.Style.FILL)
        paintBG.setColor(Color.WHITE)
        paintBG.style=Paint.Style.FILL
        paintClear.setXfermode(PorterDuffXfermode(PorterDuff.Mode.CLEAR))//如果view设置了背景,那么clear的部分就会成灰黑色。
    }

    var radius = 0f//原始几个小球的距离中心点的距离
    var radiusFactor = 1f//小球距离中心点的比例,收缩用
    var clearRadius=0f;//中间透明的半径
    var path=Path()
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        if(changed){
            radius=1f/4*Math.min(width,height)
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if(radius==0f){
            return
        }
        canvas.save()
        canvas.translate(width/2f,height/2f)
        if(clearRadius>0){
            //注释的方法,如果要用,则得确保没有设置background,否则clear的部分就成了黑色了。
//            canvas.drawColor(Color.DKGRAY)
//            canvas.drawCircle(0f,0f,clearRadius,paintClear)
            canvas.clipPath(path.apply {
                reset()
                addCircle(0f,0f,clearRadius,Path.Direction.CW)
            },Region.Op.DIFFERENCE)
            canvas.drawColor(Color.DKGRAY) //需要注意,裁剪的时候先裁剪后绘制,否则无效
        }else{
            canvas.drawColor(Color.DKGRAY)
            //draw 6 balls
            (0 ..5).forEach {
                val r=radius*radiusFactor;
                val angle=Math.PI/180*60*it+startAngle
                val x=r*Math.cos(angle)
                val y=r*Math.sin(angle)
                paint.color=colors[it]
                canvas.drawCircle(x.toFloat(),y.toFloat(),10f,paint)
            }
        }
        canvas.restore()
    }
    private fun resetDefault(){
        startAngle=0f
        radiusFactor=1f
        clearRadius=0f;
    }
    var animaRotate:ValueAnimator?=null
     fun startLoading(){
         resetDefault()
        animaRotate=ValueAnimator.ofFloat(0f,Math.PI.toFloat()*2).apply {
            setDuration(2000L)
            repeatCount=ValueAnimator.INFINITE
            addUpdateListener(object : ValueAnimator.AnimatorUpdateListener{
                override fun onAnimationUpdate(animation: ValueAnimator) {
                    startAngle=animation.animatedValue as Float
                    postInvalidate()
                }
            })
            interpolator=null//改为线性加速,默认是中间加速的
            start()
        }
    }

    fun stopLoading(){
        animaRotate?.cancel()
        ValueAnimator.ofFloat(1f,0f).apply {
            setDuration(500L)
            addUpdateListener( object :ValueAnimator.AnimatorUpdateListener{
                override fun onAnimationUpdate(animation: ValueAnimator) {
                    radiusFactor=animation.animatedValue as Float
                    postInvalidate()
                }
            })
            addListener(object :AnimatorListenerAdapter(){
                override fun onAnimationEnd(animation: Animator?) {
                    cancel()
                    startShowByCircle()
                }
            })
            interpolator=AnticipateInterpolator()
            start()
        }
    }

    private fun startShowByCircle(){
        ValueAnimator.ofFloat(0f, Math.sqrt(width*width/4.0+height*height/4.0).toFloat()).apply {
            setDuration(700L)
            addUpdateListener( object :ValueAnimator.AnimatorUpdateListener{
                override fun onAnimationUpdate(animation: ValueAnimator) {
                    clearRadius=animation.animatedValue as Float
                    postInvalidate()
                }
            })
            start()
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        animaRotate?.cancel()
    }
}

你可能感兴趣的:(雅虎新闻摘要加载)