Android开罐头———打造动漫二次元加载读取的自定义dialog

自定义dialog当然已经被写烂咯,本文的重点是很方便地获取二次元动漫动图。自带的progress dialog看腻了吗?来换个和bilibili一样的动漫二次元加载吧!

image.png

一、动画来源获取

1.1 来源简介

先来预览下效果咯:

yicha3.gif

很萌很萌吧!!!loading动画当然还是帧动画咯,由于是二次元,pixiv(俗称p站)可谓是动漫图片最多的站点了,上面有数以亿计的动漫图片和动图。

pixiv :新兴的日本同人画、插画作品分享站点。
采用了web2.0的方式,每个参与者都有自己的主页并可以对作品评价打分。

但是由于版权的保护性政策,动图是无法直接下载到gif的,所以我们必须通过一些非常规手段获取,巧合的是,获取到的是动画帧的图片压缩包,所以可以直接拿来当做安卓开发的帧动画使用!

1.2 动画帧获取

p站如今支持中文,所以阅读上没有了障碍,大家可以搜索自己喜欢主题的动图,或者直接去每日排行榜获取高人气动图,我们以此链接 为例。

  • 使用chrome内核的浏览器,右键选择检查:


    image.png
  • 进入开发者工具界面后,按下Ctrl+FCommand+F)搜索关键词zip,其实已经可以看到我们要的下载链接了,但是不能直接复制,必须选中整个段落后,右键copy到文本编辑器中。
    image.png
  • 在文本编辑器中,删除所有的转义字符\后,复制名字里带有600x600的那个zip链接,然后粘贴到浏览器或者迅雷中下载。

    image.png

  • 解压后即可获得动画帧图片,顺序为名称。


    image.png

警告:pixiv站点所有图片拥有版权©️,商用请联系原作者,下载仅仅是个人学习参考之用。

二、帧动画xml文件的建立

怕是没有人不会吧,直接贴代码了,具体的duration根据实际情况自己调节:

<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false"
>
    <item
        android:drawable="@drawable/loading_1"
        android:duration="100"
    />
    <item
        android:drawable="@drawable/loading_2"
        android:duration="100"
    />
    <item
        android:drawable="@drawable/loading_3"
        android:duration="100"
    />
    <item
        android:drawable="@drawable/loading_4"
        android:duration="100"
    />
    <item
        android:drawable="@drawable/loading_5"
        android:duration="100"
    />
    <item
        android:drawable="@drawable/loading_6"
        android:duration="100"
    />
    <item
        android:drawable="@drawable/loading_7"
        android:duration="100"
    />
    <item
        android:drawable="@drawable/loading_8"
        android:duration="100"
    />
</animation-list>

三、自定义dialog的建立

3.1 Dialog概念

dialog的官方定义:

A dialog is a small window that prompts the user to make a decision or enter additional information. A dialog does not fill the screen and is normally used for modal events that require users to take an action before they can proceed.

先来了解下什么是Window:

Window表示窗口的概念,它实际上是View的直接管理者,包括View的视图创建,事件分发机制都必须先经过Window。

自定义Dialog,要先明确Dialog的地位。

  • View是android中视图的呈现方式,但是View不能单独存在,必须依附在Window窗口这个抽象概念上,而Android中提供视图的地方有Activity,Dialog,Toast。
  • 所以某种程度上来说,Dialog是和活动平起平坐的,Dialog的Window创建过程与Activity类似(几乎没区别),所以Dialog也有setContentView方法。具体的创建过程可以参考《Android开发艺术探索》这本书。

所以我们在后面想要管理Dialog的对话框大小就必须使用WindowManager类,而不是去设置父布局的params。

3.2 建立dialog布局文件

直接上代码了,注意,我直接在布局最外层设定了固定的大小,且使用了一个圆角半透明矩形shape作为背景。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="120dp"
    android:layout_height="120dp"
    android:layout_gravity="center_horizontal"
    android:background="@drawable/shape_dialog_bg"
    android:layout_centerInParent="true"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp">

        <ImageView
            android:id="@+id/loading_view"
            android:layout_width="85dp"
            android:layout_height="85dp"
            android:scaleType="fitCenter"
            android:layout_centerInParent="true"
            android:src="@drawable/loading_data_anim"
            />

    </RelativeLayout>

    <TextView
        android:id="@+id/tv_load_dialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="读取中……"
        android:layout_marginTop="5dp"
        android:textColor="@color/color_666666"
        android:textSize="12sp"/>

</LinearLayout>

效果图:

image.png

3.3 自定义dialog的建立

我们新建一个类LoadingDialog继承自Dialog,初始化布局参数:

private void initView() {
       setContentView(R.layout.dialog_loading_layout);
       imageView = (ImageView) findViewById(R.id.loading_view);
       //启动我们的二次元帧动画
       AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
       animationDrawable.start();


       //获取WindowManager.LayoutParams来管理dialog最外部window的大小,理由在3.1          节中已经说明。其实这里不用设置,因为我们在布局最外层指定了居中,且设为固定大小             后对话框最外层布局会自适应(但是android5.0以上默认最外布局match_parent)。            
       WindowManager.LayoutParams params = getWindow().getAttributes();
       params.gravity = Gravity.CENTER;
       params.width = WindowManager.LayoutParams.WRAP_CONTENT;
       params.height = WindowManager.LayoutParams.WRAP_CONTENT;
       getWindow().setAttributes(params);

   }

但是坑来了,尽管我们设定了最外层布局为WRAP_CONTENT,但是它是默认白色的背景,所以我们的圆角矩形效果就失效了,借@青蛙要fly的一张图,如果设定background为黑色则效果更加直观:

image.png

解决的办法很简单,直接覆盖重写dialog style里的透明属性即可,另外,使用兼容包的dialog样式默认没有Title了,但是这里还是覆写一下:

<style name="dialog_no_title" parent="Theme.AppCompat.Dialog">
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>

在构造函数中传入我们的新style(dialog提供了这个构造方法):

public LoadingDialog(Context context) {
        super(context,R.style.LoadingDialog);
        mContext=context;
    }

最后我们的自定义dialog就已经完成了,贴上完整代码:

public class LoadingDialog extends Dialog {
    private Context mContext;
    ImageView imageView;

    public LoadingDialog(Context context) {
        super(context,R.style.LoadingDialog);
        mContext=context;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initView();
    }

    private void initView() {
        setContentView(R.layout.dialog_loading_layout);
        imageView = (ImageView) findViewById(R.id.loading_view);
        AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
        animationDrawable.start();


        WindowManager.LayoutParams params = getWindow().getAttributes();
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        getWindow().setAttributes(params);

    }

    @Override
    public void dismiss() {
        super.dismiss();
    }
}

四、封装构建Dialog管理类

为了效率和复用性,我们使用单例模式封装一个线程安全的DialogManger类:

public class DialogManager {

    private static DialogManager mInstnce = null;
    private ProgressDialog mDialog;

    public static DialogManager getInstnce() {

        if (mInstnce == null) {
            //线程安全模式
            synchronized (DialogManager.class) {
                if (mInstnce == null) {
                    mInstnce = new DialogManager();
                }
            }
        }
        return mInstnce;
    }

    public void showProgressDialog(Context context) {

        if (mDialog == null) {
            mDialog = new ProgressDialog(context);
            //设置点击dialog外部,不会自动退出dialog
            mDialog.setCanceledOnTouchOutside(false);
        }
        mDialog.show();
    }

    public void dismissProgressDialog() {

        if (mDialog != null) {
            mDialog.dismiss();
        }
        mDialog = null;
    }
}

这样,我们就可以一句话调出和取消读取界面了:

            //显示
        DialogManager.getInstnce().showProgressDialog(this);
         //取消显示
        DialogManager.getInstnce().dismissProgressDialog();

五、总结

其实整个过程非常简单,就是理解window类,然后自定义一个dialog,然后在dialog中启动帧动画,有些老生常谈的坑需要注意罢了。再来看一眼我们萌萌的loading动画吧:


yicha3.gif

推荐阅读更多精彩内容