1.在Android的网路请求时,经常会遇到需要重写加载样式的定义。有时候为了抓住客户的眼球,需要实现一些炫酷的效果。首先我们先看下效果图吧
2.下面看下具体的实现步骤了
(1).我们是在Android Studio中实现开发的
首先新建module,实现加载样式的lib包
<1>.翻书的效果实现代码
PageView.java类的实现代码
public class PageView extends View {
private Paint paint;
private Path path;
private int width;
private int height;
private float padding;
private int border;
public PageView(Context context) {
super(context);
initView();
}
public PageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public PageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
padding = getResources().getDimension(R.dimen.book_padding);
border = getResources().getDimensionPixelOffset(R.dimen.book_border);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(getResources().getDimension(R.dimen.page_border));
path = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(getResources().getColor(R.color.book_loading_book));
paint.setStyle(Paint.Style.STROKE);
float offset = border / 4;
path.moveTo(width / 2, padding + offset);
path.lineTo(width - padding - offset, padding + offset);
path.lineTo(width - padding - offset, height - padding - offset);
path.lineTo(width / 2, height - padding - offset);
canvas.drawPath(path, paint);
paint.setColor(getResources().getColor(R.color.book_loading_page));
paint.setStyle(Paint.Style.FILL);
offset = border / 2;
canvas.drawRect(width / 2, padding + offset, width - padding - offset, height - padding - offset, paint);
}
}
BookView.java的代码实现
public class BookView extends View {
private Paint paint;
private int width;
private int height;
public BookView(Context context) {
super(context);
initView();
}
public BookView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public BookView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
private void initView() {
paint = new Paint();
paint.setColor(getResources().getColor(R.color.book_loading_book));
paint.setStrokeWidth(getResources().getDimension(R.dimen.book_border));
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0, 0, width, height, paint);
}
}
BookLoading.java的代码实现
public class BookLoading extends FrameLayout {
private static final long DURATION = 1000;
private static final int PAGE_NUM = 5;
private static final int DELAYED = 200;
private ArrayList<PageView> pageViews;
private BookHandler bookHandler;
private boolean isStart;
public BookLoading(Context context) {
super(context);
initView(context);
}
public BookLoading(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public BookLoading(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.book_loading, this, true);
pageViews = new ArrayList<>();
bookHandler=new BookHandler(this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
addPage();
}
private void addPage() {
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
for (int i = 0; i < PAGE_NUM; i++) {
PageView pageView = new PageView(getContext());
addView(pageView, params);
pageView.setTag(R.string.app_name, i);
pageViews.add(pageView);
}
}
private void playAnim() {
setAnim(pageViews.get(PAGE_NUM - 1), DELAYED);
setAnim(pageViews.get(PAGE_NUM - 1), DURATION + DELAYED);
setAnim(pageViews.get(PAGE_NUM - 2), DURATION + DELAYED * 2);
for (int i = PAGE_NUM - 1; i >= 0; i--) {
setAnim(pageViews.get(i), DURATION * 3 + (PAGE_NUM - 1 - i) * DELAYED / 2);
}
}
private void setAnim(final View view, long delay) {
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 0.0F, -180.0F);
animator.setDuration(DURATION);
animator.setStartDelay(delay);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
boolean change = false;
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (animation.getCurrentPlayTime() > DURATION / 2 && !change) {
change = true;
view.bringToFront();
}
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
if ((int) view.getTag(R.string.app_name) == PAGE_NUM - 1) {
view.bringToFront();
}
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
public void start(){
isStart=true;
bookHandler.obtainMessage().sendToTarget();
}
public void stop(){
isStart=false;
bookHandler.removeCallbacks(null);
bookHandler.removeCallbacksAndMessages(null);
}
public boolean isStart(){
return isStart;
}
static class BookHandler extends Handler{
private WeakReference<BookLoading> weakReference;
public BookHandler(BookLoading bookLoading){
weakReference=new WeakReference<>(bookLoading);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
BookLoading bookLoading=weakReference.get();
if (null==bookLoading)
return;
bookLoading.playAnim();
Message message=obtainMessage();
sendMessageDelayed(message,DURATION*5);
}
}
}
<2>.摆钟的动画实现
CradleBall.java的代码实现
public class CradleBall extends View {
private int width;
private int height;
private Paint paint;
public CradleBall(Context context) {
super(context);
initView();
}
public CradleBall(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public CradleBall(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(width / 2, height / 2, width / 2, paint);
}
}
NewtonCradleLoading.java的代码实现
public class NewtonCradleLoading extends LinearLayout {
private CradleBall cradleBallOne;
private CradleBall cradleBallTwo;
private CradleBall cradleBallThree;
private CradleBall cradleBallFour;
private CradleBall cradleBallFive;
private static final int DURATION = 400;
private static final int SHAKE_DISTANCE = 2;
private static final float PIVOT_X = 0.5f;
private static final float PIVOT_Y = -3f;
private static final int DEGREE = 30;
private boolean isStart = false;
public NewtonCradleLoading(Context context) {
super(context);
initView(context);
}
public NewtonCradleLoading(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public NewtonCradleLoading(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.newton_cradle_loading, this, true);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
cradleBallOne = (CradleBall) findViewById(R.id.ball_one);
cradleBallTwo = (CradleBall) findViewById(R.id.ball_two);
cradleBallThree = (CradleBall) findViewById(R.id.ball_three);
cradleBallFour = (CradleBall) findViewById(R.id.ball_four);
cradleBallFive = (CradleBall) findViewById(R.id.ball_five);
initAnim();
}
RotateAnimation rotateLeftAnimation;//cradleBallOne left to right
RotateAnimation rotateRightAnimation;//cradleBallFive right to left
TranslateAnimation shakeLeftAnimation;
TranslateAnimation shakeRightAnimation;
private void initAnim() {
rotateRightAnimation = new RotateAnimation(0, -DEGREE, RotateAnimation.RELATIVE_TO_SELF, PIVOT_X, RotateAnimation.RELATIVE_TO_SELF, PIVOT_Y);
rotateRightAnimation.setRepeatCount(1);
rotateRightAnimation.setRepeatMode(Animation.REVERSE);
rotateRightAnimation.setDuration(DURATION);
rotateRightAnimation.setInterpolator(new LinearInterpolator());
rotateRightAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (isStart)
startRightAnim();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
shakeLeftAnimation = new TranslateAnimation(0, SHAKE_DISTANCE, 0, 0);
shakeLeftAnimation.setDuration(DURATION);
shakeLeftAnimation.setInterpolator(new CycleInterpolator(2));
rotateLeftAnimation = new RotateAnimation(0, DEGREE, RotateAnimation.RELATIVE_TO_SELF, PIVOT_X, RotateAnimation.RELATIVE_TO_SELF, PIVOT_Y);
rotateLeftAnimation.setRepeatCount(1);
rotateLeftAnimation.setRepeatMode(Animation.REVERSE);
rotateLeftAnimation.setDuration(DURATION);
rotateLeftAnimation.setInterpolator(new LinearInterpolator());
rotateLeftAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (isStart) {
cradleBallTwo.startAnimation(shakeLeftAnimation);
cradleBallThree.startAnimation(shakeLeftAnimation);
cradleBallFour.startAnimation(shakeLeftAnimation);
cradleBallFive.startAnimation(rotateRightAnimation);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
shakeRightAnimation = new TranslateAnimation(0, -SHAKE_DISTANCE, 0, 0);
shakeRightAnimation.setDuration(DURATION);
shakeRightAnimation.setInterpolator(new CycleInterpolator(2));
shakeRightAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
if (isStart)
startLeftAnim();
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
private void startLeftAnim() {
cradleBallOne.startAnimation(rotateLeftAnimation);
}
private void startRightAnim() {
cradleBallTwo.startAnimation(shakeRightAnimation);
cradleBallThree.startAnimation(shakeRightAnimation);
cradleBallFour.startAnimation(shakeRightAnimation);
}
public void start() {
if (!isStart) {
isStart = true;
startLeftAnim();
}
}
public void stop() {
isStart = false;
cradleBallOne.clearAnimation();
cradleBallTwo.clearAnimation();
cradleBallThree.clearAnimation();
cradleBallFour.clearAnimation();
cradleBallFive.clearAnimation();
}
public boolean isStart() {
return isStart;
}
}
<3>.圆圈转动的动画效果实现
RotateLoading.java的代码实现
public class RotateLoading extends View {
private static final int DEFAULT_WIDTH = 6;
private static final int DEFAULT_SHADOW_POSITION = 2;
private Paint mPaint;
private RectF loadingRectF;
private RectF shadowRectF;
private int topDegree = 10;
private int bottomDegree = 190;
private float arc;
private int width;
private boolean changeBigger = true;
private int shadowPosition;
private boolean isStart = false;
public RotateLoading(Context context) {
super(context);
initView(context, null);
}
public RotateLoading(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context, attrs);
}
public RotateLoading(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs);
}
private void initView(Context context, AttributeSet attrs) {
int color = Color.WHITE;
width = dpToPx(context, DEFAULT_WIDTH);
shadowPosition = dpToPx(getContext(), DEFAULT_SHADOW_POSITION);
if (null != attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RotateLoading);
color = typedArray.getColor(R.styleable.RotateLoading_loading_color, Color.WHITE);
width = typedArray.getDimensionPixelSize(R.styleable.RotateLoading_loading_width, dpToPx(context, DEFAULT_WIDTH));
shadowPosition = typedArray.getInt(R.styleable.RotateLoading_shadow_position, DEFAULT_SHADOW_POSITION);
typedArray.recycle();
}
mPaint = new Paint();
mPaint.setColor(color);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(width);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
arc = 10;
loadingRectF = new RectF(2 * width, 2 * width, w - 2 * width, h - 2 * width);
shadowRectF = new RectF(2 * width + shadowPosition, 2 * width + shadowPosition, w - 2 * width + shadowPosition, h - 2 * width + shadowPosition);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isStart) {
return;
}
mPaint.setColor(Color.parseColor("#1a000000"));
canvas.drawArc(shadowRectF, topDegree, arc, false, mPaint);
canvas.drawArc(shadowRectF, bottomDegree, arc, false, mPaint);
mPaint.setColor(Color.WHITE);
canvas.drawArc(loadingRectF, topDegree, arc, false, mPaint);
canvas.drawArc(loadingRectF, bottomDegree, arc, false, mPaint);
topDegree += 10;
bottomDegree += 10;
if (topDegree > 360) {
topDegree = topDegree - 360;
}
if (bottomDegree > 360) {
bottomDegree = bottomDegree - 360;
}
if (changeBigger) {
if (arc < 160) {
arc += 2.5;
invalidate();
}
} else {
if (arc > 10) {
arc -= 5;
invalidate();
}
}
if (arc == 160 || arc == 10) {
changeBigger = !changeBigger;
invalidate();
}
}
public void start() {
startAnimator();
isStart = true;
invalidate();
}
public void stop() {
stopAnimator();
invalidate();
}
public boolean isStart() {
return isStart;
}
private void startAnimator() {
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(this, "scaleX", 0.0f, 1);
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(this, "scaleY", 0.0f, 1);
scaleXAnimator.setDuration(300);
scaleXAnimator.setInterpolator(new LinearInterpolator());
scaleYAnimator.setDuration(300);
scaleYAnimator.setInterpolator(new LinearInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(scaleXAnimator, scaleYAnimator);
animatorSet.start();
}
private void stopAnimator() {
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(this, "scaleX", 1, 0);
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(this, "scaleY", 1, 0);
scaleXAnimator.setDuration(300);
scaleXAnimator.setInterpolator(new LinearInterpolator());
scaleYAnimator.setDuration(300);
scaleYAnimator.setInterpolator(new LinearInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(scaleXAnimator, scaleYAnimator);
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
isStart = false;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animatorSet.start();
}
public int dpToPx(Context context, float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics());
}
}
3.需要的资源文件
layout下布局文件
book_loading.xml布局文件实现
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/book_padding"
android:background="@color/book_loading_background">
<com.victor.loading.book.BookView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
newton_cradle_loading.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.victor.loading.newton.CradleBall
android:id="@+id/ball_one"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:layout_marginLeft="50dp" />
<com.victor.loading.newton.CradleBall
android:id="@+id/ball_two"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center" />
<com.victor.loading.newton.CradleBall
android:id="@+id/ball_three"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center" />
<com.victor.loading.newton.CradleBall
android:id="@+id/ball_four"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center" />
<com.victor.loading.newton.CradleBall
android:id="@+id/ball_five"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:layout_marginRight="50dp" />
</LinearLayout>
values下的资源文件
attrs.xml资源文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RotateLoading">
<attr name="loading_width" format="dimension"/>
<attr name="loading_color" format="color"/>
<attr name="shadow_position" format="integer"/>
</declare-styleable>
</resources>
color.xml资源文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="book_loading_background">#ffffff</color>
<color name="book_loading_book">#ff33b5e5</color>
<color name="book_loading_page">#ffffff</color>
</resources>
dimen.xml资源文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="book_border">10dp</dimen>
<dimen name="page_border">5dp</dimen>
<dimen name="book_padding">15dp</dimen>
</resources>