自定义dialog的方式,以及需要注意事项

来源:互联网 发布:淘宝怎么进淘宝客 编辑:程序博客网 时间:2024/06/04 19:35

上一次总结记录了dialog的载体是什么(window),在某种情况下setContentView会重置LayoutParams导致我们之前设置好的LayoutParams无效,为什么dialog依赖于activity;
这次总结一下自定义dialog的一些方式,以及需要注意的一些事项,好吧下面就来记录一下自定义dialog的方式:
方式一:

public static AlertDialog showAddProductDialog(Context context, View.OnClickListener clickListener) {        final AlertDialog dialog = new AlertDialog.Builder(context).setCancelable(false).create();        dialog.show();        Window window = dialog.getWindow();        window.setContentView(R.layout.dialog_price_add);        window.findViewById(R.id.dialog_price_cancel).setOnClickListener(clickListener);        window.findViewById(R.id.dialog_price_confirm).setOnClickListener(clickListener);        return dialog;    }

上面这种方式类自动dialog,个人觉得是最简单不过的了,也是用的比较多的方式。好了这种方式简单归简单其实也是有需要我们注意的地方的。
比如在上一次dialog的总结中说到的,在show()之前设置LayoutParams,在show()后setContentView()就会重置了原来设置的LayoutParams;那么肯能有人会提出质疑,为什么不可以在show()之前去setContentView呢?下面就是要说说为什么不在show()之前去setContentView,其实是因为会抛出
throw new AndroidRuntimeException(“requestFeature() must be called before adding content”);这个异常,所以不可以在show()之前调用,那到底为什么呢?看下面的源码:

@Override    public void setContentView(int layoutResID) {       ...        if (mContentParent == null) {            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        ...    }

当这mContentParent == null就去调用installDecor();接着看:

private void installDecor() {        if (mDecor == null) {            mDecor = generateDecor();            ...        }        if (mContentParent == null) {            mContentParent = generateLayout(mDecor);            ...        }    }

由上面可以看出当mDecor == null创建mDecor这里不是我们的重点滤过,接着看当mContentParent为空的时候调用generateLayout(mDecor);创建mContentParent,好了setContentView介绍到此为止,毕竟后面的个人觉得都不重要;

这个时候问题就来了,就上面的那部分源码还不足以说明在show()之前setContentView会抛出异常的问题,这个时候我们回归到setContentView执行完以后调用的show()方法,从上一次总结提到过onCreate()方法是在show()里面调用的,我们就去看看AlertDialog的onCreate方法,如下:

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

下面接着看mAlert.installContent();代码如下:

   public void installContent() {        /* We use a custom title so never request a window title */        mWindow.requestFeature(Window.FEATURE_NO_TITLE);        int contentView = selectContentView();        mWindow.setContentView(contentView);        setupView();        setupDecor();    }

这里我们就看第一句mWindow.requestFeature(Window.FEATURE_NO_TITLE);其实异常就是从这里面抛出来的,看看里面的代码:

    @Override    public boolean requestFeature(int featureId) {        if (mContentParent != null) {            throw new AndroidRuntimeException("requestFeature() must be called before adding content");        }        ...    }

到这里也就明朗了,当我们调用setContentView以后mContentParent已经创建完成了,但是show方法里面经过层层调用最终调用到了mWindow.requestFeature(Window.FEATURE_NO_TITLE);这个方法,它里面里有这么一个判断mContentParent != null的时候就抛出异常,由于mContentParent在onCreate前就已经存在了所以,当调用requestFeature的时候就会破除异常,所以不可以在show()之前调用setContentView方法。

同理如果要需要设置mWindow.requestFeature那就必须要在onCreate之前去掉调用,也就是在show之前去调用,不也会因为mContentParent已经存而导致抛出异常;

至于LayoutParams的设置时期在这里就不多讲了,在这里写链接内容 就已经讲述过了。

下面就继续说说第二种自定义dialog的方式:
第二种自定义dialog的方式就是继承dialog或者alertDialog,下面就看一个例子:

public class StockDialog extends AlertDialog implements View.OnClickListener {    private Context context;    private TextView title, tv1, tv2;    private EditText ed1, ed2, ed3;    private Button btn;    private ImageView img;    private BasicVHolder holder;    public StockDialog(Context context) {        super(context);        this.context = context;    }    public StockDialog(Context context, int theme) {        super(context, theme);        this.context = context;    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        View view = LayoutInflater.from(context).inflate(R.layout.dialog_stock_info, null);        setContentView(view);        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);    }    public void setTitle(String str) {        holder.setText(R.id.stock_info_dialog_title, str);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.stock_info_dialog_img:                dismiss();                break;            case R.id.stock_info_dialog_btn:                String str1 = ((EditText) holder.getChildeView(R.id.stock_info_dialog_ed1)).getText().toString();                String str2 = ((EditText) holder.getChildeView(R.id.stock_info_dialog_ed2)).getText().toString();                String str3 = ((EditText) holder.getChildeView(R.id.stock_info_dialog_ed3)).getText().toString();                dismiss();                break;        }    }}

上面这个例子是通过继承AlertDialog来实现自定义dialog需求的,下面就讲一下其中需要注意的地方,首先继承AlertDialog如果你需要设置dialog的Feature(调用requestFeature)的话,那么久不许要注意requestFeature的调用时期了,要在super.onCreate之前调用,至于为什么这里就不在细讲了,上面已经接绍过了,但是如果你继承的是dialog,就可以不必再super.onCreate方法前调用,因为dialog中的onCreate方法是一个空方法。

如果继承AlertDialog来自定义那么如果需要用到EditText的话,那么还需要加上上这么一句:

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);FLAG_NOT_FOCUSABLE 表示window不会拦截所有的焦点FLAG_ALT_FOCUSABLE_IM 表示window不会拦截键盘输入时间的焦点

如果不设置这一句的话EditText就无法获得焦点,也就是无法输入信息,那么到底是为什么呢?下面就来看看以下这个方法:

private void setupCustomContent(ViewGroup customPanel) {        final View customView;        if (mView != null) {            customView = mView;        } else if (mViewLayoutResId != 0) {            final LayoutInflater inflater = LayoutInflater.from(mContext);            customView = inflater.inflate(mViewLayoutResId, customPanel, false);        } else {            customView = null;        }        final boolean hasCustomView = customView != null;        if (!hasCustomView || !canTextInput(customView)) {            mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);        }        ...    }

从上面的代码可以看出来一般情况下customView!=null,说以就会给Window设置Flags,来看看setFlags中的传入参数FLAG_ALT_FOCUSABLE_IM,这个常量表示Window拦截了所有的焦点,说以要想对EditText进行输入操作,你就必须释放焦点的拦截,所有需要调用上面的那一句话,如果继承的是dialog就不需要释放焦点拦截。

对应继承父类来实现自定义dialog,如果需设置LayoutParams只要在setContentView以后调用就可以了,这里就不详细说了,阅读全文就可以知道了。

关于继承dialog来实现自定义,可以去参考参考一下AlertDialog,关于AlertDialog里面的实现是通过一个构建者模式来完成AlertDialog的创建过程,AlertDialog.Bulider类把创建过程委托给了AlertController,所有负责的创建操作都是在这个类里面实现的,Bulider类就相当于一个桥梁的作用。这样可以似的构建与表现分离开来,可以通过同样的构建过程创建不同的表现形态。

到这里,本次的总结就告一段落。希望我的个人记录可以对其他人也带来帮助。

0 0
原创粉丝点击