AndroidAndroid开发Android资源收录

如何从0构造出一个万能的Dialog

2017-06-02  本文已影响513人  码农大表哥

前言

本文会用到Builder设计模式:Android开发---Builder 模式必知必会

效果一.png 效果二.png 效果三.png 效果四.png 效果五.png 效果六.png

以上是部分效果展示,下面我们将进入正题:

目录

1.继承Dialog,高仿一个AlertDialog

1.1 开始按照v7包的AlertDialog撸一个轮子

上一个链接让大家看看如何简单自定义:Android AlertDialog/AlertDialog.builder 以及 自定义AlertDialog方法

private final AlertController mAlert;
static final int LAYOUT_HINT_NONE = 0;
static final int LAYOUT_HINT_SIDE = 1;

protected AlertDialog(@NonNull Context context) {
    this(context, 0);
}


protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
    super(context, resolveDialogTheme(context, themeResId));
    mAlert = new AlertController(getContext(), this, getWindow());
}

protected AlertDialog(@NonNull Context context, boolean cancelable,
        @Nullable OnCancelListener cancelListener) {
    this(context, 0);
    setCancelable(cancelable);
    setOnCancelListener(cancelListener);
}

private static int resolveDialogTheme(@NonNull Context context, @StyleRes int resid) {
    if (resid >= 0x01000000) {   // start of real resource IDs.
        return resid;
    } else {
        TypedValue outValue = new TypedValue();
        context.getTheme().resolveAttribute(R.attr.alertDialogTheme, outValue, true);
        return outValue.resourceId;
    }
}


public void setView(View view) {
    mAlert.setView(view);
}

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


public static class Builder {
    private final AlertController.AlertParams P;
    private final int mTheme;

  
    public Builder(@NonNull Context context) {
        this(context, resolveDialogTheme(context, 0));
    }

    public Builder(@NonNull Context context, @StyleRes int themeResId) {
        P = new AlertController.AlertParams(new ContextThemeWrapper(
                context, resolveDialogTheme(context, themeResId)));
        mTheme = themeResId;
    }

  
    @NonNull
    public Context getContext() {
        return P.mContext;
    }


    public Builder setCancelable(boolean cancelable) {
        P.mCancelable = cancelable;
        return this;
    }

  
    public Builder setOnCancelListener(OnCancelListener onCancelListener) {
        P.mOnCancelListener = onCancelListener;
        return this;
    }


    public Builder setOnDismissListener(OnDismissListener onDismissListener) {
        P.mOnDismissListener = onDismissListener;
        return this;
    }

  
    public Builder setOnKeyListener(OnKeyListener onKeyListener) {
        P.mOnKeyListener = onKeyListener;
        return this;
    }

   
    public Builder setView(int layoutResId) {
        P.mView = null;
        P.mViewLayoutResId = layoutResId;
        P.mViewSpacingSpecified = false;
        return this;
    }

   
    public Builder setView(View view) {
        P.mView = view;
        P.mViewLayoutResId = 0;
        P.mViewSpacingSpecified = false;
        return this;
    }

    @Deprecated
    public Builder setView(View view, int viewSpacingLeft, int viewSpacingTop,
            int viewSpacingRight, int viewSpacingBottom) {
        P.mView = view;
        P.mViewLayoutResId = 0;
        P.mViewSpacingSpecified = true;
        P.mViewSpacingLeft = viewSpacingLeft;
        P.mViewSpacingTop = viewSpacingTop;
        P.mViewSpacingRight = viewSpacingRight;
        P.mViewSpacingBottom = viewSpacingBottom;
        return this;
    }

    public AlertDialog create() {
        final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
        P.apply(dialog.mAlert);
        dialog.setCancelable(P.mCancelable);
        if (P.mCancelable) {
            dialog.setCanceledOnTouchOutside(true);
        }
        dialog.setOnCancelListener(P.mOnCancelListener);
        dialog.setOnDismissListener(P.mOnDismissListener);
        if (P.mOnKeyListener != null) {
            dialog.setOnKeyListener(P.mOnKeyListener);
        }
        return dialog;
    }

    public AlertDialog show() {
        final AlertDialog dialog = create();
        dialog.show();
        return dialog;
    }
}

从源码可以看出,类中有个AlertController类型的属性,我们进入这个类文件可以看到定义了很多属性,比如Window View 上下左右边距等等,这个类的作用我们将会在2.1中提到。

我们新建一个PowerfulDialog仿照这个类,去继承Dialog,这里有人拿着刀就要说了,人家是继承的AppCompatDialog(这里我解释一下AppCompatDialog ,点进去看源码,发现它还是继承的Dialog,是为了简便的实现Material Design风格的弹框),所以很多人使用AlertDialog,基本都会是长这样:

md风格弹框.png

当然了,原生的AlertDialog可以通过Biulder中的setView方法去设置自定义的View但是,这样写达不到我们理想中的代码量尽可能少的要求,好了,现在我们直接继承Dialog,不熟悉Builder的建议可以直接copy一份源码,直接在源码上面修改。

我们会在PowerFulDialog中写一个Builder的静态内部类,这个静态类主要是用来通知控制器来设置这个弹框的事件、动画、属性及view的操作,所以我们一开始要扩展Dialog的功能,就可以在这里面配置编写相应的方法,在Builder内部的方法一般是可以链式操作的,达到灵活配置的作用。

在Builder静态内部类外部的方法除了构造函数以外,其他的方法应该是可以直接通过自定义的Dialog获取到的属性或事件等,这些操作大多是单个操作,主要是获取某些属性或者事件,比如获取Dialog中View的某个控件等。

好了 ,PowerFulDialog这个类的组成大概就是这个样子。下面我们来说说直接和弹框打交道的控制类PowerfulController.class


2.编写一个控制类(统一配置事件、参数和弹窗属性)

2.1 为什么要编写一个控制类
2.2 如何编写这个控制类
show之前也执行了create.png

3.编写一个View辅助类(用于实际直接操作view)

2.1 为什么要编写一个辅助类
2.2 如何编写一个辅助类
private View mDialogView=null;
//防止泄露
private SparseArray<WeakReference<View>> mViews;

public DialogViewHelper(Context mContext, int mViewLayoutResId) {
    this();
    mDialogView= LayoutInflater.from(mContext).inflate(mViewLayoutResId,null);
}

public DialogViewHelper() {
    mViews=new SparseArray<>();
}

/**
 * 设置布局
 * @param mView
 * */
public void setDialogView(View mView) {
    this.mDialogView=mView;
}

/**
 * 设置文本
 * @param viewId
 * @param text
 */
public void setText(int viewId, CharSequence text) {

    TextView tv=getView(viewId);
    if(tv!=null){
        tv.setText(text==null?"":text);
    }

}

/**
 * 设置文本颜色
 * @param viewId
 * @param colorRes
 */
public void setTextColor(Context context, int viewId, int colorRes) {

    TextView tv=getView(viewId);
    if(tv!=null){
        tv.setTextColor(context.getResources().getColor(colorRes==0? R.color.text_gray_normal:colorRes));
    }

}

/**
 * 设置图片
 * @param viewId
 * @param imgRes
 */
public void setImage(int viewId, int imgRes) {

    ImageView iv=getView(viewId);
    if(iv!=null){
        iv.setImageResource(imgRes==0? R.mipmap.default_head:imgRes);
    }

}
/**
 * 设置view的显示和隐藏
 * @param viewId
 * @param visibilityMode
 */
public void setVisiable(int viewId, int visibilityMode) {
    View v=getView(viewId);
    if(v!=null){
        v.setVisibility(visibilityMode);
    }
}
/**
 * 优化findViewById
 * @param viewId
 * @param <T>
 * @return
 */
public  <T extends View> T getView(int viewId) {
    View v=null;
    WeakReference<View> viewRefrence=mViews.get(viewId);
    if(viewRefrence!=null){
        v=viewRefrence.get();
    }
    if(v==null){
        v=mDialogView.findViewById(viewId);
        if(v!=null){
            mViews.put(viewId,new WeakReference<View>(v));
        }
    }
    return (T)v;
}

/**
 * 设置点击事件
 * @param viewId
 * @param onClickListener
 */
public void setOnclickListener(int viewId, View.OnClickListener onClickListener) {
    View v=getView(viewId);
    if(v!=null){
        v.setOnClickListener(onClickListener);
    }

}

public View getDialogView() {
    return mDialogView;
}

总结

1.做一个“懒”的程序员
2.时刻想着这段代码是否还可以被优化

老司机带你理解SparseArray:Android内存优化(使用SparseArray和ArrayMap代替HashMap)
老司机带你理解为什么要使用弱引用:Java 理论与实践
用弱引用堵住内存泄漏)

3.要善于去学习源码

在线查看Android源码地址:在线查看android源码

4.最后奉上我的demo

一个万能的Dialog demo地址:PowerfulDialog,为程序猿而生


请点赞,因为您的鼓励将是我写作的最大动力!

上一篇下一篇

猜你喜欢

热点阅读