属性动画实战之扁平化自定义Loading

最近换了一个项目组,需要从新开发一个APP,美工给推荐了几个Loading效果,都不是很满意,总觉得有违和感,于是自己从网上找了一个还算满意的效果,自己用代码实现了一遍,顺便重温了一下属性动画,100多行代码轻轻松松搞定。


网上原图效果如下:

这里写图片描述

Loading的载体其实就是一个自定义的dialog,下面总结一下实现过程:

1,首先,要有一个自定义的view,用来实现一个能屏幕适配的圆点。这里只继承view,不继承surfaceview的原因是不需要频繁的重绘我们的圆点,所以也就不存在阻塞UI线程的问题。

/**
 * 自定义圆点
 * @author lizheng
 *
 */
public class MyPointView extends View{
    /**
     * 点的半径
     */
    public int pointW = 20;
    public void setPointW(int pointW) {
        this.pointW = pointW;
    }
    public MyPointView(Context context) {
        super(context);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.WHITE);
        //点的圆心在父控件的正中心
        canvas.drawCircle(getWidth()/2, getHeight()/2, pointW, paint);
        super.onDraw(canvas);
    }
}

2,圆点有了,在实现dialog之前,还需为他设定一些参数。这个diaolg应该是没有标题栏,没有背景,同时为了美观,背景的四角应该使用圆角效果。明确了这几点,首先我们来实现他的shape和style。

shape:

<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <corners android:radius="20dp" />
    <solid android:color="@color/BaseColor"/>
</shape>

style:

<style name="PointDialogStyle" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@drawable/dialog_bg</item>
        <item name="android:backgroundDimEnabled">true</item>
    </style>

在这里,我们为这个dialog设置了圆角,背景透明,无标题,背景色为纯色。

3,下面就可以开始自定义我们的dialog了,首先,为他实现一个layout,使用动态布局和静态布局都是OK的,这个layout应该是一个横向的线性布局,可以依次排列我们的圆点,并且居中显示,如下:

<?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:gravity="center"
    android:id="@+id/mianView"
    android:orientation="horizontal" >
</LinearLayout>

4,在开始实现自定义dialog之前,我们来规划一下他应该做哪些事
(1),在构造方法中设置style
(2),根据手机屏幕的宽高,动态计算dialog的宽高
(3),动态计算圆点的半径,并为dialog的父控件添加圆点,数量可控
(4),在页面加载完毕后,在回调内为每一个圆点添加属性动画
(5) , 为属性动画添加监听,并实现动画算法

下面给出完整实现代码:

/**
 * 自定义Loading效果的Dialog
 * @author lizheng
 *
 */
public class MyPointDialog extends Dialog {

    /**
     * 点的总数
     */
    public static final int POINT_NUM = 5;
    /**
     * 延迟时间
     */
    public static final int DELAY = 100;

    private Context context;
    private LinearLayout mian;
    private List<MyPointView> views;
    private int mScreenW;
    private int mScreenH;
    private int dialogW;
    private int dialogH;

    public MyPointDialog(Context context) {
        super(context, R.style.PointDialogStyle);
        this.context = context;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mypoint_dialog_view);
        initView();
        getPointView();
    }

    private void getPointView() {
        views = new ArrayList<MyPointView>();
        LinearLayout.LayoutParams lay = new LinearLayout.LayoutParams(
                mScreenW / 14, mScreenW / 14);
        for (int i = 0; i < POINT_NUM; i++) {
            MyPointView pointView = new MyPointView(context);
            pointView.setLayoutParams(lay);
            pointView.setPointW(mScreenW / 54);
            views.add(pointView);
            mian.addView(pointView);
        }
    }

    private void initView() {
        mian = (LinearLayout) findViewById(R.id.mianView);
        Display display = getWindow().getWindowManager().getDefaultDisplay();
        // 屏幕宽高
        mScreenH = display.getHeight();
        mScreenW = display.getWidth();
        // dialog宽高
        dialogW = (int) (mScreenW * 0.23);
        dialogH = (int) (mScreenH * 0.1);

        FrameLayout.LayoutParams fl = new FrameLayout.LayoutParams(
                Utils.dip2px(context, dialogW), Utils.dip2px(context, dialogH));
        mian.setLayoutParams(fl);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        //创建完毕回调
        if (hasFocus) {
            for (int i = 0; i < POINT_NUM; i++) {
                final MyPointView view = views.get(i);
                //计算圆点基于父控件的Y轴位置,公式为:dialog高度的一半 - 圆点父控件高度的一半
                int height = mian.getHeight() / 2 - mScreenW / 28;
                ValueAnimator va = ValueAnimator.ofInt(height, height
                        + mScreenH / 30, height - mScreenH / 27, height);
                va.setDuration(1800);//动画的持续时间
                va.setRepeatCount(Integer.MAX_VALUE);//重复播放
                va.setStartDelay(DELAY + DELAY * i);//延迟播放
                va.setInterpolator(new DecelerateInterpolator());
                va.start();
                va.addUpdateListener(new AnimatorUpdateListener() {

                    @SuppressLint("NewApi")
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        // Y轴方向变化参数
                        int parseInt = Integer.parseInt(animation
                                .getAnimatedValue().toString());
                        view.setY(parseInt);

                        // XY轴大小变化参数
                        float fraction = animation.getAnimatedFraction();
                        view.setScaleX(fraction < 0.5 ? 1 - fraction : fraction);
                        view.setScaleY(fraction < 0.5 ? 1 - fraction : fraction);
                    }
                });
            }
        }
        super.onWindowFocusChanged(hasFocus);
    }
}

简单说一下动画算法的思路,每个圆点做的事其实是一样的,只是每个点比前一个延迟了近0.1秒的时间,通过使用ValueAnimator,设置圆点的起始位置,上下偏移位置,即可实现,最后在ValueAnimator 的监听方法中对我们圆点的Y轴坐标进行赋值即可。
原图中在位移的同时还有一个透明度的变化,这里为了迎合项目我做了一个修改,动态改变其大小,XY轴方向同时从1缩小至0.5,再从0.5放大至1。实现方法很很简单,getAnimatedFraction()参数返回的是一个从0至1的float值,其意义在于监听动画完成进度,通过判断getAnimatedFraction()的值的大小,就可以实现我们需要的大小变化效果,配合位移,个人觉得比原图更出彩。


至此,自定义扁平化的Loading效果已实现完毕,最近天天加班,比较累,有什么不足的地方还请大家提出,一起学习一起进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值