Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

AlertDialog 源码分析及Bulider 模式打造万能的dialog

$
0
0

AlertDialog 源码分析及Bulider 模式打造万能的dialog

背景:不管我们在哪个项目中,都会用到dialog,会有不同的各种样式的dialog,Android自带的dialog采用的bulider模式,但是定制很差,往往项目中采用的dialog 都会需要一些动画,和弹出方式以及所用到的背景图片等等。这个时候我们往往采用自定义dialog,但是如果有很多不同样式的dialog,我们就需要创建好多个自定义的dialog类。这个时候就在想可不可以用一个dialog类,当然可以最简单的AlertDialog 就只有一个类,我们只需要传布局便可。

我们先看一下AlertDialog的源码,进行分析

查看源码是我们最好的学习方式


按住Ctrl右键点击查看AlertDialog的源码

public class AlertDialog extends AppCompatDialog implements DialogInterface 

可以看到AlertDialog是继承AppCompatDialog类,我们来看一下这个类

可以看到AppCompatDialog extends Dialog 由此可见Android 的AlertDialog 也是一个自定义成的Dialog,至此我们的猜想是成立的,既然成立那就看一下他是怎么实现的继续看AppCompatDialog的构造函数,至于AppCompatDialog implements AppCompatCallback 这个接口我们先不考虑,官方的是这样注释的:Implemented this in order for AppCompat to be able to callback in certain situations. 有兴趣的可以自己去了解下。

private AppCompatDelegate mDelegate;

public AppCompatDialog(Context context) {
    this(context, 0);
}

public AppCompatDialog(Context context, int theme) {
    super(context, getThemeResId(context, theme));

    // This is a bit weird, but Dialog's are typically created and setup before being shown,
    // which means that we can't rely on onCreate() being called before a content view is set.
    // To workaround this, we call onCreate(null) in the ctor, and then again as usual in
    // onCreate().
    getDelegate().onCreate(null);

    // Apply AppCompat's DayNight resources if needed
    getDelegate().applyDayNight();
}

protected AppCompatDialog(Context context, boolean cancelable,
        OnCancelListener cancelListener) {
    super(context, cancelable, cancelListener);
}

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

可以看到和平常的自定义dialog没什么不同,但是多了一个AppCompatDelegate这个类:
AppCompatDelegate-
This class represents a delegate which you can use to extend AppCompat’s support to any
可以看到一段官方的注释,英语不好的话就在线翻译吧–|,这句话的意思是:
此类表示一个委托,可以使用它来将AppCompat的支持扩展。也就是说这是一个委托类

从源码中我们可以看到他是将许多方法都交给了这个委托类去处理,这个类有什么作用呢?如果有谁做过夜间模式的一定知道这个类AppCompatDelegate AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); 这个类有木有很熟悉,如果有不熟悉的可以去看这个链接,http://godcoder.me/2016/07/28/Android%20Material%20Design%E7%B3%BB%E5%88%97%E4%B9%8B%E5%A4%9C%E9%97%B4%E6%A8%A1%E5%BC%8F/

  public ActionBar getSupportActionBar() {
    return getDelegate().getSupportActionBar();
}

@Override
public void setContentView(@LayoutRes int layoutResID) {
    getDelegate().setContentView(layoutResID);
}

@Override
public void setContentView(View view) {
    getDelegate().setContentView(view);
}

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    getDelegate().setContentView(view, params);
}

@Nullable
@Override
public View findViewById(@IdRes int id) {
    return getDelegate().findViewById(id);
}

@Override
public void setTitle(CharSequence title) {
    super.setTitle(title);
    getDelegate().setTitle(title);
}

@Override
public void setTitle(int titleId) {
    super.setTitle(titleId);
    getDelegate().setTitle(getContext().getString(titleId));
}

@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
    getDelegate().addContentView(view, params);
}

@Override
protected void onStop() {
    super.onStop();
    getDelegate().onStop();
}

由此可以看出,源码写的非常全面,实现了夜间和日间模式的切换,这是一个非常好的思路。

AppCompatDialog 类分析完了,说白就是我们平常自定义的dialog,只是AppCompatDialog 还实现了夜间模式

我们继续返回AlertDialog 类,接下来我们主要看这一段代码 276行

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

    /**
     * Creates a builder for an alert dialog that uses the default alert
     * dialog theme.
     * <p>
     * The default alert dialog theme is defined by
     * {@link android.R.attr#alertDialogTheme} within the parent
     * {@code context}'s theme.
     *
     * @param context the parent context
     */
    public Builder(@NonNull Context context) {
        this(context, resolveDialogTheme(context, 0));
    }

    /**
     * Creates a builder for an alert dialog that uses an explicit theme
     * resource.
     * <p>
     * The specified theme resource ({@code themeResId}) is applied on top
     * of the parent {@code context}'s theme. It may be specified as a
     * style resource containing a fully-populated theme, such as
     * {@link R.style#Theme_AppCompat_Dialog}, to replace all
     * attributes in the parent {@code context}'s theme including primary
     * and accent colors.
     * <p>

很明显采用了Builder模式,我们来看一下是怎么样实现的:
看一下这个类 AlertController 从字面意思上可以看出是Alert 的控制器,我们挑一段代码查看:346行
public void setIcon(int resId) {
mIcon = null;
mIconId = resId;

    if (mIconView != null) {
        if (resId != 0) {
            mIconView.setVisibility(View.VISIBLE);
            mIconView.setImageResource(mIconId);
        } else {
            mIconView.setVisibility(View.GONE);
        }
    }
}

可以看出他是给icon赋值,也就是说这个控制器我们也可以写在Builder中,在Builder扩展赋值,也可以向源码一样写一个控制器public static class AlertParams 传参单独控制和扩展。

源码分析完毕,从源码我们可以学习到很多思想,很有益处

学到了技术,打磨我们的工具

下面我只写简单的实现方法

自定义一个Dialog

模式 布局 弹窗样式 弹出动画等逻辑和点击事件交给Builder去处理

public class CommentDialog extends Dialog {
private View mView;
private Activity context;
private WindowManager.LayoutParams lp;
//这里我将Builder自定义一个类,分开单独处理
public CommentDialog(CommentBuilder builder) {
    super(builder.context);
    context = (Activity) builder.context;
    mView = builder.view;

}

public CommentDialog(CommentBuilder builder, int resStyle) {
    super(builder.context, resStyle);
    context = (Activity) builder.context;
    mView = builder.view;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(mView);
    setCanceledOnTouchOutside(false);
    Window window = getWindow();
    window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    window.getDecorView().setPadding(0, 0, 0, 0);
    lp = window.getAttributes();
    lp.gravity = Gravity.CENTER; //弹出位置
    window.setAttributes(lp);
}

}

Builder 类

public class CommentBuilder {
public Context context;
public View view;
private int resStyle = -1;
public boolean cancelTouchout;

public CommentBuilder(Context context) {
    this.context = context;
}

/**
* 布局
*
* @param resView
* @return
*/
public CommentBuilder view(int resView) {
    view = LayoutInflater.from(context).inflate(resView, null);
    return this;
}

/**
* dialog样式
*
* @param resStyle
* @return
*/
public CommentBuilder style(int resStyle) {
    this.resStyle = resStyle;
    return this;
}

/**
* 添加一个点击事件
*
* @param viewRes
* @param listener
* @return
*/
public CommentBuilder addViewOnclick(int viewRes, View.OnClickListener listener) {
    view.findViewById(viewRes).setOnClickListener(listener);
    return this;
}

/**
* 触摸dialog外部是否可以取消
*
* @param val false 不可dismiss
* @return
*/
public CommentBuilder cancelTouchout(boolean val) {
    cancelTouchout = val;
    return this;
}

public CommentDialog build() {
    if (resStyle != -1) {
        return new CommentDialog(this, resStyle);
    } else {
        return new CommentDialog(this);
    }
}

}

如何使用呢

 CommentBuilder builder = new CommentBuilder(CropperActivity.this);
    builder.view(R.layout.dialog_comment_upload_success_layout)
            .style(R.style.shareStyles)
            .setReview(R.id.dcus_tv, isReview)
            .addViewOnclick(R.id.ll_dialog_ok, CropperActivity.this);
    commentDialog = builder.build();
    commentDialog.show();

注:对于不同样式的Dialog,我们只需要传不同的布局和样式,赋值和改变值还可以在Builder中扩展增加对应的方法;点击事件我们可以写在当前的Activity 和 Fragment onClick()中实现,若想添加多个点击事件 可以添加多个.addViewOnclick(R.id.XX, CropperActivity.this);我们还可以学习源码一样,写一个控制器进行控制,具体大家可以自己实现。

至此谢幕,技术的提升:千里之行,始于足下

作者:su19921021 发表于2017/6/28 19:12:57 原文链接
阅读:10 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>