浅谈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"
           
            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"
               
                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"
               
                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"
               
                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() {
    }
}

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



本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
1.android 的UI线程阻超过5秒就会引发ANR(Application not responding)异常,如果等待超过3秒,你就会失去用户。 2.在android中组件的启动线程被称为主线程(也称UI线程),一般不在这线程中进行耗时的工作,所以我们将线程分为两种,分别是main thread和worker thread,当应用程度运行是时,系统默认的启动线程就是主线程,主要用来加载UI,完成和用户间的交互,所有这些都在同一个线程中进行,所以不能在这个线程中进行耗时工作,不能阻塞UI,androi
接触过自定义控件的开发者一看,笑了,立马关了网页。但是…你真的知道怎么绘制居中文本吗? 我不会?开玩笑,不就是: X=控件宽度/2 - 文本宽度/2;Y=控件高度/2 + 文本宽度/2 好吧,那我试一下。 1.自定义控件基本步骤 自定义View的属性 在View的构造方法中获得我们自定义的属性 #重写onMesure # 重写onDraw OK,简单,直接干起来。 1. 自定义View的属性 按照最简单的来,属性有:文本,文本颜色,文本大小。 我们在 /value/attrs.xml 中这么写: ?xml
首先,自定义控件分为三类: 自定义的组合控件 继承View的自定义控件 继承ViewGroup的自定义控件 在这里,我要写的是第二种,也就是继承自View的自定义控件,第一种自定义的组合控件,我已经写过了,可以在我的博客中可以找到 现在来看一下继承View的自定义控件 首先,需要写一个类继承自View,那么,它也有三个构造方法,有一个参数的构造方法实在代码中new这个自定义控件时被调用;有两个参数的构造方法是在布局中使用这个自定义控件的时候调用,有三个参数的构造方法,实在使用到这个自定义控件的样式时被调用

WindowManager的分析 - 2016-07-22 18:07:46

一、Window和WindowManager Window:表示一个窗口,从下面Window的源码中可以看出它有且只有一个实现类PhoneWindow。 The only existing implementation of this abstract class is * android.policy.PhoneWindow, which you should instantiate when needing a * Window. WindowManager:它是系统提供我们操作Window的一个接口

MTK6580-Psensor hal层驱动分析 - 2016-07-22 18:07:25

一、HAL 层Sensor 流程         Hal 就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。 Sensor 打开设备时序图:  其中SensorDevice 属于 JNI 层,与 HAL 进行通信的接口 ; 在 JNI 层调用了 HAL 层的 open_sensors() 方法
ART世界探险(2) - 从java byte code说起 Dalvik时代,如果不做JIT的话,只需要了解java字节码和Dalivk的字节码就够了。但是,到了ART时代,我们可能还要至少学习两种新东西:一个是编译后端的IR中间代码。比如,我们假如使用LLVM做为编译后端的话,需要做从dex到LLVM IR的转换工作。这个IR可能还不只一层,比如分中层的MIR和底层的LIR。 最后,我们还得了解机器指令。仅就ARM来说,现在是64位时代了,我们需要了解的就是AArch64和AArch32两种状态下的A

使用AndFix实现Android热修复 - 2016-07-22 18:07:19

AndFix Github: https://github.com/alibaba/AndFix AndFix介绍 AndFix是一个Android App的在线热补丁框架。使用此框架,我们能够在不重复发版的情况下,在线修改App中的Bug。AndFix就是 “Android Hot-Fix”的缩写。  就目前来说,AndFix支持Android 2.3到6.0版本,并且支持arm 与 X86系统架构的设备。完美支持Dalvik与ART的Runtime。  AndFix 的补丁文件是以 .apatch 结

[置顶] VR学习 - 2016-07-22 18:07:15

VR学习 由于到去的公司从事VR这方面的开发,为了不打无准备之战,因此学习了一下Google的CardBoard VR实现。(仅仅是表皮,只是看Demo但是还是值得花点功夫看看) 效果图 这里的实现效果其实是,使用到了手机的传感器,陀螺仪(具体的往后面看) 学习Demo(再往深研究) 首先我们先看一下Demo中清单文件的权限和Activity的要求。 //请求网络权限 uses-permission android:name = "android.permission.INTERNET" / //手机NF

语音识别 - 2016-07-22 18:07:58

我使用 Speech Recognizer Intent 来获取 User Input ,然后再翻译成文本。但是我想让intent 连续的获取 user input 然后翻译成文本,来看用户是否说了某个词。现在的代码能实现,但是每次程序开始侦听输入时,电话就会发出短期的警笛声,准备输入。 有什么方法来删除已经播放过的又一次播放的声音? 代码: import java.util.ArrayList;import android.app.Activity;import android.content.Inte
前面已经分析了Android应用程序窗口View的的测量,布局过程,接下来分析View的draw过程. 在 frameworks/base/core/java/android/view/ ViewRootImpl.java中的 performTraversals()函数里调用 performLayout()函数进行布局之后,接着会调用 performDraw()函数进行绘制,现在就从这个函数开始分析 第一步: performDraw() 在 frameworks/base/core/java/androi