我们会用到PS,即使不会也不要怂,只需要几步傻瓜式操作即可。

属性动画可以看看我另一篇文章:属性动画详解

效果图

相信机智的各位,看完之后一定能发挥创意,做出更酷更炫的效果

图层获取

首先你需要找一张你喜欢的GIF图,然后一张背景图片,以及一些小组件图片(为了这个页面不卡,我就直接发我处理后的图片资源了)。

打开PS,把图片拖拽进去,然后GIF就会拆成各个独立的图层,也可以点击窗口——>动画进行预览。

点击图层旁边的复选框(选中显示一个眼睛的图案),选中表示图层可见,每次选择一个图层,然后保存文件即可,然后一张GIF就被拆分成以下几个图片(我对图层进行了简单处理,如果会PS可以自行扩展):

其它资源(其实是有4张图片,白底可能看不到):

认识Animation-list

根标签为animation-list,文件存放在res/drawable目录下,强调是drawable文件夹,我和小伙伴都有放错文件夹的经历的说。

item标签用于声明图片,没什么好说的

两个关键属性:

oneshot代表着是否只展示一遍,设置为false会不停的循环播放动画

android:duration 表示展示所用的该图片的时间长度

完整的文件如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@mipmap/load_anim_a"
        android:duration="150" />
    <item
        android:drawable="@mipmap/load_anim_b"
        android:duration="150" />
    <item
        android:drawable="@mipmap/load_anim_c"
        android:duration="150" />
    <item
        android:drawable="@mipmap/load_anim_d"
        android:duration="150" />
    <item
        android:drawable="@mipmap/load_anim_e"
        android:duration="150" />
</animation-list>

源码:

然后就直接上代码了,具体注意事项我就放在代码注释中好了,String资源文件自行添加。

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <!--使用CardView,好看!-->
    <android.support.v7.widget.CardView
        android:id="@+id/error_top"
        android:layout_width="240dp"
        android:layout_height="216dp"
        app:cardCornerRadius="3dp"
        app:cardElevation="5dp">

        <!--定义一个布局,放背景飘过的云彩,动态添加也成-->
        <RelativeLayout
            android:id="@+id/error_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorPrimaryLightest"
            android:contentDescription="@string/description_img"
            android:scaleType="fitXY" />

        <!--动画的背景图片-->
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@string/description_img"
            android:scaleType="fitXY"
            android:src="@mipmap/load_anim_bg" />

        <!--启动动画时候的图片-->
        <ImageView
            android:id="@+id/error_in"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@string/description_img"
            android:scaleType="fitXY"
            android:src="@drawable/internet_connect"
            android:visibility="invisible" />

        <!--默认状态下的图片-->
        <ImageView
            android:id="@+id/error_show"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="bottom"
            android:contentDescription="@string/description_img"
            android:scaleType="fitXY"
            android:src="@mipmap/load_anim_default" />

        <!--按钮,可以点击重新加载-->
        <Button
            android:id="@+id/error_refresh_btn"
            android:layout_width="80dp"
            android:layout_height="24dp"
            style="@style/Widget.AppCompat.Button.Borderless"
            android:background="@color/holo_orange_light"
            android:layout_gravity="bottom|center"
            android:layout_margin="6dp"
            android:gravity="center"
            android:textColor="@android:color/white"
            android:text="@string/err_try_connect" />
    </android.support.v7.widget.CardView>
</RelativeLayout>

Java代码:

/**
 * Created by ChenSS on 2016/10/25.
 */
public class ErrorDialog extends Dialog {
    private static final int REFRESH_FAILED = 0;
    private static final int REFRESH_OK = 1;
    private static final int REFRESH_TIMES = 2;
    private static final int ANIM_START = 3;

    private Context mContext;

    private Button error_refresh;
    private ImageView error_in;
    private ImageView error_show;

    private Handler mHandler;
    private AnimationDrawable animationDrawable;
    private InternetRunnable runnable;

    private List<ImageView> mWind;

    public ErrorDialog(Context context) {
        super(context, R.style.dialog);
        this.mContext = context;
        setCanceledOnTouchOutside(true);
    }

    //网络连接成功的标志
    private boolean flag;

    public boolean isConnected() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.error_activity);

        mHandler = new MyHandler(ErrorDialog.this);
        error_in = (ImageView) findViewById(R.id.error_in);
        error_show = (ImageView) findViewById(R.id.error_show);
        error_refresh = (Button) findViewById(R.id.error_refresh_btn);
        RelativeLayout error_container = (RelativeLayout) findViewById(R.id.error_container);

        //AnimationDrawable对象
        animationDrawable = (AnimationDrawable) error_in.getDrawable();

        //生成随机飘过的云彩,这样每次打开ErrorDialog动画都是不同的
        mWind = new ArrayList<>(5);
        for (int i = 0; i < 5; i++) {
            ImageView wind = new ImageView(mContext);
            LinearLayout.LayoutParams params;
            switch ((int) (Math.random() * 3)) {
                case 0:
                    //生成一个小的16×1的白色矩形
                    params = new LinearLayout.LayoutParams(16, 1);
                    wind.setLayoutParams(params);
                    wind.setBackgroundColor(mContext
                            .getResources()
                            .getColor(R.color.well_background));
                    break;
                case 1:
                    //使用白云的图片资源
                    params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                            LinearLayout.LayoutParams.WRAP_CONTENT);
                    wind.setLayoutParams(params);
                    wind.setImageResource(R.mipmap.clouds_a);
                    break;
                case 2:
                    //使用白云的图片资源
                    params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                            LinearLayout.LayoutParams.WRAP_CONTENT);
                    wind.setLayoutParams(params);
                    wind.setImageResource(R.mipmap.clouds_b);
                    break;
            }
            //先隐藏起来,防止出现画面闪烁,或者莫名的白点
            wind.setVisibility(View.INVISIBLE);
            //将之前的图层放到集合中备用
            mWind.add(wind);
            error_container.addView(wind);
        }

        error_refresh.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //当用户选择重新加载
                runnable = new InternetRunnable(ErrorDialog.this);
                //默认图片隐藏
                error_show.setVisibility(View.GONE);
                //动画可见
                error_in.setVisibility(View.VISIBLE);
                //按钮不可点击
                error_refresh.setClickable(false);
                //按钮文本变化
                error_refresh.setText(mContext.getString(R.string.err_title));

                if (CheckNetUtils.isNetworkConnected(mContext)) {
                    //这是我写的判断手机当前是否联网的方法,如果手机断网了,也没必要尝试重新链接。
                    //测试时,可直接删除代码,实际使用,设计自己的代码逻辑。
                }
                //启动动画
                animationDrawable.start();

                //这里我是直接调用项目的线程池,你可以new一个线程。
                ThreadPool.getCachedThreadPool().execute(runnable);
                //向Handler发送延迟消息(是线程跑完再发送?还是发送延迟消息?自己选择吧)
                mHandler.sendEmptyMessage(REFRESH_FAILED);
            }
        });
    }

    /**
     * 按返回键关掉dialogue
     */
    @Override
    public void onBackPressed() {
        this.dismiss();
        if (runnable != null)
            runnable.stopThread();
        mHandler.removeCallbacksAndMessages(null);
        super.onBackPressed();
    }

    /**
     * 这是一个我写的检测网络连接的方法,如果链接成功,返回true。
     * 测试时,可以删除方法体,直接返回false,实际使用,编写自己的代码逻辑
     */
    private boolean checkConnect() {
        RetrofitService retrofitService = RetrofitClient.getClient();
        Call<ResponseBody> call = retrofitService.checkConnection(Parameter.URI_CHECK_NET);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if (response.isSuccessful()) {
                    try {
                        String str = response.body().string();
                        if (Parameter.SUCCEEDED.equals(str))
                            setFlag(true);
                    } catch (IOException e) {
                        setFlag(false);
                    }
                } else {
                    setFlag(false);
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                setFlag(false);
            }
        });
        return false;
    }

    /**
     * 写一个随时可以stop的runnable
     */
    private static class InternetRunnable implements Runnable {
        private boolean stop;
        private Thread thread;
        private WeakReference<Dialog> reference;

        //参数就是当前Dialog对象了
        public InternetRunnable(Dialog dialog) {
            stop = false;
            thread = Thread.currentThread();
            reference = new WeakReference<>(dialog);
        }

        @Override
        public void run() {
            //activity 其实是ErrorDialog,忘记改了o(∩_∩)o
            final ErrorDialog activity = (ErrorDialog) reference.get();
            int count = 0;
            while (!stop) {
                try {
                    //这个参数,我是尝试加载一次,“连接中...”中的省略号就加一个,然后天上飘过一朵
                    count++;
                    Message message = activity.mHandler.obtainMessage();
                    if (count % 2 == 0) {
                        message.arg1 = count / 2;
                        //如果链接成功,就向Handler发送消息,然后关闭线程等等,失败就继续检测
                        if (activity.isConnected()) {
                            activity.mHandler.sendEmptyMessage(REFRESH_OK);
                        } else {
                            activity.checkConnect();
                        }
                        message.what = REFRESH_TIMES;
                        activity.mHandler.sendMessage(message);
                    } else {
                        //天上飘过一朵云
                        message.arg1 = count / 2;
                        message.what = ANIM_START;
                        activity.mHandler.sendMessage(message);
                    }
                    //每次线程睡眠0.2秒
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    this.thread.interrupt();
                }
            }
        }

        //关闭线程
        public void stopThread() {
            this.stop = !stop;
            System.gc();
        }
    }

    private static class MyHandler extends Handler {
        private WeakReference<Dialog> reference;

        public MyHandler(Dialog dialog) {
            reference = new WeakReference<>(dialog);
        }

        @Override
        public void handleMessage(Message msg) {
            final ErrorDialog dialog = (ErrorDialog) reference.get();
            if (dialog != null) {
                switch (msg.what) {
                    case REFRESH_FAILED:
                        //失败就显示默认页面
                        postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                dialog.error_in.setVisibility(View.GONE);
                                dialog.error_show.setVisibility(View.VISIBLE);
                                dialog.runnable.stopThread();
                                dialog.animationDrawable.stop();
                                dialog.error_refresh.setText(dialog.mContext.getString(R.string.err_try_connect));
                                dialog.error_refresh.setClickable(true);
                            }
                        }, 5000);
                        break;
                    case REFRESH_OK:
                        //成功就关闭动画、关闭dialog
                        dialog.animationDrawable.stop();
                        dialog.dismiss();
                        break;
                    case REFRESH_TIMES:
                        //修改“连接中...”的省略号个数
                        String txt = dialog.mContext.getString(R.string.err_title);
                        int count = msg.arg1;
                        for (int i = 0; i < count % 3; i++) {
                            txt += dialog.mContext.getString(R.string.err_title_dot);
                        }
                        dialog.error_refresh.setText(txt);
                        break;
                    case ANIM_START:
                        //天上飘过一朵云
                        int position = msg.arg1 % 5;
                        dialog.startWind(dialog.mWind.get(position));
                        break;
                }
            }
        }
    }

    /**
     * 天上飘过一朵云的动画,参数是View,大家可以直接传入任意一个View测试一下这个方法,
     * 这里我就不具体展开介绍属性动画了
     */
    public void startWind(final View view) {
        final float pointY = Math.round(Math.random() * 240);
        if (view.getVisibility() == View.INVISIBLE)
            view.setVisibility(View.VISIBLE);
        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(1000);
        valueAnimator.setObjectValues(new PointF(0, 0));
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setEvaluator(new TypeEvaluator<PointF>() {
            @Override
            public PointF evaluate(float fraction, PointF startValue,
                                   PointF endValue) {
                PointF point = new PointF();
                point.x = 250 * fraction * 3;
                point.y = pointY;
                return point;
            }
        });

        valueAnimator.start();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF point = (PointF) animation.getAnimatedValue();
                view.setX(point.x);
                view.setY(point.y);
            }
        });
    }
}

注意事项:

由于这是一个自定义Dialog,要对它的样式进行处理,否则背景会是布局中所显示的颜色

    <style name="dialog" parent="@android:style/Theme.Dialog">
        <!--背景颜色及透明程度-->
        <item name="android:windowBackground">@android:color/transparent</item>
        <!--是否有标题 -->
        <item name="android:windowNoTitle">true</item>
        <!--是否浮现在activity之上-->
        <item name="android:windowIsFloating">true</item>
    </style>

可能漏传部分资源文件,大家可以自行添加,或者留言提醒我:

    <string name="err_title">连接中.</string>
    <string name="err_title_dot">.</string>

Animation-list,帧动画+属性动画,做出Flash般的效果的更多相关文章

  1. android 帧动画,补间动画,属性动画的简单总结

      帧动画——FrameAnimation 将一系列图片有序播放,形成动画的效果.其本质是一个Drawable,是一系列图片的集合,本身可以当做一个图片一样使用 在Drawable文件夹下,创建ani ...

  2. Android 动画——属性动画Property Animation

    Android在3.0之前只提供了两种动画:View Animation .Drawable Animation .也就是我们在<Android 动画——Frame Animation与Twee ...

  3. Android笔记(六十五) android中的动画——属性动画(propertyanimation)

    补间动画只能定义起始和结束两个帧在“透明度”.“旋转”.“倾斜”.“位移”4个方面的变化,逐帧动画也只能是播放多个图片,无法满足我们日常复杂的动画需求,所以谷歌在3.0开始,推出了属性动画(prope ...

  4. Android 动画 属性动画 视图动画 补间动画 帧动画 详解 使用

    Android动画 Property Animation res/animator/filename.xml In Java: R.animator.filename In XML: @[packag ...

  5. 【属性动画总结】Property Animation

    属性动画概述 3.0以前,android仅支持两种动画模式,tweened animation 和 frame-by-frame animation,在android3.0中又引入了一个新的动画系统: ...

  6. 第三部分:Android 应用程序接口指南---第四节:动画和图形---第一章 属性动画及动画与图形概述

    第1章 属性动画及动画与图形概述 Android提供了一系列强大的API来把动画加到UI元素中,以及绘制自定义的2D和3D图像中去.下面的几节将综述这些可用的API以及系统的功能,同时帮你做出最优的选 ...

  7. View动画和属性动画

    在应用中, 动画效果提升用户体验, 主要分为View动画和属性动画. View动画变换场景图片效果, 效果包括平移(translate), 缩放(scale), 旋转(rotate), 透明(alph ...

  8. 属性动画详解 Interpolator TypeEvaluator

    概述 产生原因         3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中又引入了一个新的动画系统:prope ...

  9. 每日一问:到底为什么属性动画后 View 在新位置还能响应事件

    在 Android 开发中,我们难免会使用动画来处理各种各样的动画效果,以满足 UI 的高逼格设计.对于比较复杂的动画效果,我们通常会采用著名的开源库:lottie-android,或许你会对 lot ...

随机推荐

  1. java的windows自动化-自动运行java程序

    那么在一些工具齐全并且已经有了一定的写好的java程序的情况下(环境变量和软件见上一章http://www.cnblogs.com/xuezhezlr/p/7718273.html),如何自动化运行j ...

  2. vux 组件打造手机端项目

    其实,我用vux组件的过程是这样的,哇!太方便了!!功能好全!!太简单了!!然后,就各种"跳坑".以下排坑环节. 1.安装vux:cnpm i -S vux;   比较顺利吧. 2 ...

  3. 461. Hamming Distance(leetcode)

    The Hamming distance between two integers is the number of positions at which the corresponding bits ...

  4. Entity Framework Core 2.0 使用代码进行自动迁移

    一.前言 我们在使用EF进行开发的时候,肯定会遇到将迁移更新到生产数据库这个问题,前面写了一篇文章介绍了Entity Framework Core 2.0的入门使用,这里面介绍了使用命令生成迁移所需的 ...

  5. 优先队列(存储结构数组)--Java实现

    /*优先队列--是对队列的一种改进 *要存储的数据存在优先级--数值小的优先级高--在队头 *优先队列的实现 *1.数组:适合数据量小的情况(没有用rear+front实现) *优先队列头在items ...

  6. onclick事件触发 input type=“file” 上传文件

    添加按钮: <input type="button" name="button" value="浏览" onclick="j ...

  7. python核心编程一书笔记之第一篇

    #!/usr/bin/env python# -*- coding:utf-8 -*- #env 是一个命令用来寻找系统中的python解释器.第二条解释使用utf-8编码 在类unix系统中允许py ...

  8. 在vue2.0中使用sass

    第一步:使用sass必须安装下面三个东西 cnpm install node-sass --save //安装node-sass cnpm install sass-loader --save //安 ...

  9. 京东口红top 30分析

    一.抓取商品id 分析网页源码,发现所有id都是在class="gl-item"的标签里,可以利用bs4的select方法查找标签,获取id: 获取id后,分析商品页面可知道每个商 ...

  10. ②bootstrap栅栏使用基础案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...