Android加载动画系列——BalloonLoading
2016-06-05 22:56
525 查看
Android加载动画系列——BalloonLoading
已经好久没有写博客了呢~,下文的源码并非小编原创,而是来自github上的一位大神,小编只是偶然在微博上看到了一篇文章,小编被gif中华丽的效果吸引啦!小编想自己动手敲一敲,做个笔记什么的,也方便以后万一遇到这样的需求就不需要到处去找资源,在此小编奉上原文地址:https://github.com/dinuscxj/LoadingDrawable
小编本着利人利己的原则来写这篇文章,恳请各位读者老爷不要鄙视小编(可怜状~)。
让我们先来看看效果图:
小编对Paint的使用不熟,所以只能简单的把源码贴出来供大家阅读。
1、LoadingDrawable.java源码如下:
public class LoadingDrawableextends
Drawable implementsAnimatable {
private LoadingRenderermLoadingRender;
private final Callback
mCallback= newCallback() {
@Override
public void invalidateDrawable(Drawabled) {
invalidateSelf();
}
@Override
public void scheduleDrawable(Drawabled, Runnable what,long
when) {
scheduleSelf(what, when);
}
@Override
public void unscheduleDrawable(Drawabled, Runnable what) {
unscheduleSelf(what);
}
};
public LoadingDrawable(LoadingRendererloadingRender) {
this.mLoadingRender= loadingRender;
this.mLoadingRender.setCallback(mCallback);
}
@Override
public void draw(Canvascanvas) {
mLoadingRender.draw(canvas,getBounds());
}
@Override
public void setAlpha(intalpha) {
mLoadingRender.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFiltercf) {
mLoadingRender.setColorFilter(cf);
}
@Override
public int getOpacity(){
return PixelFormat.TRANSLUCENT;
}
@Override
public void start() {
mLoadingRender.start();
}
@Override
public void stop() {
mLoadingRender.stop();
}
@Override
public boolean isRunning(){
return mLoadingRender.isRunning();
}
@Override
public int getIntrinsicHeight(){
return (int) (mLoadingRender.getHeight()+1);
}
@Override
public int getIntrinsicWidth(){
return (int) (mLoadingRender.getWidth()+1);
}
}
2、抽象类LoadingRenderer源码如下:
3、核心的BalloonLoadingRenderer源码如下:
4、接下来就是如何使用的问题,首先我们需要在layout中定一个Imageview,如下所示:
5、然后在相关的Activity中实现动画的播放和停止,使用事例如下:
6、最后小编双手奉上源码的下载地址:http://download.csdn.net/detail/zhimingshangyan/9542044
7、能够理解的大神麻烦给我讲讲实现的原理,谢谢~
已经好久没有写博客了呢~,下文的源码并非小编原创,而是来自github上的一位大神,小编只是偶然在微博上看到了一篇文章,小编被gif中华丽的效果吸引啦!小编想自己动手敲一敲,做个笔记什么的,也方便以后万一遇到这样的需求就不需要到处去找资源,在此小编奉上原文地址:https://github.com/dinuscxj/LoadingDrawable
小编本着利人利己的原则来写这篇文章,恳请各位读者老爷不要鄙视小编(可怜状~)。
让我们先来看看效果图:
小编对Paint的使用不熟,所以只能简单的把源码贴出来供大家阅读。
1、LoadingDrawable.java源码如下:
public class LoadingDrawableextends
Drawable implementsAnimatable {
private LoadingRenderermLoadingRender;
private final Callback
mCallback= newCallback() {
@Override
public void invalidateDrawable(Drawabled) {
invalidateSelf();
}
@Override
public void scheduleDrawable(Drawabled, Runnable what,long
when) {
scheduleSelf(what, when);
}
@Override
public void unscheduleDrawable(Drawabled, Runnable what) {
unscheduleSelf(what);
}
};
public LoadingDrawable(LoadingRendererloadingRender) {
this.mLoadingRender= loadingRender;
this.mLoadingRender.setCallback(mCallback);
}
@Override
public void draw(Canvascanvas) {
mLoadingRender.draw(canvas,getBounds());
}
@Override
public void setAlpha(intalpha) {
mLoadingRender.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFiltercf) {
mLoadingRender.setColorFilter(cf);
}
@Override
public int getOpacity(){
return PixelFormat.TRANSLUCENT;
}
@Override
public void start() {
mLoadingRender.start();
}
@Override
public void stop() {
mLoadingRender.stop();
}
@Override
public boolean isRunning(){
return mLoadingRender.isRunning();
}
@Override
public int getIntrinsicHeight(){
return (int) (mLoadingRender.getHeight()+1);
}
@Override
public int getIntrinsicWidth(){
return (int) (mLoadingRender.getWidth()+1);
}
}
2、抽象类LoadingRenderer源码如下:
public abstract class LoadingRenderer { private static final long ANIMATION_DURATION = 1333; private static final float DEFAULT_SIZE = 56.0f; private static final float DEFAULT_CENTER_RADIUS = 12.5f; private static final float DEFAULT_STROKE_WIDTH = 2.5f; protected float mWidth; protected float mHeight; protected float mStrokeWidth; protected float mCenterRadius; private long mDuration; private Drawable.Callback mCallback; private ValueAnimator mRenderAnimator; public LoadingRenderer(Context context) { setupDefaultParams(context); setupAnimators(); } public abstract void draw(Canvas canvas, Rect bounds); public abstract void computeRender(float renderProgress); public abstract void setAlpha(int alpha); public abstract void setColorFilter(ColorFilter cf); public abstract void reset(); public void start() { reset(); setDuration(mDuration); mRenderAnimator.start(); } public void stop() { mRenderAnimator.cancel(); } public boolean isRunning() { return mRenderAnimator.isRunning(); } public void setCallback(Drawable.Callback callback) { this.mCallback = callback; } protected void invalidateSelf() { mCallback.invalidateDrawable(null); } private void setupDefaultParams(Context context) { final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); final float screenDensity = metrics.density; mWidth = DEFAULT_SIZE * screenDensity; mHeight = DEFAULT_SIZE * screenDensity; mStrokeWidth = DEFAULT_STROKE_WIDTH * screenDensity; mCenterRadius = DEFAULT_CENTER_RADIUS * screenDensity; mDuration = ANIMATION_DURATION; } private void setupAnimators() { mRenderAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); mRenderAnimator.setRepeatCount(Animation.INFINITE); mRenderAnimator.setRepeatMode(Animation.RESTART); //fuck you! the default interpolator is AccelerateDecelerateInterpolator mRenderAnimator.setInterpolator(new LinearInterpolator()); mRenderAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { computeRender((float) animation.getAnimatedValue()); invalidateSelf(); } }); } protected void addRenderListener(Animator.AnimatorListener animatorListener) { mRenderAnimator.addListener(animatorListener); } public void setCenterRadius(float centerRadius) { mCenterRadius = centerRadius; } public float getCenterRadius() { return mCenterRadius; } public void setStrokeWidth(float strokeWidth) { mStrokeWidth = strokeWidth; } public float getStrokeWidth() { return mStrokeWidth; } public float getWidth() { return mWidth; } public void setWidth(float width) { this.mWidth = width; } public float getHeight() { return mHeight; } public void setHeight(float height) { this.mHeight = height; } public long getDuration() { return mDuration; } public void setDuration(long duration) { this.mDuration = duration; mRenderAnimator.setDuration(mDuration); } }
3、核心的BalloonLoadingRenderer源码如下:
public class BalloonLoadingRenderer extends LoadingRenderer { private static final String PERCENT_SIGN = "%"; private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator(); private static final float START_INHALE_DURATION_OFFSET = 0.4f; private static final float DEFAULT_WIDTH = 200.0f; private static final float DEFAULT_HEIGHT = 150.0f; private static final float DEFAULT_STROKE_WIDTH = 2.0f; private static final float DEFAULT_GAS_TUBE_WIDTH = 48; private static final float DEFAULT_GAS_TUBE_HEIGHT = 20; private static final float DEFAULT_CANNULA_WIDTH = 13; private static final float DEFAULT_CANNULA_HEIGHT = 37; private static final float DEFAULT_CANNULA_OFFSET_Y = 3; private static final float DEFAULT_CANNULA_MAX_OFFSET_Y = 15; private static final float DEFAULT_PIPE_BODY_WIDTH = 16; private static final float DEFAULT_PIPE_BODY_HEIGHT = 36; private static final float DEFAULT_BALLOON_WIDTH = 38; private static final float DEFAULT_BALLOON_HEIGHT = 48; private static final float DEFAULT_RECT_CORNER_RADIUS = 2; private static final int DEFAULT_BALLOON_COLOR = Color.parseColor("#ffF3C211"); private static final int DEFAULT_GAS_TUBE_COLOR = Color.parseColor("#ff174469"); private static final int DEFAULT_PIPE_BODY_COLOR = Color.parseColor("#aa2369B1"); private static final int DEFAULT_CANNULA_COLOR = Color.parseColor("#ff174469"); private static final float DEFAULT_TEXT_SIZE = 7.0f; private static final long ANIMATION_DURATION = 3333; private final Paint mPaint = new Paint(); private final RectF mCurrentBounds = new RectF(); private final RectF mGasTubeBounds = new RectF(); private final RectF mPipeBodyBounds = new RectF(); private final RectF mCannulaBounds = new RectF(); private final RectF mBalloonBounds = new RectF(); private final Rect mProgressBounds = new Rect(); private float mTextSize; private float mProgress; private String mProgressText; private float mGasTubeWidth; private float mGasTubeHeight; private float mCannulaWidth; private float mCannulaHeight; private float mCannulaMaxOffsetY; private float mCannulaOffsetY; private float mPipeBodyWidth; private float mPipeBodyHeight; private float mBalloonWidth; private float mBalloonHeight; private float mRectCornerRadius; private int mBalloonColor; private int mGasTubeColor; private int mCannulaColor; private int mPipeBodyColor; public BalloonLoadingRenderer(Context context) { super(context); init(context); setupPaint(); } private void init(Context context) { final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); final float screenDensity = metrics.density; mTextSize = DEFAULT_TEXT_SIZE * screenDensity; mWidth = DEFAULT_WIDTH * screenDensity; mHeight = DEFAULT_HEIGHT * screenDensity; mStrokeWidth = DEFAULT_STROKE_WIDTH * screenDensity; mGasTubeWidth = DEFAULT_GAS_TUBE_WIDTH * screenDensity; mGasTubeHeight = DEFAULT_GAS_TUBE_HEIGHT * screenDensity; mCannulaWidth = DEFAULT_CANNULA_WIDTH * screenDensity; mCannulaHeight = DEFAULT_CANNULA_HEIGHT * screenDensity; mCannulaOffsetY = DEFAULT_CANNULA_OFFSET_Y * screenDensity; mCannulaMaxOffsetY = DEFAULT_CANNULA_MAX_OFFSET_Y * screenDensity; mPipeBodyWidth = DEFAULT_PIPE_BODY_WIDTH * screenDensity; mPipeBodyHeight = DEFAULT_PIPE_BODY_HEIGHT * screenDensity; mBalloonWidth = DEFAULT_BALLOON_WIDTH * screenDensity; mBalloonHeight = DEFAULT_BALLOON_HEIGHT * screenDensity; mRectCornerRadius = DEFAULT_RECT_CORNER_RADIUS * screenDensity; mBalloonColor = DEFAULT_BALLOON_COLOR; mGasTubeColor = DEFAULT_GAS_TUBE_COLOR; mCannulaColor = DEFAULT_CANNULA_COLOR; mPipeBodyColor = DEFAULT_PIPE_BODY_COLOR; setDuration(ANIMATION_DURATION); } private void setupPaint() { mPaint.setAntiAlias(true); mPaint.setStrokeWidth(getStrokeWidth()); } @Override public void draw(Canvas canvas, Rect bounds) { int saveCount = canvas.save(); RectF arcBounds = mCurrentBounds; arcBounds.set(bounds); //draw draw gas tube mPaint.setColor(mGasTubeColor); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(getStrokeWidth()); canvas.drawPath(createGasTubePath(mGasTubeBounds), mPaint); //draw balloon mPaint.setColor(mBalloonColor); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawPath(createBalloonPath(mBalloonBounds, mProgress), mPaint); //draw progress mPaint.setColor(mGasTubeColor); mPaint.setTextSize(mTextSize); mPaint.setStrokeWidth(getStrokeWidth() / 5.0f); canvas.drawText(mProgressText, arcBounds.centerX() - mProgressBounds.width() / 2.0f, mGasTubeBounds.centerY() + mProgressBounds.height() / 2.0f, mPaint); //draw cannula mPaint.setColor(mCannulaColor); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(getStrokeWidth()); canvas.drawPath(createCannulaHeadPath(mCannulaBounds), mPaint); mPaint.setStyle(Paint.Style.FILL); canvas.drawPath(createCannulaBottomPath(mCannulaBounds), mPaint); //draw pipe body mPaint.setColor(mPipeBodyColor); mPaint.setStyle(Paint.Style.FILL); canvas.drawRoundRect(mPipeBodyBounds, mRectCornerRadius, mRectCornerRadius, mPaint); canvas.restoreToCount(saveCount); } @Override public void computeRender(float renderProgress) { RectF arcBounds = mCurrentBounds; //compute gas tube bounds mGasTubeBounds.set(arcBounds.centerX() - mGasTubeWidth / 2.0f, arcBounds.centerY(), arcBounds.centerX() + mGasTubeWidth / 2.0f, arcBounds.centerY() + mGasTubeHeight); //compute pipe body bounds mPipeBodyBounds.set(arcBounds.centerX() + mGasTubeWidth / 2.0f - mPipeBodyWidth / 2.0f, arcBounds.centerY() - mPipeBodyHeight, arcBounds.centerX() + mGasTubeWidth / 2.0f + mPipeBodyWidth / 2.0f, arcBounds.centerY()); //compute cannula bounds mCannulaBounds.set(arcBounds.centerX() + mGasTubeWidth / 2.0f - mCannulaWidth / 2.0f, arcBounds.centerY() - mCannulaHeight - mCannulaOffsetY, arcBounds.centerX() + mGasTubeWidth / 2.0f + mCannulaWidth / 2.0f, arcBounds.centerY() - mCannulaOffsetY); //compute balloon bounds float insetX = mBalloonWidth * 0.333f * (1 - mProgress); float insetY = mBalloonHeight * 0.667f * (1 - mProgress); mBalloonBounds.set(arcBounds.centerX() - mGasTubeWidth / 2.0f - mBalloonWidth / 2.0f + insetX, arcBounds.centerY() - mBalloonHeight + insetY, arcBounds.centerX() - mGasTubeWidth / 2.0f + mBalloonWidth / 2.0f - insetX, arcBounds.centerY()); if (renderProgress <= START_INHALE_DURATION_OFFSET) { mCannulaBounds.offset(0, -mCannulaMaxOffsetY * renderProgress / START_INHALE_DURATION_OFFSET); mProgress = 0.0f; mProgressText = 0 + PERCENT_SIGN; mPaint.setTextSize(mTextSize); mPaint.getTextBounds(mProgressText, 0, mProgressText.length(), mProgressBounds); } else { float exhaleProgress = ACCELERATE_INTERPOLATOR.getInterpolation(1.0f - (renderProgress - START_INHALE_DURATION_OFFSET) / (1.0f - START_INHALE_DURATION_OFFSET)); mCannulaBounds.offset(0, -mCannulaMaxOffsetY * exhaleProgress); mProgress = 1.0f - exhaleProgress; mProgressText = adjustProgress((int) (exhaleProgress * 100.0f)) + PERCENT_SIGN; mPaint.setTextSize(mTextSize); mPaint.getTextBounds(mProgressText, 0, mProgressText.length(), mProgressBounds); } } private int adjustProgress(int progress) { progress = progress / 10 * 10; progress = 100 - progress + 10; if (progress > 100) { progress = 100; } return progress; } private Path createGasTubePath(RectF gasTubeRect) { Path path = new Path(); path.moveTo(gasTubeRect.left, gasTubeRect.top); path.lineTo(gasTubeRect.left, gasTubeRect.bottom); path.lineTo(gasTubeRect.right, gasTubeRect.bottom); path.lineTo(gasTubeRect.right, gasTubeRect.top); return path; } private Path createCannulaHeadPath(RectF cannulaRect) { Path path = new Path(); path.moveTo(cannulaRect.left, cannulaRect.top); path.lineTo(cannulaRect.right, cannulaRect.top); path.moveTo(cannulaRect.centerX(), cannulaRect.top); path.lineTo(cannulaRect.centerX(), cannulaRect.bottom - 0.833f * cannulaRect.width()); return path; } private Path createCannulaBottomPath(RectF cannulaRect) { RectF cannulaHeadRect = new RectF(cannulaRect.left, cannulaRect.bottom - 0.833f * cannulaRect.width(), cannulaRect.right, cannulaRect.bottom); Path path = new Path(); path.addRoundRect(cannulaHeadRect, mRectCornerRadius, mRectCornerRadius, Path.Direction.CCW); return path; } /** * Coordinates are approximate, you have better cooperate with the designer's design draft */ private Path createBalloonPath(RectF balloonRect, float progress) { Path path = new Path(); path.moveTo(balloonRect.centerX(), balloonRect.bottom); float progressWidth = balloonRect.width() * progress; float progressHeight = balloonRect.height() * progress; //draw left half float leftIncrementX1 = progressWidth * -0.48f; float leftIncrementY1 = progressHeight * 0.75f; float leftIncrementX2 = progressWidth * -0.03f; float leftIncrementY2 = progressHeight * -1.6f; float leftIncrementX3 = progressWidth * 0.9f; float leftIncrementY3 = progressHeight * -1.0f; path.cubicTo(balloonRect.left + balloonRect.width() * 0.25f + leftIncrementX1, balloonRect.centerY() - balloonRect.height() * 0.4f + leftIncrementY1, balloonRect.left - balloonRect.width() * 0.20f + leftIncrementX2, balloonRect.centerY() + balloonRect.height() * 1.15f + leftIncrementY2, balloonRect.left - balloonRect.width() * 0.4f + leftIncrementX3, balloonRect.bottom + leftIncrementY3); // the results of the left final transformation // path.cubicTo(balloonRect.left - balloonRect.width() * 0.13f, balloonRect.centerY() + balloonRect.height() * 0.35f, // balloonRect.left - balloonRect.width() * 0.23f, balloonRect.centerY() - balloonRect.height() * 0.45f, // balloonRect.left + balloonRect.width() * 0.5f, balloonRect.bottom - balloonRect.height()); //draw right half float rightIncrementX1 = progressWidth * 1.51f; float rightIncrementY1 = progressHeight * -0.05f; float rightIncrementX2 = progressWidth * 0.03f; float rightIncrementY2 = progressHeight * 0.5f; float rightIncrementX3 = 0.0f; float rightIncrementY3 = 0.0f; path.cubicTo(balloonRect.left - balloonRect.width() * 0.38f + rightIncrementX1, balloonRect.centerY() - balloonRect.height() * 0.4f + rightIncrementY1, balloonRect.left + balloonRect.width() * 1.1f + rightIncrementX2, balloonRect.centerY() - balloonRect.height() * 0.15f + rightIncrementY2, balloonRect.left + balloonRect.width() * 0.5f + rightIncrementX3, balloonRect.bottom + rightIncrementY3); // the results of the right final transformation // path.cubicTo(balloonRect.left + balloonRect.width() * 1.23f, balloonRect.centerY() - balloonRect.height() * 0.45f, // balloonRect.left + balloonRect.width() * 1.13f, balloonRect.centerY() + balloonRect.height() * 0.35f, // balloonRect.left + balloonRect.width() * 0.5f, balloonRect.bottom); return path; } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); invalidateSelf(); } @Override public void setColorFilter(ColorFilter cf) { mPaint.setColorFilter(cf); invalidateSelf(); } @Override public void setStrokeWidth(float strokeWidth) { super.setStrokeWidth(strokeWidth); mPaint.setStrokeWidth(strokeWidth); invalidateSelf(); } @Override public void reset() { } }
4、接下来就是如何使用的问题,首先我们需要在layout中定一个Imageview,如下所示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/balloon_view" android:background="#ffd4d9da" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
5、然后在相关的Activity中实现动画的播放和停止,使用事例如下:
private LoadingDrawable mBalloonDrawable; private ImageView mIvBalloon; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mIvBalloon = (ImageView) findViewById(R.id.balloon_view); mBalloonDrawable = new LoadingDrawable(new BalloonLoadingRenderer(this)); mIvBalloon.setImageDrawable(mBalloonDrawable); } @Override protected void onStop() { super.onStop(); mBalloonDrawable.stop(); } @Override protected void onStart() { super.onStart(); mBalloonDrawable.start(); }
6、最后小编双手奉上源码的下载地址:http://download.csdn.net/detail/zhimingshangyan/9542044
7、能够理解的大神麻烦给我讲讲实现的原理,谢谢~
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories