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

来源:互联网 发布:视频画中画制作软件 编辑:程序博客网 时间:2024/05/30 23:00

最近换了一个项目组,需要从新开发一个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
原创粉丝点击