提示控件之自定义Dialog
来源:互联网 发布:使命召唤 for mac 编辑:程序博客网 时间:2024/06/06 03:15
Dialog作为一个安卓应用中的常用提示控件,使用率非常之高,基本上每个APP中都有不同程度的运用。系统提供了一些便捷的使用方法,但原生的Dialog样式非常复古,使用时与本应用风格很难搭配,所以我们需要自定义一些常用的Dialog,封装常用的方法,以便需要时使用。
一、期望目标:
1.Dialog能自定义布局样式;
2.Dialog能自由设置弹出及隐藏效果;
3.Dialog能便捷控制其隐藏触发条件;
4.封装常用类型的Dialog。
二、大致思路:
1.自定义布局文件替换Dialog布局;
2.自定义Dialog属性样式;
3.监听实现点击外部点击、返回键点击等等;
4.封装常用对话框使用方法,如:普通对话框(提示信息)、加载对话框(无进度进度条或gif)、进度对话框(进度文本或带进度进度条)等。
三、具体实现
1.自定义CustomeDialog
package com.alone.custome.dialog;import android.app.Dialog;import android.content.Context;import android.content.DialogInterface;import android.os.Bundle;import android.view.KeyEvent;import android.view.View;/** * Created by Alone on 16/3/1. * 自定义dilog,避免了系统dialog样式简单不宜修改等问题 * 可自由设置样式,透明度,圆角等等 * * 示例样式查看style.xml * <!--自定义弹出框的样式 start--> * <!--自定义弹出框的样式 end--> */public class CustomeDialog extends Dialog { private CustomeDialogListener mListener; private int[] mViewIds; private boolean mBackKeyCancel = true;//返回键是否隐藏 public CustomeDialog(Context context, int layoutId, int styleId, int[] viewIds) { super(context, styleId); setContentView(layoutId); this.mViewIds = viewIds; } public CustomeDialog(Context context, View layout, int styleId, int[] viewIds) { super(context, styleId); setContentView(layout); this.mViewIds = viewIds; } /** * 自定义dialog构造器 * @param context 上下文 * @param layoutId 布局文件id * @param styleId 自定义样式id * @param viewIds 控件id数组 * @param listener 回调函数 */ public CustomeDialog(Context context, int layoutId, int styleId, int[] viewIds, CustomeDialogListener listener) { this(context, layoutId, styleId, viewIds); setListener(listener); } /** * 自定义dialog构造器 * @param context 上下文 * @param layout 布局文件view * @param styleId 自定义样式id * @param viewIds 控件id数组 * @param listener 回调函数 */ public CustomeDialog(Context context, View layout, int styleId, int[] viewIds, CustomeDialogListener listener) { this(context, layout, styleId, viewIds); setListener(listener); } /** * 在dialog的onCreate方法中绑定监听事件 * @param savedInstanceState */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); for (int viewId : mViewIds) { (findViewById(viewId)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mListener != null) { mListener.buttonOnClick(v); } } }); } this.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { //点击返回键 if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { if (!mBackKeyCancel) { return true; } } return false; } }); } public void setListener(CustomeDialogListener mListener) { this.mListener = mListener; } /** * 按返回键是否取消 */ public void setBackKeyCancel(boolean mBackKeyMark) { this.mBackKeyCancel = mBackKeyMark; } public interface CustomeDialogListener { void buttonOnClick(View view); }}
CustomeDialog继承Dialog,则拥有Dialog所有的特性;构造器中传入layoutId便于使用自定义布局文件,styleId为自定义样式属性,获取viewIds遍历添加onClick事件;Dialog的setOnKeyListener可以给对话框设置按钮监听,返回值false有效,true拦截,此处监听返回键即可,同时对外公布一个公共方法setBackKeyCancel()用以设置mBackKeyCancel属性值。
2.自定义属性样式
此处我们自定义弹出框的样式,此处定义两种:普通提示框(弹出时背景变暗),加载对话框(弹出时背景不变暗)。在res/values/styles.xml中节点下加上:
<!--自定义弹出框的样式 start--><style name="customeDialog" parent="android:style/Theme.Dialog"> <item name="android:windowBackground">@android:color/transparent</item><!-- 窗口透明,如此颜色决定为布局文件颜色 --> <item name="android:windowFrame">@null</item><!--Dialog的windowFrame框为无 --> <item name="android:windowNoTitle">true</item><!-- 无标题 --> <item name="android:windowIsFloating">true</item><!-- 是否漂现在activity上 --> <item name="android:windowIsTranslucent">true</item><!-- 是否半透明 --> <!--<item name="android:windowContentOverlay">@null</item>--> <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item> <item name="android:backgroundDimEnabled">true</item><!--背景变暗--> <item name="android:backgroundDimAmount">0.6</item><!--模糊程度--></style><style name="loadingDialog" parent="android:style/Theme.Dialog"> <item name="android:windowBackground">@android:color/transparent</item><!-- 窗口透明,如此颜色决定为布局文件颜色 --> <item name="android:windowFrame">@null</item><!--Dialog的windowFrame框为无 --> <item name="android:windowNoTitle">true</item><!-- 无标题 --> <item name="android:windowIsFloating">true</item><!-- 是否漂现在activity上 --> <item name="android:windowIsTranslucent">true</item><!-- 是否半透明 --> <!--<item name="android:windowContentOverlay">@null</item>--> <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item> <item name="android:backgroundDimEnabled">true</item><!--背景变暗--> <item name="android:backgroundDimAmount">0.0</item><!--模糊程度--></style><!--自定义弹出框的样式 end-->
3.自定义布局文件
自定义三种常用Dialog布局文件:提示对话框、进度对话框、正在加载对话框。
提示对话框layout_dialog_tips.xml如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/normal_dialog_style"> <TextView android:id="@+id/id_text_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="20dp" android:gravity="center" android:text="提示" android:textColor="@color/black" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/id_text_details" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/id_text_title" android:layout_marginBottom="25dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="15dp" android:gravity="center" android:textColor="@color/black" android:textSize="14sp" /> <ImageView android:layout_width="match_parent" android:layout_height="1px" android:layout_above="@+id/id_btn_confirm" android:src="@color/gray" /> <Button android:id="@+id/id_btn_confirm" android:layout_width="match_parent" android:layout_height="50dp" android:layout_below="@+id/id_text_details" android:background="@drawable/selector_btn_dialog_state" android:text="取 消" android:textColor="@drawable/selector_text_dialog_state" /></RelativeLayout>
为了美化界面,layout_dialog_tips.xml中使用了自定义的圆角背景normal_dialog_style.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/white" /> <corners android:radius="5dp"/></shape>
为了美化按钮点击效果,layout_dialog_tips.xml中使用了自定义的背景selector_btn_dialog_state.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape android:shape="rectangle"> <corners android:bottomLeftRadius="5dp" android:bottomRightRadius="5dp" /> <solid android:color="@color/pale" /> </shape> </item> <item android:state_pressed="false"> <shape android:shape="rectangle"> <corners android:radius="5dp" /> <solid android:color="@color/white" /> </shape> </item></selector>
按钮点击颜色变幻使用了自定义的切换效果selector_text_dialog_state.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:color="@color/red" /> <item android:state_pressed="false" android:color="@color/black" /></selector>
此处为了方便,我们用文本“**%”来表示进度,进度对话框与正在加载对话框共用一个布局文件layout_dialog_loading.xml代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:circleprogress="http://schemas.android.com/apk/res-auto" android:layout_width="125dp" android:layout_height="125dp" android:background="@drawable/loading_dialog_style" android:gravity="center_horizontal" android:orientation="vertical"> <com.alone.custome.progressBar.CircleProgress android:id="@+id/progress" android:layout_width="100dp" android:layout_height="100dp" circleprogress:color1="@android:color/holo_red_light" circleprogress:color2="@android:color/holo_green_light" circleprogress:color3="@android:color/holo_blue_light" /> <TextView android:id="@+id/id_text_details" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:text="正在加载..." android:textColor="@color/white" android:textSize="11sp" /></LinearLayout>
为了美化界面,layout_dialog_loading.xml中使用了自定义的圆角背景loading_dialog_style,代码如下:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/translucent" /> <corners android:radius="10dp"/></shape>
layout_dialog_loading.xml中使用了一个自定义进度条控件CircleProgress,来自于github,循环动画很棒,界面非常漂亮,地址:https://github.com/Fichardu/CircleProgress
颜色定义文件colors.xml:
<color name="transparent">#00000000</color><color name="translucent">#70000000</color><color name="white">#FFFFFF</color><color name="black">#000000</color><color name="red">#FF0000</color><color name="green">#00FF00</color><color name="blue">#0000FF</color><color name="gray">#BEBEBE</color><color name="pale">#EBEBEB</color>
4.创建工具类封装常用Diaolg
package com.alone.custome.dialog;import android.app.Activity;import android.content.Context;import android.content.DialogInterface;import android.os.Handler;import android.util.Log;import android.view.View;import android.widget.TextView;import com.alone.R;import com.alone.application.MyApplication;import java.util.ArrayList;import java.util.List;/** * Created by Alone on 16/8/12. * Be used for */public class DialogUtils { private static final String TAG = DialogUtils.class.getSimpleName(); private static Handler mHandler = new Handler(MyApplication.getInstance().getMainLooper()); private static List<CustomeDialog> mList = new ArrayList<>(); public static void showTipsDialog(Context context, int resId) { showTipsDialog(context, context.getString(resId)); } /** * 普通提示对话框 * */ public static void showTipsDialog(final Context context, final String detailsMsg) { ((Activity)context).runOnUiThread(new Runnable() { @Override public void run() { final CustomeDialog dialog = new CustomeDialog( context, R.layout.layout_dialog_tips, R.style.customeDialog, new int[]{R.id.id_btn_confirm}); dialog.setListener(new CustomeDialog.CustomeDialogListener() { @Override public void buttonOnClick(View view) { switch (view.getId()) { case R.id.id_btn_confirm: if (dialog != null && dialog.isShowing()) { dialog.cancel(); } break; } } }); dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { mList.remove(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); } }); TextView detailsText = (TextView) dialog.findViewById(R.id.id_text_details); detailsText.setText(detailsMsg); dialog.setBackKeyCancel(true); dialog.show(); mList.add(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); } }); } public static void showLoadingDialog(Context context, CustomeDialog oldDialog, int resId, DialogCallBack callBack) { showLoadingDialog(context, oldDialog, context.getString(resId), callBack); } /** * 正在加载对话框 * @param oldDialog 此参数为了确保new出多个对话框无法关闭的情况 */ public static void showLoadingDialog(final Context context, final CustomeDialog oldDialog, final String detailsMsg, final DialogCallBack callBack) { ((Activity)context).runOnUiThread(new Runnable() { @Override public void run() { CustomeDialog dialog = null; if (oldDialog != null) { mList.remove(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); dialog = oldDialog; } else { dialog = new CustomeDialog( context, R.layout.layout_dialog_loading, R.style.loadingDialog, new int[]{}); } dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { mList.remove(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); } }); TextView detailsText = (TextView) dialog.findViewById(R.id.id_text_details); detailsText.setText(detailsMsg); dialog.setBackKeyCancel(false); dialog.setCanceledOnTouchOutside(false); dialog.show(); mList.add(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); callBack.getDialog(dialog); } }); } /** * 下载进度对话框 * @param oldDialog 此参数为了确保new出多个对话框无法关闭的情况 */ public static void showDownloadingDialog(final Context context, final CustomeDialog oldDialog, final int progress, final DialogCallBack callBack) { ((Activity)context).runOnUiThread(new Runnable() { @Override public void run() { CustomeDialog dialog = null; if (oldDialog != null) { mList.remove(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); dialog = oldDialog; } else { dialog = new CustomeDialog( context, R.layout.layout_dialog_loading, R.style.customeDialog, new int[]{}); } dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { mList.remove(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); } }); TextView detailsText = (TextView) dialog.findViewById(R.id.id_text_details); detailsText.setText("正在下载: " + progress + "%"); dialog.setBackKeyCancel(false); dialog.setCanceledOnTouchOutside(false); dialog.show(); mList.add(dialog); Log.d(TAG, "#对话框数量:" + mList.size()); callBack.getDialog(dialog); } }); } /** * 设置下载进度对话框的进度 */ public static void setDownloadingProgress(CustomeDialog dialog, int progress) { setDialogMsg(dialog, "正在下载: " + progress + "%"); } public static void setDialogMsg(CustomeDialog dialog, int resId) { setDialogMsg(dialog, MyApplication.getInstance().getString(resId)); } /** * 设置对话框文本 */ public static void setDialogMsg(final CustomeDialog dialog, final String detailsMsg) { mHandler.post(new Runnable() { @Override public void run() { if (dialog != null && dialog.isShowing()) { TextView textView = (TextView) dialog.findViewById(R.id.id_text_details); if (textView != null) { textView.setText(detailsMsg); } } } }); } /** * 关闭对话框 */ public static void closeDialog(final CustomeDialog dialog) { mHandler.post(new Runnable() { @Override public void run() { if (dialog != null && dialog.isShowing()) { dialog.cancel(); } } }); } public interface DialogCallBack { void getDialog(CustomeDialog dialog); }}
工具类中将所有的Dialog存储于一个List中进行管理,通过
setCanceledOnTouchOutside()设置外部触摸是否关闭;
setBackKeyCancel()设置点击返回键是否关闭;
setListener()设置点击监听;
通过Handler确保在主线程更新UI;
使用oldDialog考虑其复用情况;
5.测试
新建测试Activity,分别弹出普通提示对话框,正在加载对话框,进度对话框,核心代码:
private CustomeDialog mNormalDialog;private CustomeDialog mLoadingDialog;private CustomeDialog mDownloadDialog;
弹出加载对话框,3秒后关闭:
DialogUtils.showLoadingDialog(DialogActivity.this, mLoadingDialog, "正在加载...", new DialogUtils.DialogCallBack() { @Override public void getDialog(CustomeDialog dialog) { mLoadingDialog = dialog; }});new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } DialogUtils.closeDialog(mLoadingDialog); }}).start();
谈出提示对话框:
DialogUtils.showTipsDialog(DialogActivity.this, "网络错误");
弹出进度对话框,进度100则关闭:
DialogUtils.showDownloadingDialog(DialogActivity.this, mDownloadDialog, 50, new DialogUtils.DialogCallBack() { @Override public void getDialog(CustomeDialog dialog) { mDownloadDialog = dialog; }});new Thread(new Runnable() { @Override public void run() { try { for (int i = 50; i <= 100; i++) { DialogUtils.setDownloadingProgress(mDownloadDialog, i); Thread.sleep(100); if (i == 100) { DialogUtils.closeDialog(mDownloadDialog); } } } catch (InterruptedException e) { e.printStackTrace(); } }}).start();
如果遇到具体业务的对话框,手动创建一个正常对话框即可,示例:
private void showNormalDialog() { mNormalDialog = new CustomeDialog( this, R.layout.layout_dialog_normal, R.style.customeDialog, new int[] {R.id.id_btn_cancel, R.id.id_btn_confirm}); mNormalDialog.setListener(new CustomeDialog.CustomeDialogListener() { @Override public void buttonOnClick(View buttonView) { switch (buttonView.getId()) { case R.id.id_btn_cancel: colseNormalDialog(); break; case R.id.id_btn_confirm: MyToast.makeText(DialogActivity.this, "点击确认", Toast.LENGTH_SHORT).show(); break; } } }); mNormalDialog.setCanceledOnTouchOutside(false); mNormalDialog.setBackKeyCancel(true); mNormalDialog.show();}private void colseNormalDialog() { if (mNormalDialog != null && mNormalDialog.isShowing()) { mNormalDialog.dismiss(); }}
演示视频如下(视频压成gif,失真的有点厉害):
结束语
自定义Dialog布局文件后,Dialog的界面显示效果变为无限可能,你可以编写任意符合你需求的对话框样式。本期就到这里,之后可能会做一些提示控件SnackBar分享。
- 提示控件之自定义Dialog
- 自定义控件之Dialog
- 自定义Dialog之信息提示
- 自定义控件实践之Dialog
- 消息提示类控件之Dialog(对话框)
- android自定义控件之Dialog详解
- 提示控件之自定义Toast
- 自定义Dialog, Toast提示
- Android-自定义View之重写控件(自定义Dialog)
- Android-自定义View之重写控件(自定义Dialog)
- 自定义dialog控件
- Android自定义控件-dialog
- Android 自定义View之消息提示控件
- 7.自定义Dialog提示框
- android 自定义提示框Dialog
- 自定义Dialog通用提示框
- app中自定义提示dialog
- Android控件之Dialog(two)列表与自定义弹窗
- 跟小博老师一起学习数据库 ——外连接
- BZOJ 2186: [Sdoi2008]沙拉公主的困惑
- jQuery Mobile页面
- linux基础学习09
- 解决远程连接mysql错误1130
- 提示控件之自定义Dialog
- 笔记
- Hibernate 基本创建
- PHP 验证码
- SQL语句--delete
- window bat脚本 变量操作
- spring boot整合shiro引用配置文件配置是出现的问题
- Linux学习笔记11
- 社会浮躁,太多女孩一副娼妓面孔