前言
过年放假前最后一天班,就想着做个简单又有趣的小东西。于是决定来写个自定义的LoadingView作为这个App框架的加载效果吧。
走过路过点歌Start O(∩_∩)O
Github项目地址
这篇文章叫你如何搭建手写LoadingView,看完这篇文章你能学会:
- 属性动画使用
- 自定义View
--------------------------------关门,上分割线------------------------------------------------
先看看效果:
在这里插入图片描述
有点像饿了么那种风格。
最重要的,在看到这个效果之前脑中想到的实现思路:
1.创建item_loading_view.xml布局,把样式先写出来
2.自定义View,加载这个自定义布局
3.自定义View内做动画处理
1.布局:item_loading_view.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:padding="24dp"
android:layout_height="148dp">
<ImageView
android:id="@+id/iv_loading"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_above="@+id/tv_loading"
android:src="@drawable/ic_favorites" />
<TextView
android:id="@+id/tv_loading"
android:layout_width="wrap_content"
android:text="加载中..."
android:layout_marginTop="12dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content" />
</RelativeLayout>
2.自定义View
public class LoadingView extends FrameLayout {
public LoadingView(Context context) {
this(context,null);
}
public LoadingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 加载布局
LayoutInflater.from(mContext).inflate(R.layout.item_loading_view, this);
}
这个时候在Activity的布局中引用的话,能看到这个loadingView的静态效果了。最后最关键的一步
3.动画效果实现:
这里面涉及到两个动画效果,一个是那个爱心上下跳动,以及底下一个阴影缩放
爱心上下跳动效果:
private void logic() {
ValueAnimator animator = ValueAnimator.ofInt(0,mJumpHeightPx,0);
animator.setDuration(800);
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mIv.setTranslationY(-value);
// 为了画shadow
mMovePercent = value / 100f;
postInvalidate();
}
});
animator.start();
}
LoaingView构造函数中调用该方法(详细见githun项目源码)
实现思路:利用属性动画移动
关于属性动画作用的原理可以去百度几篇文章,一句话概括就是,属性动画提供的是:使用某种变化规则去得到一个不断变化值,然后自己利用这个值去设置控件的属性以达到动画的效果。
shadow缩放效果:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 阴影宽高的一半 最大不超过16 / 8
float shadowHalfWidth = Math.max(16f,(mIvRight - mIvLeft) / 2 );
float shadowHalfHeight = Math.max(8f,(mIvBottom - mIvTop) / 2 );
// 阴影区域中线位置(决定阴影所在位置) yCenter:垂直中线的纵坐标 xCenter:水平中线的横坐标
float yCenter = mIvLeft + shadowHalfWidth;
float xCenter = mIvTop + shadowHalfHeight * 2;
// 缩放的变化量(决定阴影变化大小)
float horizontalDiff = Math.max(2,(1f - mMovePercent) * shadowHalfWidth * mShadowFlatX);
float verticalDiff = Math.max(2,(1f - mMovePercent) * shadowHalfHeight * mShadowFlatY);
Log.d("bigname", "onDraw: " + yCenter + "-----" + xCenter + "------" + horizontalDiff + "--------" + verticalDiff + "-------" + mMovePercent);
mShadowRectF.set(
yCenter - horizontalDiff,
xCenter - verticalDiff,
yCenter + horizontalDiff,
xCenter + verticalDiff
);
canvas.drawOval(mShadowRectF, mShadowPaint);
}
shadow利用画布去实现:
首先在使用属性动画的时候将变化的值赋给全局变量mMovePercent;并且变化的时候调用postInvalidate()重绘,这样的话在onDraw()方法中接口和mMovePercent就能知道当前动画进行的进度。依此来控制shadow缩放。
这个自定义LoadingView效果就已经实现了,接下来会对这个控件做些优化,增加更炫的动画效果,然后做好封装。
源码在上面的github链接
敬请期待---
网友评论