美文网首页
NiceDialog 学习,写统一Dialog

NiceDialog 学习,写统一Dialog

作者: 翻滚吧王咸鱼 | 来源:发表于2018-06-13 19:55 被阅读0次

最近项目需要dialog 这需求,在网上找一些 发现这个作者写的效果很好,就记录一下,方便下次使用,
项目地址:https://github.com/Othershe/NiceDialog

先看效果图. commit.gif
confirm.gif
![red_packet.gif](https://img.haomeiwen.com/i7406922/cc52c173a40a7421.gif?imageMogr2/auto-orient/strip)
share.gif
set.gif

基本用法.

可以自己下载来到项目的代码拷到自己的项目里 ,也可以用依赖.
Step 1. 添加 JitPack 仓库 在当前项目等根目录下的 build.gradle 文件中添加如下内容:

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

Step 2. 添加项目依赖

dependencies {
        compile 'com.github.Othershe:NiceDialog:1.1.1'
}

Step 2.例子代码

NiceDialog.init()
          .setLayoutId(R.layout.dialog)     //设置 dialog 布局文件
          .setConvertListener(new ViewConvertListener() {     //进行相关 View 操作的回调
              @Override
              public void convertView(ViewHolder holder, final BaseNiceDialog dialog) {
    //如果需要找布局中相应的控件;
    Button mBtnCancel =(Button)holder.getConvertView().findViewById(R.id.cancel);

//如果只需要控件的点击事件
viewHolder.setOnClickListener(R.id.cancel, new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                dialog.dismiss();
                            }
                        });
              }
          })
          .setDimAmount(0.3f)     //调节灰色背景透明度[0-1],默认 0.5f
          .setShowBottom(true)     //是否在底部显示 dialog,默认flase
          .setMargin()     //dialog 左右两边到屏幕边缘的距离(单位:dp),默认0dp
          .setWidth()     //dialog 宽度(单位:dp),默认为屏幕宽度
          .setHeight()     //dialog 高度(单位:dp),默认为WRAP_CONTENT
          .setOutCancel(false)     //点击 dialog 外是否可取消,默认true
          .setAnimStyle(R.style.EnterExitAnimation)     //设置dialog进入、退出的动画style(底部显示的 dialog 有默认动画)
          .show(getSupportFragmentManager());     //显示dialog

源码分析:
先看一下代码的结构


QQ图片20180613183013.png
  1. 看一下 BaseNiceDialog 这个类 是继承DialogFragment 没有继承 Dialog, 按照谷歌官方的是推荐尽量使用DialogFragment.(对于Android 3.0以下的版本,可以结合使用support包中提供的DialogFragment以及FragmentActivity). 不懂的话可以看一下这一篇文章:https://blog.csdn.net/djun100/article/details/19991699 说的蛮仔细的. 现在代码一步一步的看.
public abstract class BaseNiceDialog extends DialogFragment {
    private static final String MARGIN = "margin";
    private static final String WIDTH = "width";
    private static final String HEIGHT = "height";
    private static final String DIM = "dim_amount";
    private static final String BOTTOM = "show_bottom";
    private static final String CANCEL = "out_cancel";
    private static final String ANIM = "anim_style";
    private static final String LAYOUT = "layout_id";

    private int margin;//左右边距
    private int width;//宽度
    private int height;//高度
    private float dimAmount = 0.5f;//灰度深浅
    private boolean showBottom;//是否底部显示
    private boolean outCancel = true;//是否点击外部取消
    @StyleRes
    private int animStyle;
    @LayoutRes
    protected int layoutId;

    public abstract int intLayoutId(); //布局写成抽象类让子类继承.

    public abstract void convertView(ViewHolder holder, BaseNiceDialog dialog);

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NO_TITLE, R.style.NiceDialog);   //设置弹框的样式
        layoutId = intLayoutId(); //设置布局, 

        //恢复保存的数据
        if (savedInstanceState != null) {
            margin = savedInstanceState.getInt(MARGIN);
            width = savedInstanceState.getInt(WIDTH);
            height = savedInstanceState.getInt(HEIGHT);
            dimAmount = savedInstanceState.getFloat(DIM);
            showBottom = savedInstanceState.getBoolean(BOTTOM);
            outCancel = savedInstanceState.getBoolean(CANCEL);
            animStyle = savedInstanceState.getInt(ANIM);
            layoutId = savedInstanceState.getInt(LAYOUT);
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(layoutId, container, false);
        convertView(ViewHolder.create(view), this);
        return view;
    }

    @Override
    public void onStart() {  
        super.onStart();
        initParams();   //当页面显示的时候初始化一些参数
    }

    /**
     * 屏幕旋转等导致DialogFragment销毁后重建时保存数据
     *
     * @param outState
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(MARGIN, margin);
        outState.putInt(WIDTH, width);
        outState.putInt(HEIGHT, height);
        outState.putFloat(DIM, dimAmount);
        outState.putBoolean(BOTTOM, showBottom);
        outState.putBoolean(CANCEL, outCancel);
        outState.putInt(ANIM, animStyle);
        outState.putInt(LAYOUT, layoutId);
    }

private void initParams() {
        Window window = getDialog().getWindow();
        if (window != null) {
            WindowManager.LayoutParams lp = window.getAttributes(); //设置属性
            //调节灰色背景透明度[0-1],默认0.5f
            lp.dimAmount = dimAmount;
            //是否在底部显示
            if (showBottom) {
                lp.gravity = Gravity.BOTTOM;
                if (animStyle == 0) {
                    animStyle = R.style.DefaultAnimation; //设置动画
                }
            }

            //设置dialog宽度
            if (width == 0) {  //当没有设置宽度 时候
                lp.width = Utils.getScreenWidth(getContext()) - 2 * Utils.dp2px(getContext(), margin);
            } else if (width == -1) {
                lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
            } else {
                lp.width = Utils.dp2px(getContext(), width);
            }

            //设置dialog高度
            if (height == 0) {  //没有设置高度的时候
                lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            } else {
                lp.height = Utils.dp2px(getContext(), height);
            }

            //设置dialog进入、退出的动画
            window.setWindowAnimations(animStyle);
            window.setAttributes(lp);
        }
        setCancelable(outCancel);
    }

    /**
     * 设置左右边的边距
     * @param margin
     * @return
     */
    public BaseNiceDialog setMargin(int margin) {
        this.margin = margin;
        return this;
    }

    /**
     * 设置宽度
     * @param width
     * @return
     */
    public BaseNiceDialog setWidth(int width) {
        this.width = width;
        return this;
    }

    /**
     * 设置高度
     * @param height
     * @return
     */
    public BaseNiceDialog setHeight(int height) {
        this.height = height;
        return this;
    }

    /**
     * 调节灰色背景透明度[0-1],默认0.5f
     * @param dimAmount
     * @return
     */
    public BaseNiceDialog setDimAmount(float dimAmount) {
        this.dimAmount = dimAmount;
        return this;
    }

    /**
     * 是否在底部显示dialog,默认flase
     * @param showBottom
     * @return
     */
    public BaseNiceDialog setShowBottom(boolean showBottom) {
        this.showBottom = showBottom;
        return this;
    }

    /**
     * 点击dialog外是否可取消,默认true
     * @param outCancel
     * @return
     */
    public BaseNiceDialog setOutCancel(boolean outCancel) {
        this.outCancel = outCancel;
        return this;
    }

    /**
     * 设置dialog进入、退出的动画style(底部显示的dialog有默认动画)
     * @param animStyle
     * @return
     */
    public BaseNiceDialog setAnimStyle(@StyleRes int animStyle) {
        this.animStyle = animStyle;
        return this;
    }

    /**
     * 显示dialog
     * @param manager
     * @return
     */
    public BaseNiceDialog show(FragmentManager manager) {
        FragmentTransaction ft = manager.beginTransaction();
        if (this.isAdded()) {
            ft.remove(this).commit();
        }
        ft.add(this, String.valueOf(System.currentTimeMillis()));
        ft.commitAllowingStateLoss();
        return this;
    }

里面我把注释写的蛮清楚了, 主要说一点是每一个方法都返回this 本身, 这是主要可以用连式的调用代码. 其实发现DialogFragment 就是继承Fragment 用发就跟fragment 用发一样的.

现在看一下NiceDialog的代码是怎么写的

public class NiceDialog extends BaseNiceDialog {
    private ViewConvertListener convertListener;

    public static NiceDialog init() {
        return new NiceDialog();
    }

    @Override
    public int intLayoutId() {
        return layoutId;
    }

    @Override
    public void convertView(ViewHolder holder, BaseNiceDialog dialog) {
        if (convertListener != null) {
            convertListener.convertView(holder, dialog);
        }
    }


    public NiceDialog setLayoutId(@LayoutRes int layoutId) {
        this.layoutId = layoutId;
        return this;
    }

    public NiceDialog setConvertListener(ViewConvertListener convertListener) {
        this.convertListener = convertListener;
        return this;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            convertListener = savedInstanceState.getParcelable("listener");
        }
    }

    /**
     * 保存接口
     *
     * @param outState
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable("listener", convertListener);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        convertListener = null;
    }
}

其实这类蛮简单的 里面主要做了数据的保存, 跟跟页面销毁的时候,把convertListener 这个抽象类制空.

1.看一下ViewConvertListener 这个类, 里面就是convertView 这个抽象方法.跟数据的Parcelable序列化.
在Android 中 Serializable 跟Parcelable 的序列化.是有区别.在java中可以使用Serializable接口实现对象的序列化,而在android中既可以使用Serializable接口实现对象序列化也可以使用Parcelable接口实现对象序列化,但是在内存操作时更倾向于实现Parcelable接口,这样会使用传输效率更高效。接下来我们将分别详细地介绍这样两种序列化操作

Parcelable 与 Serializable 区别
两者的实现差异
  Serializable的实现,只需要实现Serializable接口即可。这只是给对象打了一个标记(UID),系统会自动将其序列化。而Parcelabel的实现,不仅需要实现Parcelabel接口,还需要在类中添加一个静态成员变量CREATOR,这个变量需要实现 Parcelable.Creator 接口,并实现读写的抽象方法。

两者的设计初衷
  Serializable的设计初衷是为了序列化对象到本地文件、数据库、网络流、RMI以便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是由于Serializable效率过低,消耗大,而android中数据传递主要是在内存环境中(内存属于android中的稀有资源),因此Parcelable的出现为了满足数据在内存中低开销而且高效地传递问题。

�两者效率选择
  Parcelable的性能比Serializable好,在内存开销方面较小,所以Android应用程序在内存间数据传输时推荐使用Parcelable,如activity间传输数据和AIDL数据传递,而Serializable将数据持久化的操作方便,因此在将对象序列化到存储设置中或将对象序列化后通过网络传输时建议选择Serializable(Parcelable也是可以,只不过实现和操作过程过于麻烦并且为了防止android版本不同而导致Parcelable可能不同的情况,因此在序列化到存储设备或者网络传输方面还是尽量选择Serializable接口)。

两者需要注意的共同点
  无论是Parcelable还是Serializable,执行反序列操作后的对象都是新创建的,与原来的对象并不相同,只不过内容一样罢了。

Android studio 中的快捷生成方式
 在程序开发过程中,我们实现Parcelable接口的代码都是类似的,如果我们每次实现一个Parcelable接口类,就得去编写一次重复的代码,这显然是不可取的,不过幸运的是,android studio 提供了自动实现Parcelable接口的方法的插件,相当实现,我们只需要打开Setting,找到plugin插件,然后搜索Parcelable插件,最后找到android Parcelable code generator 安装即可:

image.png

重启android studio后,我们创建一个User类,如下:


image.png

然后使用刚刚安装的插件协助我们生成实现Parcelable接口的代码,window快捷键:Alt+Insert,Mac快捷键:cmd+n,如下:


image.png

现在看一下

public abstract class ViewConvertListener implements Parcelable {

    protected abstract void convertView(ViewHolder holder, BaseNiceDialog dialog);

    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 该方法负责序列化
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {

    }

    public ViewConvertListener() {
    }

    protected ViewConvertListener(Parcel parcel) {
    }

    /**
     * 负责反序列化
     */
    public static final Creator<ViewConvertListener> CREATOR = new Creator<ViewConvertListener>() {

        /**
         * 从序列化后的对象中创建原始对象
         */
        @Override
        public ViewConvertListener createFromParcel(Parcel source) {
            return new ViewConvertListener(source){
                @Override
                protected void convertView(ViewHolder holder, BaseNiceDialog dialog) {

                }
            };
        }
        /**
         * 创建指定长度的原始对象数组
         */
        @Override
        public ViewConvertListener[] newArray(int size) {
            return new ViewConvertListener[size];
        }
    };
}

发现作者还用了 ViewHolder 这个类对View进行一次封装.
这里面主要用到 views = new SparseArray<>(); 这个集合来,保存 View.这样一样就更好的设置View 的属性.
对于 SparseArray到底哪点比HashMap区别可以看篇文章.https://blog.csdn.net/xiangzhihong8/article/details/52139091 里面讲的仔细.
SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的class,目的是提高效率,其核心是折半查找函数(binarySearch)。

HashMap底层是一个Hash表,是数组和链表的集合实现,有需要的可以去看看我关于Hashmap的分析。hashmap源码分析

所以Android开发中官方推荐:当使用HashMap(K, V),如果K为整数类型时,使用SparseArray的效率更高。

现在看一下 怎么使用的

public class MainActivity extends AppCompatActivity {
    private BaseQuickAdapter<String, BaseViewHolder> mAdapter;
    private List<String > list;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        list = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            list.add("你好"+i);
        }
    }

    public void showDialog0(View view) {
        NiceDialog.init()
                .setLayoutId(R.layout.share_layout)
                .setConvertListener(new ViewConvertListener() {
                    @Override
                    public void convertView(ViewHolder holder, final BaseNiceDialog dialog) {
                        holder.setOnClickListener(R.id.wechat, new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                Toast.makeText(MainActivity.this, "分享成功", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                })
                .setDimAmount(0.3f)
                .setShowBottom(true)
                .show(getSupportFragmentManager());
    }

    public void showDialog1(View view) {
        NiceDialog.init()
                .setLayoutId(R.layout.friend_set_layout)
                .setConvertListener(new ViewConvertListener() {
                    @Override
                    public void convertView(ViewHolder holder, final BaseNiceDialog dialog) {

                    }
                })
                .setShowBottom(true)
                .setHeight(310)
                .show(getSupportFragmentManager());
    }

    public void showDialog2(View view) {
        NiceDialog.init()
                .setLayoutId(R.layout.commit_layout)
                .setConvertListener(new ViewConvertListener() {
                    @Override
                    public void convertView(ViewHolder holder, final BaseNiceDialog dialog) {
                        final EditText editText = holder.getView(R.id.edit_input);
                        editText.post(new Runnable() {
                            @Override
                            public void run() {
                                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                                imm.showSoftInput(editText, 0);
                            }
                        });
                    }
                })
                .setShowBottom(true)
                .show(getSupportFragmentManager());
    }

    public void showDialog3(View view) {
        NiceDialog.init()
                .setLayoutId(R.layout.ad_layout)
                .setConvertListener(new ViewConvertListener() {
                    @Override
                    public void convertView(ViewHolder holder, final BaseNiceDialog dialog) {
                        holder.setOnClickListener(R.id.close, new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                dialog.dismiss();
                            }
                        });
                    }
                })
                .setWidth(210)
                .setOutCancel(false)
                .setAnimStyle(R.style.EnterExitAnimation)
                .show(getSupportFragmentManager());
    }


    public void showDialog4(View view) {
        NiceDialog.init()
                .setLayoutId(R.layout.loading_layout)
                .setWidth(100)
                .setHeight(100)
                .setDimAmount(0)
                .show(getSupportFragmentManager());
    }

    public void showDialog5(View view) {
        ConfirmDialog.newInstance("1")
                .setMargin(60)
                .setOutCancel(false)
                .show(getSupportFragmentManager());
    }

    public void showDialog6(View view) {
        ConfirmDialog.newInstance("2")
                .setMargin(60)
                .setOutCancel(false)
                .show(getSupportFragmentManager());
    }


    public void showDialog11(View view){
        NiceDialog.init()
                .setLayoutId(R.layout.friend_recyclew_layout)
                .setConvertListener(new ViewConvertListener() {
                    @Override
                    public void convertView(ViewHolder holder, final BaseNiceDialog dialog) {
                        RecyclerView holderView = holder.getView(R.id.recycler);
                        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this);
                        holderView.setLayoutManager(linearLayoutManager);
                        holderView.setAdapter(mAdapter = new BaseQuickAdapter<String, BaseViewHolder>(R.layout.item_recyview, list){
                            @Override
                            protected void convert(BaseViewHolder helper, String item) {
                                helper.setText(R.id.tv_name,item);

                            }
                        });

                        mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {  //条目的点击事件
                            @Override
                            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                                Toast.makeText(MainActivity.this,"第几个被点了"+position,Toast.LENGTH_SHORT).show();
                                dialog.dismiss();
                            }
                        });
                    }
                })
                .setShowBottom(true)
                .setDimAmount(0.3f)     //调节灰色背景透明度[0-1],默认0.5f
                .setHeight((int) (Utils.getScreenHeight(MainActivity.this)*0.3))
                .show(getSupportFragmentManager());


    }



    public static class ConfirmDialog extends BaseNiceDialog {
        private String type;

        public static ConfirmDialog newInstance(String type) {
            Bundle bundle = new Bundle();
            bundle.putString("type", type);
            ConfirmDialog dialog = new ConfirmDialog();
            dialog.setArguments(bundle);
            return dialog;
        }

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Bundle bundle = getArguments();
            if (bundle == null) {
                return;
            }
            type = bundle.getString("type");
        }

        @Override
        public int intLayoutId() {
            return R.layout.confirm_layout;
        }

        @Override
        public void convertView(ViewHolder holder, final BaseNiceDialog dialog) {
            if ("1".equals(type)) {
                holder.setText(R.id.title, "提示");
                holder.setText(R.id.message, "您已支付成功!");
            } else if ("2".equals(type)) {
                holder.setText(R.id.title, "警告");
                holder.setText(R.id.message, "您的账号已被冻结!");
            }
            holder.setOnClickListener(R.id.cancel, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dialog.dismiss();
                }
            });

            holder.setOnClickListener(R.id.ok, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dialog.dismiss();
                }
            });
        }
    }
}

相关文章

网友评论

      本文标题:NiceDialog 学习,写统一Dialog

      本文链接:https://www.haomeiwen.com/subject/rxoqjftx.html