浅谈android中的自定义封装易用的Dialog

  好久没写android的博客,最近在做一个android的项目,里面用到我们经常用的一个控件就是对话框,大家都知道android自带的对话框是很丑的,android5.x之后除外.所以就出现了自定义view,自己定义美观的对话框.好我们就来自定义对话框.

整体思路:定义一个类然后去继承Dialog类,然后重写相应的构造器方法.大家都知道一般的对话框的创建过程都是来一个AlertDialog.Builder对象,然后使用一些set方法来设置标题内容以及设置一些自定义的view和点击的Button以及相应的点击事件并且可以采用链式编程一连串设置一些属性.但是有没有想过这些都是套路,都是很多类似的操作,只是数据不一样而已.以至于如果在一个项目中用到多次对话框,可能对话框样式有一些不一样,难道我们每一个对话框都要去走一个类似甚至相同的操作吗?这样貌似代码的冗余太强了.所以既然学了面向对象编程思想,我们不妨来试着封装一下,并且可以简化代码和实现复用代码,可以实现一次封装,以后只要拿来用即可.

既然要使用封装的面向对象思想,为了更好地体现封装优点,我们就一起来对比一下一般正常的套路和使用封装的不一样的套路两种方式实现:

首先,我们需要简单定义一下对话框的样式(注意:自定义对话框样式思想来源于网上的一名大神,必须尊重别人成果,然后个人在其基础上更多加入了封装使得使用起来非常方便),先定义一个主题styles.xml:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    <style name="text_18_ffffff">
        <item name="android:textSize">18.0dip</item>
        <item name="android:textColor">#ffffffff</item>
    </style>

    <style name="text_16_666666">
        <item name="android:textSize">16.0dip</item>
        <item name="android:textColor">#ff666666</item>
    </style>

    <style name="sdw_white">
        <item name="android:shadowColor">#7fffffff</item>
        <item name="android:shadowDx">0.0</item>
        <item name="android:shadowDy">0.65</item>
        <item name="android:shadowRadius">1.0</item>
    </style>

    <style name="sdw_79351b">
        <item name="android:shadowColor">#ff79351b</item>
        <item name="android:shadowDx">0.0</item>
        <item name="android:shadowDy">1.0</item>
        <item name="android:shadowRadius">1.0</item>
    </style>

    <style name="text_15_ffffff_sdw" parent="@style/sdw_79351b">
        <item name="android:textSize">15.0dip</item>
        <item name="android:textColor">#ffffffff</item>
    </style>

    <style name="text_15_666666_sdw" parent="@style/sdw_white">
        <item name="android:textSize">15.0dip</item>
        <item name="android:textColor">#ff666666</item>
    </style>

    <style name="Dialog" parent="android:style/Theme.Dialog">
        <item name="android:background">#00000000</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
    </style>
    <style name="weekName">
        <item name="android:layout_width">0.0dip</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_gravity">center</item>
        <item name="android:layout_weight">1.0</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">#ffffffff</item>
        <item name="android:textSize">14sp</item>
    </style>

    <style name="main_bar_text_style">
        <item name="android:textColor">#ffffffff</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textSize">18sp</item>
    </style>
</resources>

再来四个drawable资源文件,两个是确定和取消按钮的样式,其他的两个用于弹出框整体浮层的背景和对话框整体的一个样式

<?xml version="1.0" encoding="utf-8"?>
<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
        android:drawable="@drawable/btn_ok_pressed" />
    <item android:drawable="@drawable/btn_ok_normal" />
</selector>

<?xml version="1.0" encoding="utf-8"?>
<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="true" android:state_pressed="true" android:drawable="@drawable/btn_cancel_pressed" />
    <item android:state_enabled="true" android:drawable="@drawable/btn_cancel_normal" />
</selector>


<?xml version="1.0" encoding="utf-8"?>
<layer-list
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <gradient android:startColor="#ffe7e7e7" android:endColor="#ffe7e7e7" android:angle="0.0" />
            <corners android:topLeftRadius="0.0dip" android:topRightRadius="0.0dip" android:bottomLeftRadius="5.0px" android:bottomRightRadius="5.0px" />
        </shape>
    </item>
</layer-list>

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid android:color="#56abe4" />

    <stroke
        android:width="0.5dp"
        android:color="#56abe4" />

    <corners android:radius="15dp" />

</shape>

布局文件:

<pre name="code" class="html"><?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true"
    android:orientation="vertical"
    android:padding="20.0dip" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="@drawable/bg_bombbox"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/title"
            style="@style/text_18_ffffff"
            android:layout_width="fill_parent"
            android:layout_height="40.0dip"
            android:gravity="center"
            android:text="@string/title_alert"
            android:visibility="visible" />

        <LinearLayout
            android:id="@+id/content"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center" >

            <TextView
                android:id="@+id/message"
                style="@style/text_16_666666"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:gravity="left|center"
                android:lineSpacingMultiplier="1.5"
                android:minHeight="120.0dip"
                android:paddingBottom="15.0dip"
                android:paddingLeft="20.0dip"
                android:paddingRight="20.0dip"
                android:paddingTop="15.0dip" />
        </LinearLayout>

        <View
            android:layout_width="fill_parent"
            android:layout_height="1.0px"
            android:background="#ffd0d0d0" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="60.0dip"
            android:layout_gravity="bottom"
            android:background="@drawable/dialog_bottom_bg"
            android:gravity="center"
            android:orientation="horizontal" >

            <Button
                android:id="@+id/positiveButton"
                style="@style/text_15_ffffff_sdw"
                android:layout_width="114.0dip"
                android:layout_height="40.0dip"
                android:background="@drawable/btn_ok_selector"
                android:gravity="center"
                android:text="@string/ok" />

            <Button
                android:id="@+id/negativeButton"
                style="@style/text_15_666666_sdw"
                android:layout_width="114.0dip"
                android:layout_height="40.0dip"
                android:layout_marginLeft="20.0dip"
                android:background="@drawable/btn_cancel_selector"
                android:gravity="center"
                android:text="@string/cancel" />
        </LinearLayout>
    </LinearLayout>

</FrameLayout>


 
 

当然你可以去自定义你的对话框的样式和布局,可以根据个人喜好来定,但是这不是今天的重点.

CustomDialog自定义View类:

package com.mikyou.myguardian.myview;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;



public class CustomDialog extends Dialog {

	public CustomDialog(Context context) {
		super(context);
	}

	public CustomDialog(Context context, int theme) {
		super(context, theme);
	}

	public static class Builder {
		private Context context;
		private String title;
		private String message;
		private String positiveButtonText;
		private String negativeButtonText;
		private View contentView;
		private OnClickListener positiveButtonClickListener;
		private OnClickListener negativeButtonClickListener;

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

		public Builder setMessage(String message) {
			this.message = message;
			return this;
		}

		/**
		 * Set the Dialog message from resource
		 * 
		 * @param title
		 * @return
		 */
		public Builder setMessage(int message) {
			this.message = (String) context.getText(message);
			return this;
		}

		/**
		 * Set the Dialog title from resource
		 * 
		 * @param title
		 * @return
		 */
		public Builder setTitle(int title) {
			this.title = (String) context.getText(title);
			return this;
		}

		/**
		 * Set the Dialog title from String
		 * 
		 * @param title
		 * @return
		 */

		public Builder setTitle(String title) {
			this.title = title;
			return this;
		}

		public Builder setContentView(View v) {
			this.contentView = v;
			return this;
		}

		/**
		 * Set the positive button resource and it's listener
		 * 
		 * @param positiveButtonText
		 * @return
		 */
		public Builder setPositiveButton(int positiveButtonText,
				OnClickListener listener) {
			this.positiveButtonText = (String) context
					.getText(positiveButtonText);
			this.positiveButtonClickListener = listener;
			return this;
		}

		public Builder setPositiveButton(String positiveButtonText,
				OnClickListener listener) {
			this.positiveButtonText = positiveButtonText;
			this.positiveButtonClickListener = listener;
			return this;
		}

		public Builder setNegativeButton(int negativeButtonText,
				OnClickListener listener) {
			this.negativeButtonText = (String) context
					.getText(negativeButtonText);
			this.negativeButtonClickListener = listener;
			return this;
		}

		public Builder setNegativeButton(String negativeButtonText,
				OnClickListener listener) {
			this.negativeButtonText = negativeButtonText;
			this.negativeButtonClickListener = listener;
			return this;
		}

		public CustomDialog create() {
			LayoutInflater inflater = (LayoutInflater) context
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			// instantiate the dialog with the custom Theme
			final CustomDialog dialog = new CustomDialog(context, R.style.Dialog);
			View layout = inflater.inflate(R.layout.dialog_normal_layout, null);
			dialog.addContentView(layout, new LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
			// set the dialog title
			((TextView) layout.findViewById(R.id.title)).setText(title);
			// set the confirm button
			if (positiveButtonText != null) {
				((Button) layout.findViewById(R.id.positiveButton))
						.setText(positiveButtonText);
				if (positiveButtonClickListener != null) {
					((Button) layout.findViewById(R.id.positiveButton))
							.setOnClickListener(new View.OnClickListener() {
								public void onClick(View v) {
									positiveButtonClickListener.onClick(dialog,
											DialogInterface.BUTTON_POSITIVE);
								}
							});
				}
			} else {
				// if no confirm button just set the visibility to GONE
				layout.findViewById(R.id.positiveButton).setVisibility(
						View.GONE);
			}
			// set the cancel button
			if (negativeButtonText != null) {
				((Button) layout.findViewById(R.id.negativeButton))
						.setText(negativeButtonText);
				if (negativeButtonClickListener != null) {
					((Button) layout.findViewById(R.id.negativeButton))
							.setOnClickListener(new View.OnClickListener() {
								public void onClick(View v) {
									negativeButtonClickListener.onClick(dialog,
											DialogInterface.BUTTON_NEGATIVE);
								}
							});
				}
			} else {
				// if no confirm button just set the visibility to GONE
				layout.findViewById(R.id.negativeButton).setVisibility(
						View.GONE);
			}
			// set the content message
			if (message != null) {
				((TextView) layout.findViewById(R.id.message)).setText(message);
			} else if (contentView != null) {
				// if no message set
				// add the contentView to the dialog body
				((LinearLayout) layout.findViewById(R.id.content))
						.removeAllViews();
				((LinearLayout) layout.findViewById(R.id.content)).addView(
						contentView, new LayoutParams(
								LayoutParams.FILL_PARENT,
								LayoutParams.FILL_PARENT));
			}
			dialog.setContentView(layout);
			return dialog;
		}

	}
}

到这里一个自定义View大体工作就完成了,可以测试了,然后通过测试编码过程然后一步一步优化即可.

测试Activity的布局只有三个Button,用于点击后弹出三个不一样的对话框,所以布局很简单就贴了,那么我们还是来一起看下MainActivity

这种使用一般普通的方法.

package com.mikyou.dialogdemo;

import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import com.mikyou.dialogdemo.myview.CustomDialog;

public class MainActivity extends AppCompatActivity {

    public void dialog1(View view){//弹出第一个对话框
        CustomDialog.Builder dialog=new CustomDialog.Builder(this);
        dialog.setTitle("测试一")
                .setMessage("普通文本提示信息对话框")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                        Toast.makeText(MainActivity.this, "确定", Toast.LENGTH_SHORT).show();
                    }
                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
                Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
            }
        }).create().show();
    }
    public void dialog2(View view){//弹出第二个对话框
        CustomDialog.Builder dialog=new CustomDialog.Builder(this);
        dialog.setTitle("测试二")
                .setMessage("密码错误")
                .setPositiveButton("重新输入", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                        Toast.makeText(MainActivity.this, "重新输入", Toast.LENGTH_SHORT).show();
                    }
                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
                Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
            }
        }).create().show();
    }
    public void dialog3(View view){//弹出第三个对话框
        View customView =View.inflate(this,R.layout.input_text_dialog,null);
        CustomDialog.Builder dialog=new CustomDialog.Builder(this);
        dialog.setTitle("测试三带有自定义View的布局")
                .setContentView(customView)//设置自定义customView
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                        Toast.makeText(MainActivity.this, "确定", Toast.LENGTH_SHORT).show();
                    }
                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
                Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
            }
        }).create().show();
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
    }
}
运行结果:



注意:我们可以很清楚从测试的activity中可以发现,这三个方法中的代码几乎是一样的,只是设置内容和布局不一样,写了这么多代码实际上更多是的毫无技术含量,有的人说不碍事粘贴复制呗,粘贴复制这种最傻的方式我们就不说了.所以我们能不能想想将他们共同的部分抽取出来,把不一样或者会变化的内容作为参数传进去.

封装的思路:

看到这个第一时间就是写一个类利用设计模式中的组合模式的思想,在我们的自定义的类中保存CustomDialog引用,然后就去操作这个类.

然后仔细想下需要注意几点:

第一点,我们可以看到确定和取消按钮都有一个点击事件的回调监听器,这个监听器的设计的初衷就是为了让用户点击确定后去实现确定的逻辑代码,点击取消后去实现取消的逻辑代码,所以必须将该接口抛给用户去处理,因为确定和取消后的逻辑并不是我们所能控制的.所以最后就可以得出就是我们必须要去自定义一个事件监听器,让我们自定义这个监听器去绑定和联系确定和取消点击事件,也就是可以理解为把原来在确定和取消点击事件做的业务逻辑委派给我们自己定义事件监听器统一管理和操作.

第二点,就是传数据,具体要传入哪些数据呢?这不是我说了算,也不是你说了算,而是根据里面逻辑具体需要什么,并且传入数据是变化的不是固定的.

所以我们就可以大概罗列一下需要哪些参数:

                 1,首先上下文对象context(因为在初始化CustomDialog.Builder对象需要,以及如果传入一个自定布局加载成一个view对象的时候需要上下文对象)

                                                              2,然后就是传入标题(title),内容(message),布局id,这里需要注意一下,就是message和布局id(用于自定义view对话框)是互斥关系

也就是设置了内容再去设置布局id的view后者是无效有冲突.所以,我们也很容易想到一点就是初始化我们的这个封装的对话框应该有两种方式分别是文本类型和自定义view界面类型,实际上也就引出了我们这个自定义的类中应该两个构造器重载,一个用与初始化文本类型,一个是用于自定义view类型,然后还需要传入两个button需要显示文本内容

第三点,就是是否暴露一些数据接口用于方便用户后期的操作,这里有时候不要,看实际情况.仔细想想我们这里需要吗?实际上是需要就是我们在使用自定义view的对象,因为我们有时候需要去操作自定义布局中的一些控件,就比如上面最后例子就是输入密码,可能我们需要获得EditText中的内容,首先需要拿到EditText对象,所以需要传出一个自定义布局的view对象,所以我就提供一个setter和getter方法留给外部用户使用,这里为了后面的使用方便我就把这个view对象使用回调方法将其回调出去.

那么具体来看下代码的实现:

package com.mikyou.dialogdemo.myview;

import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.widget.Toast;

/**
 * @自定义封装的通用对话框:MikyouCommonDialog
 *
 * Created by mikyou on 16-7-21.
 * @param dialogTitle 对话框的标题
 * @param dialogMessage 对话框的内容
 * @param positiveText 表示是确定意图的按钮上text文本
 * @param negativeText 表示是取消意图的按钮上text文本
 * @param customeLayoutId 对话框自定义布局的id 若没有自定义布局  默认传入0
 * @param context 上下文对象
 * @param dialogView 自定义布局的View对象,提供被外界访问的接口get和set方法,并且利用自定义的监听器将view对象回调出去
 * @param listener 监听器 用于将确定和取消意图的两个点击事件监听器,合成一个监听器,并传入一个标记变量区别确定和取消意图
 */
public class MikyouCommonDialog {
    private Context context;
    private int customeLayoutId;
    private String dialogTitle;
    private String dialogMessage;
    private String positiveText;
    private String negativeText;

    private View dialogView;
    private OnDialogListener listener;
    //带有自定义view的构造器
    public MikyouCommonDialog(Context context, int customeLayoutId, String dialogTitle, String positiveText, String negativeText) {
        this.context = context;
        this.customeLayoutId = customeLayoutId;
        this.dialogTitle = dialogTitle;
        this.positiveText = positiveText;
        this.negativeText = negativeText;
        this.dialogView=View.inflate(context,customeLayoutId,null);
    }
    //不带自定义view的构造器
    public MikyouCommonDialog(Context context, String dialogMessage,String dialogTitle, String positiveText, String negativeText) {
        this.context = context;
        this.dialogTitle = dialogTitle;
        this.dialogMessage = dialogMessage;
        this.positiveText = positiveText;
        this.negativeText = negativeText;
    }

    public View getDialogView() {
        return dialogView;
    }

    public void setDialogView(View dialogView) {
        this.dialogView = dialogView;
    }

    public void showDialog(){
        CustomDialog.Builder dialog=new CustomDialog.Builder(context);
        dialog.setTitle(dialogTitle);//设置标题
        //注意:dialogMessage和dialogView是互斥关系也就是dialogMessage存在dialogView就不存在,dialogView不存在dialogMessage就存在
        if (dialogMessage!=null){
            Toast.makeText(context, "sddsdsds", Toast.LENGTH_SHORT).show();
            dialog.setMessage(dialogMessage);//设置对话框内容
        }else{

            dialog.setContentView(dialogView);//设置对话框的自定义view对象
        }
        /**
         * 尽管有两个点击事件监听器,可以通过我们自定义的监听器设置一个标记变量,从而可以实现将两个点击事件合并成一个
         * 监听器OnDialogListener
         * */
        //确定意图传入positive标记值
        dialog.setPositiveButton(positiveText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int which) {
                dialogInterface.dismiss();
                if (listener!=null){
                    listener.dialogListener("positive",dialogView,dialogInterface,which);
                }
            }
            //取消意图传入negative标记值
        }).setNegativeButton(negativeText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int which) {
                dialogInterface.dismiss();
                if (listener!=null){
                    listener.dialogListener("negative",dialogView,dialogInterface,which);
                }
            }
        }).create().show();
    }
    //注册监听器方法
    public MikyouCommonDialog setOnDiaLogListener(OnDialogListener listener){
        this.listener=listener;
        return this;//把当前对象返回,用于链式编程
    }
    //定义一个监听器接口
    public interface OnDialogListener{
        //customView 这个参数需要注意就是如果没有自定义view,那么它则为null
        public void dialogListener(String btnType, View customView, DialogInterface dialogInterface, int which);
    }
}

实现方式:

  private void popInputPwdDialog() {
        new MikyouCommonDialog(this,R.layout.input_pwd_dialog,"输入安全密码","确定","取消")
                .setOnDiaLogListener(new MikyouCommonDialog.OnDialogListener() {
                    @Override
                    public void dialogListener(String btnType, View customView, DialogInterface dialogInterface, int which) {
                        switch (btnType){
                            case "positive"://点击确定按钮的操作
                                EditText eTPwd=(EditText) customView.findViewById(R.id.input_pwd);
                                checkAndSavePwd("input",eTPwd.getText().toString(),"");
                                break;
                            case "negative"://点击取消按钮的操作
                                break;
                        }
                    }
                }).showDialog();

    }
测试的Activity那么我们一起来对比一下封装后的代码:

package com.mikyou.dialogdemo;

import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import com.mikyou.dialogdemo.myview.MikyouCommonDialog;

public class MainActivity extends AppCompatActivity {

    public void dialog1(View view){//弹出第一个对话框
   new MikyouCommonDialog(this,"普通文本提示信息对话框","测试一","确定","取消")
           .setOnDiaLogListener(new MikyouCommonDialog.OnDialogListener() {
               @Override
               public void dialogListener(String btnType, View customView, DialogInterface dialogInterface, int which) {
                   switch (btnType){
                       case "positive":
                           Toast.makeText(MainActivity.this, "确定", Toast.LENGTH_SHORT).show();
                           break;
                       case "negative":
                           Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
                           break;
                   }
               }
           }).showDialog();
    }
    public void dialog2(View view){//弹出第二个对话框
       new MikyouCommonDialog(this,"密码错误","测试二","重新输入","取消")
               .setOnDiaLogListener(new MikyouCommonDialog.OnDialogListener() {
                   @Override
                   public void dialogListener(String btnType, View customView, DialogInterface dialogInterface, int which) {
                       switch (btnType){
                           case "positive":
                               Toast.makeText(MainActivity.this, "重新输入", Toast.LENGTH_SHORT).show();
                               break;
                           case "negative":
                               Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
                               break;
                       }
                   }
               }).showDialog();
    }
   public void dialog3(View view){//弹出第三个对话框
       new MikyouCommonDialog(this,R.layout.input_text_dialog,"测试三带有自定义View的布局","确定","取消")
               .setOnDiaLogListener(new MikyouCommonDialog.OnDialogListener() {
                   @Override
                   public void dialogListener(String btnType, View customView, DialogInterface dialogInterface, int which) {
                       switch (btnType){
                           case "positive":
                               Toast.makeText(MainActivity.this, "确定", Toast.LENGTH_SHORT).show();
                               break;
                           case "negative":
                               Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
                               break;
                       }
                   }
               }).showDialog();
   }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
    }
}

是不是感觉比开始那种最原始方法要好些,代码逻辑更清晰些.我们现在的这种封装还能进一步优化吗?我们都知道封装思想在于什么,在于用户外部接口调用传入数据不一样进行操作不一样,但是整个封装的框架是一样的.所以仔细观察一下上面的代码,是不是会发现那三个方法中的switch显得很鸡肋,因为在三个方法中还需要去判断是否是确定还是取消类型,并且在封装类里面我们还得把标记的字符串变量通过回调方法回调出来,感觉就有点多此一举了.

优化思想:

            就是我们不需要传入标记变量并且不需要在外部调用的时候使用switch来判断,而是采用两个回调方法,一个是确定类型的回调方法,另一个则是取消类型的回调方法.然后分别在不同类型回调方法实现各自的业务逻辑.这种方式实际上最原始那种方式也就是这样处理.

看下优化的代码:

package com.mikyou.myguardian.myview;

import android.content.Context;
import android.content.DialogInterface;
import android.view.View;

/**
 * @自定义封装的通用对话框:MikyouCommonDialog
 *
 * Created by mikyou on 16-7-21.
 * @param dialogTitle 对话框的标题
 * @param dialogMessage 对话框的内容
 * @param positiveText 表示是确定意图的按钮上text文本
 * @param negativeText 表示是取消意图的按钮上text文本
 * @param customeLayoutId 对话框自定义布局的id 若没有自定义布局  默认传入0
 * @param context 上下文对象
 * @param dialogView 自定义布局的View对象,提供被外界访问的接口get和set方法,并且利用自定义的监听器将view对象回调出去
 * @param listener 监听器 用于将确定和取消意图的两个点击事件监听器,合成一个监听器,并传入一个标记变量区别确定和取消意图
 */
public class MikyouCommonDialog2 {
    private Context context;
    private int customeLayoutId;
    private String dialogTitle;
    private String dialogMessage;
    private String positiveText;
    private String negativeText;

    private View dialogView;
    private OnDialogListener listener;
    //带有自定义view的构造器
    public MikyouCommonDialog2(Context context, int customeLayoutId, String dialogTitle, String positiveText, String negativeText) {
        this.context = context;
        this.customeLayoutId = customeLayoutId;
        this.dialogTitle = dialogTitle;
        this.positiveText = positiveText;
        this.negativeText = negativeText;
        this.dialogView=View.inflate(context,customeLayoutId,null);
    }
    //不带自定义view的构造器
    public MikyouCommonDialog2(Context context, String dialogMessage, String dialogTitle, String positiveText, String negativeText) {
        this.context = context;
        this.dialogTitle = dialogTitle;
        this.dialogMessage = dialogMessage;
        this.positiveText = positiveText;
        this.negativeText = negativeText;
    }

    public View getDialogView() {
        return dialogView;
    }

    public void setDialogView(View dialogView) {
        this.dialogView = dialogView;
    }

    public void showDialog(){
        CustomDialog.Builder dialog=new CustomDialog.Builder(context);
        dialog.setTitle(dialogTitle);//设置标题
        //注意:dialogMessage和dialogView是互斥关系也就是dialogMessage存在dialogView就不存在,dialogView不存在dialogMessage就存在
        if (dialogMessage!=null){
            dialog.setMessage(dialogMessage);//设置对话框内容
        }else{
            dialog.setContentView(dialogView);//设置对话框的自定义view对象
        }
        dialog.setPositiveButton(positiveText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int which) {
                dialogInterface.dismiss();
                if (listener!=null){
                    listener.dialogPositiveListener(dialogView,dialogInterface,which);
                }
            }
        }).setNegativeButton(negativeText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int which) {
                dialogInterface.dismiss();
                if (listener!=null){
                    listener.dialogNegativeListener(dialogView,dialogInterface,which);
                }
            }
        }).create().show();
    }
    //注册监听器方法
    public MikyouCommonDialog2 setOnDiaLogListener(OnDialogListener listener){
        this.listener=listener;
        return this;//把当前对象返回,用于链式编程
    }
    //定义一个监听器接口
    public interface OnDialogListener{
        //customView 这个参数需要注意就是如果没有自定义view,那么它则为null
        public void dialogPositiveListener(View customView, DialogInterface dialogInterface, int which);
        public void dialogNegativeListener(View customView, DialogInterface dialogInterface, int which);
    }
}

测试方式:

 private void popInputPwdDialog() {
        new MikyouCommonDialog2(this,R.layout.input_pwd_dialog,"输入安全密码","确定","取消")
                .setOnDiaLogListener(new MikyouCommonDialog2.OnDialogListener() {
                    @Override
                    public void dialogPositiveListener(View customView, DialogInterface dialogInterface, int which) {//点击确定按钮的操作
                        EditText eTPwd=(EditText) customView.findViewById(R.id.input_pwd);
                        checkAndSavePwd("input",eTPwd.getText().toString(),"");
                    }
                    @Override
                    public void dialogNegativeListener(View customView, DialogInterface dialogInterface, int which) {//点击取消按钮的操作
+
                    }
                }).showDialog();
    }

测试Activity:

package com.mikyou.dialogdemo;

import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import com.mikyou.dialogdemo.myview.MikyouCommonDialog;

public class MainActivity extends AppCompatActivity {
    public void dialog1(View view){//弹出第一个对话框
        new MikyouCommonDialog(this,"普通文本提示信息对话框","测试一","确定","取消")
                .setOnDiaLogListener(new MikyouCommonDialog.OnDialogListener() {
                    @Override
                    public void dialogPositiveListener(View customView, DialogInterface dialogInterface, int which) {
                        Toast.makeText(MainActivity.this, "确定", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void dialogNegativeListener(View customView, DialogInterface dialogInterface, int which) {
                        Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
                    }
                }).showDialog();
    }
    public void dialog2(View view){//弹出第二个对话框
        new MikyouCommonDialog(this,"密码错误","测试二","重新输入","取消")
                .setOnDiaLogListener(new MikyouCommonDialog.OnDialogListener() {
                    @Override
                    public void dialogPositiveListener(View customView, DialogInterface dialogInterface, int which) {
                        Toast.makeText(MainActivity.this, "重新输入", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void dialogNegativeListener(View customView, DialogInterface dialogInterface, int which) {
                        Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
                    }
                }).showDialog();
    }
    public void dialog3(View view){//弹出第三个对话框
        new MikyouCommonDialog(this,R.layout.input_text_dialog,"测试三带有自定义View的布局","确定","取消")
                .setOnDiaLogListener(new MikyouCommonDialog.OnDialogListener() {
                    @Override
                    public void dialogPositiveListener(View customView, DialogInterface dialogInterface, int which) {
                        Toast.makeText(MainActivity.this, "确定", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void dialogNegativeListener(View customView, DialogInterface dialogInterface, int which) {
                        Toast.makeText(MainActivity.this, "取消", Toast.LENGTH_SHORT).show();
                    }
                }).showDialog();
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
    }
}

到这里,基本封装已经完了,以后就可以直接使用该自定义的类就可以实现一个自定义的对话框,感觉还是蛮实用的.



猜你喜欢

转载自blog.csdn.net/u013064109/article/details/51990526