专栏名称: 安卓开发精选
伯乐在线旗下账号,分享安卓应用相关内容,包括:安卓应用开发、设计和动态等。
目录
相关文章推荐
郭霖  ·  iPhone 到 Android ... ·  1 周前  
郭霖  ·  Now in Android ... ·  1 周前  
stormzhang  ·  打工人可以薅点羊毛​了 ·  6 天前  
51好读  ›  专栏  ›  安卓开发精选

Android 自定义View UC下拉刷新效果(一)

安卓开发精选  · 公众号  · android  · 2016-11-16 21:21

正文

(点击上方公众号,可快速关注)


来源:伯乐在线专栏作者 - joe

链接:http://android.jobbole.com/85135/

点击 → 了解如何加入专栏作者


啦啦啦,今天给大家带来最近弄的CircleProgress相关的效果。这里的效果图可能还看不出是UC浏览器的那个下拉刷新的效果,不过首先还是要说说这个进度条,在下一篇中将实现真正的下拉刷新!


话不多说,直接上图:



progress_静态.png



progress_动态.gif


特点:就是一个进度条


1、可以设置多种颜色。

2、可以显示多种状态(LOADING、SUCCESS、ERROR,其实远不止这几种)

3、可以控制是否显示箭头


相关准备工作


知识点:


1.Canvas里面相关方法

2.drawArc()画圆弧的方法

3.drawPath()画路径的方法

4.属性动画使用


结果的钩钩或者那个叉叉还有那个箭头都是使用drawPath()来完成的。


在onDraw里面对应有四个相关的方法:


1.drawArc(Canvas canvas):画对应的进度

2.drawTriangle(Canvas c, float startAngle, float sweepAngle):画箭头

3.drawHook(Canvas canvas):画钩钩

4.drawError(Canvas canvas):画叉叉


三个动画控制:


两个来控制进度条的 startAngle和sweepAngle,一个用来控制画钩钩或者画叉叉的时候的渐变效果!


相关代码


三支画笔


    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    mPaint.setStyle(Paint.Style.STROKE);

    mPaint.setStrokeCap(Cap.ROUND);

    mPaint.setStrokeWidth(mBorderWidth);

    mPaint.setColor(mColors[mCurrentColorIndex]);

 

    mHookPaint = new Paint(mPaint);

    mArrowPaint = new Paint(mPaint);


三个动画


private void setupAnimations() {

    mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, mAngleProperty, 360f);

    mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);

    mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);

    mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART);

    mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);

 

    mObjectAnimatorSweep = ObjectAnimator.ofFloat(this, mSweepProperty, 360f - MIN_SWEEP_ANGLE * 2);

    mObjectAnimatorSweep.setInterpolator(SWEEP_INTERPOLATOR);

    mObjectAnimatorSweep.setDuration(SWEEP_ANIMATOR_DURATION);

    mObjectAnimatorSweep.setRepeatMode(ValueAnimator.RESTART);

    mObjectAnimatorSweep.setRepeatCount(ValueAnimator.INFINITE);

    mObjectAnimatorSweep.addListener(new SimpleAnimatorListener() {

        @Override

        public void onAnimationRepeat(Animator animation) {

            toggleAppearingMode();

        }

    });

    fractionAnimator = ValueAnimator.ofInt(0, 255);

    fractionAnimator.setInterpolator(ANGLE_INTERPOLATOR);

    fractionAnimator.setDuration(100);

    fractionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        @Override

        public void onAnimationUpdate(ValueAnimator animation) {

            fraction = animation.getAnimatedFraction();

            mHookPaint.setAlpha((Integer) animation.getAnimatedValue());

            invalidate();

        }

    });

}


四个draw相关方法


private void drawError(Canvas canvas) {

    mError.reset();

    mError.moveTo(fBounds.centerX() + fBounds.width() * 0.2f * fraction, fBounds.centerY() - fBounds.height() * 0.2f * fraction);

    mError.lineTo(fBounds.centerX() - fBounds.width() * 0.2f * fraction, fBounds.centerY() + fBounds.height() * 0.2f * fraction);

    mError.moveTo(fBounds.centerX() - fBounds.width() * 0.2f * fraction, fBounds.centerY() - fBounds.height() * 0.2f * fraction);

    mError.lineTo(fBounds.centerX() + fBounds.width() * 0.2f * fraction, fBounds.centerY() + fBounds.height() * 0.2f * fraction);

    mHookPaint.setColor(mColors[3]);

    canvas.drawPath(mError, mHookPaint);

    canvas.drawArc(fBounds, 0, 360, false, mHookPaint);

}

 

private void drawHook(Canvas canvas) {

    mHook.reset();

    mHook.moveTo(fBounds.centerX() - fBounds.width() * 0.25f * fraction, fBounds.centerY());

    mHook.lineTo(fBounds.centerX() - fBounds.width() * 0.1f * fraction, fBounds.centerY() + fBounds.height() * 0.18f * fraction);

    mHook.lineTo(fBounds.centerX() + fBounds.width() * 0.25f * fraction, fBounds.centerY() - fBounds.height() * 0.20f * fraction);

    mHookPaint.setColor(mColors[0]);

    canvas.drawPath(mHook, mHookPaint);

    canvas.drawArc(fBounds, 0, 360, false, mHookPaint);

 

}

 

private void drawArc(Canvas canvas) {

    float startAngle = mCurrentGlobalAngle - mCurrentGlobalAngleOffset;

    float sweepAngle = mCurrentSweepAngle;

    if (mModeAppearing) {

        mPaint.setColor(gradient(mColors[mCurrentColorIndex], mColors[mNextColorIndex],

                mCurrentSweepAngle / (360 - MIN_SWEEP_ANGLE * 2)));

        sweepAngle += MIN_SWEEP_ANGLE;

    } else {

        startAngle = startAngle + sweepAngle;

        sweepAngle = 360 - sweepAngle - MIN_SWEEP_ANGLE;

    }

    canvas.drawArc(fBounds, startAngle, sweepAngle, false, mPaint);

    if (showArrow) {

        drawTriangle(canvas, startAngle, sweepAngle);

    }

}

 

public void drawTriangle(Canvas c, float startAngle, float sweepAngle) {

    if (mArrow == null) {

        mArrow = new Path();

        mArrow.setFillType(Path.FillType.EVEN_ODD);

    } else {

        mArrow.reset();

    }

 

    float x = (float) (mRingCenterRadius * Math.cos(0) + fBounds.centerX());

    float y = (float) (mRingCenterRadius * Math.sin(0) + fBounds.centerY());

    mArrow.moveTo(0, 0);

    mArrow.lineTo(ARROW_WIDTH * mArrowScale, 0);

    mArrow.lineTo((ARROW_WIDTH * mArrowScale / 2), (ARROW_HEIGHT

            * mArrowScale));

    mArrow.offset(x, y);

    mArrow.close();

    c.rotate(startAngle + sweepAngle, fBounds.centerX(),

            fBounds.centerY());

    c.drawPath(mArrow, mPaint);

}


上面的代码就是相关核心的方法了,其实对应的进度条效果就是控制 startAngle和sweepAngle这两个对应的字段,然后不断的调用drawArc()方法。


对于画钩钩或者画叉叉,就是一个ValueAnimator,通过百分比控制缩放和画笔的透明度。


对于drawTriangle()方法,如果你觉得很眼熟的话也很正常,其实这个就是在SwipeRefreshLayout里面抄过来的。。。。。


一开始,我很纠结这个箭头怎么才能跟着进度条一起旋转,自己写的也是有各种问题,另外mArrowScale这个参数在里面其实没有使用的。

如果没有offset偏移量,那么那个path肯定是画在左上角的。


x=mRingCenterRadius +fBounds.centerX();

y=fBounds.centerY();


通过这个一设置,这个path其实就到了右边的中间靠着圆弧的内侧一点去了(因为这里的半径减去了圆弧自己的宽度。。),这么一来,再根据相关的角度旋转角度,就有一种跟着进度条一直转的效果了!


对于drawArc()方法,主要是控制startAngle和sweepAngle这两个变量,mCurrentGlobalAngle的变化范围是(0~360),而mCurrentSweepAngle的变化范围是(0~360f – MIN_SWEEP_ANGLE * 2),为什么要减去两个最小值呢?因为sweepAngle总会加一个或者总会减去一个最小值,所以最小间距还是MIN_SWEEP_ANGLE。


至于什么时候加什么时候减呢?这里有一个变量值mModeAppearing提供记录!那就是当mObjectAnimatorSweep的动画重复的时候,就需要切换一下了。


mObjectAnimatorSweep.addListener(new SimpleAnimatorListener() {

        @Override

        public void onAnimationRepeat(Animator animation) {

            toggleAppearingMode();

        }

    });

private void toggleAppearingMode() {

    mModeAppearing = !mModeAppearing;

    if (mModeAppearing) {

        mCurrentColorIndex = ++mCurrentColorIndex % 4;

        mNextColorIndex = ++mNextColorIndex % 4;

        mCurrentGlobalAngleOffset = (mCurrentGlobalAngleOffset + MIN_SWEEP_ANGLE * 2) % 360;

    }

}


最终效果图





下一篇介绍剩余的下拉刷新部分,还有就是两个圆圈的过度效果。。


相关的代码请移步 我的github。。。

  • https://github.com/lovejjfg/Circle


专栏作者简介( 点击 → 加入专栏作者 


joe:90后程序猿。。

打赏支持作者写出更多好文章,谢谢!



 关注「安卓开发精选

看更多精选安卓技术文章
↓↓↓