DialogFragment: DialogFragment的一些理解
Android 自3.0版本引入了DialogFragment这个类,并推荐开发者使用这个类替代之前经常使用的Dialog类,那么DialogFragment相对于之前的Dialog究竟有什么优势呢?这个DialogFragment又该如何使用呢?今天总结一下:
一. 与传统的Dialog类的对比
1.更完善的生命周期管理
之前创建的Dialog的方式如下:
static class MyDialog extends Dialog { private String TAG = "xp.chen-Dialog"; public MyDialog(@NonNull Context context)
{
super(context);
setContentView(R.layout.dialog_normal);
} @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate: MyDialog->onCreate()");
} @Override
protected void onStart()
{
super.onStart();
Log.i(TAG, "onStart: MyDialog->onStart()");
} @Override
protected void onStop()
{
super.onStop();
Log.i(TAG, "onStop: MyDialog->onStop()");
} @Override
public void cancel() {
super.cancel();
Log.i(TAG, "cancel: MyDialog->cancel()");
} @Override
public void dismiss()
{
super.dismiss();
Log.i(TAG, "dismiss: MyDialog->dismiss()");
}
}
使用时:
/**
* Show a normal dialog use Dialog API.
*/
private void showNormalDialog() {
mNormalDialog = new MyDialog(DialogFragmentApiUseDemoActivity.this);
mNormalDialog.show();
}
这样创建本来没什么问题,但是如果这个时候屏幕方向发生变化,就会导致Activity重建,然后之前显示的对话框就消失了,Log上也会报如下错误:
-- ::29.996 -/com.yongdaimi.android.androidapitest E/WindowManager: android.view.WindowLeaked: Activity com.yongdaimi.android.androidapitest.DialogFragmentApiUseDemoActivity has leaked window DecorView@1fa30b4[DialogFragmentApiUseDemoActivity] that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:)
at android.app.Dialog.show(Dialog.java:)
at com.yongdaimi.android.androidapitest.DialogFragmentApiUseDemoActivity.showNormalDialog(DialogFragmentApiUseDemoActivity.java:)
at com.yongdaimi.android.androidapitest.DialogFragmentApiUseDemoActivity.onClick(DialogFragmentApiUseDemoActivity.java:)
at android.view.View.performClick(View.java:)
at android.view.View.performClickInternal(View.java:)
at android.view.View.access$(View.java:)
at android.view.View$PerformClick.run(View.java:)
at android.os.Handler.handleCallback(Handler.java:)
at android.os.Handler.dispatchMessage(Handler.java:)
at android.os.Looper.loop(Looper.java:)
at android.app.ActivityThread.main(ActivityThread.java:)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:)
虽说并不影响使用,程序也不会崩溃,但至少说明这里是有问题的,解决这个问题的办法也很简单,在Activity的onDestory方法中主动关闭:
@Override
protected void onDestroy() {
super.onDestroy();
if (mNormalDialog != null){
mNormalDialog.cancel();
}
Log.e(TAG,"onDestroy");
}
而且如果想在屏幕切换后仍然显示Dialog的话,可以采用如下方法:
在onSaveInstanceState()方法中进行状态的保存:
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
Log.i(TAG, "onSaveInstanceState: ");
if (mNormalDialog != null && mNormalDialog.isShowing()) {
outState.putBoolean("DIALOG_SHOWN", true);
}
}
在onCreate()方法对其进行恢复:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dialog_fragment_api_use_demo);
initView();
Log.i(TAG, "onCreate: "); if (savedInstanceState != null) {
boolean is_shown = savedInstanceState.getBoolean("DIALOG_SHOWN");
if (is_shown) {
showNormalDialog();
}
} else {
Toast.makeText(this, "savedInstanceState is NULL", Toast.LENGTH_SHORT).show();
}
}
这样就既解决了上面的异常也处理了屏幕方向切换后Dialog消失的问题。但是个人觉得这样很不合理,屏幕旋转是很正常的操作,旋转前旋转后保持一样的界面UI是很正常的事情,要是每次涉及到屏幕旋转都让我做一遍上面的操作,那真的让人抓狂。而且,如果在Activity的onDestory()方法里销毁了Dialog还好,万一忘记销毁了,Dialog里面又有一些复杂操作,还有可能造成内存泄露,所以没办法自动管理Dialog的生命周期是传统Dialog的第一个缺陷。
2. 更合理的功能划分
如果是弹出一个简单的确认取消的对话框,可能直接就在Activity里使用以下方式:
new AlertDialog.Builder(GuideActivity.this).setTitle("用户申明")
.setMessage(getResources().getString(R.string.statement))
.setPositiveButton("我同意", new positiveListener())
.setNegativeButton("不同意", new negativeListener())
.setCancelable(false)
.show();
}private class positiveListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
prefs.setIsFirstTime(false);
}
} private class negativeListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
Util.virtualHome(GuideActivity.this);
}
}
这倒也没什么不对,对话框也能正常显示 ,可问题是“Activity知道太多了”,你点击对话框上的按钮,那是对话框本身的事情,对话框本身的事情对话框自己知道就好了,Activity没必要知道,上面的onClick()方法里的代码量还算少,多了的话,简直惨不忍睹。
二. DialogFragment的使用
使用上并没有什么特别值得注意的地方,大致和Fragment的使用差不多。以前是在onCreateView()方法里写Fragment的界面,现在在这个方法里写Dialog的界面。
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.dialog_fragment_use_demo, container, false);
return view;
}
另外它还新提供了一个onCreateDialog()的方法,我们可以直接在这个方法里创建传统的Dialog,然后直接返回,很方便。
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState)
{
return super.onCreateDialog(savedInstanceState);
}
显示的话,以前的Dialog是调用show()方法显示的,现在同样是调用show()方法显示,只不过参数有点不同:
/**
* Show a normal dialog use Dialog API.
*/
private void showNormalDialog() {
mNormalDialog = new MyDialog(DialogFragmentApiUseDemoActivity.this);
mNormalDialog.show();
} private void showDialogFragment() {
MyDialogFragment dialogFragment = new MyDialogFragment();
dialogFragment.show(getSupportFragmentManager(), "dialogFragment");
}
我这里分别在代码中使用Dialog和DialogFragment创建了两个对话框,然后在横竖屏切换的时候分别比较两个对话框的状态,并用DialogFragment实现了一个类似iOS上UIActionSheet的效果,代码和效果图如下:
主界面
package com.yongdaimi.android.androidapitest; import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast; import com.yongdaimi.android.androidapitest.view.MyDialogFragment; public class DialogFragmentApiUseDemoActivity extends AppCompatActivity implements View.OnClickListener
{ private static final String TAG = "xp.chen"; private Button btn_show_dialog_fragment;
private Button btn_show_normal_dialog; private MyDialog mNormalDialog; @Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dialog_fragment_api_use_demo);
initView();
Log.i(TAG, "onCreate: ");
/*if (savedInstanceState != null) {
boolean is_shown = savedInstanceState.getBoolean("DIALOG_SHOWN");
if (is_shown) {
showNormalDialog();
}
} else {
Toast.makeText(this, "savedInstanceState is NULL", Toast.LENGTH_SHORT).show();
}*/
} @Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
Log.i(TAG, "onSaveInstanceState: ");
/*if (mNormalDialog != null && mNormalDialog.isShowing()) {
outState.putBoolean("DIALOG_SHOWN", true);
}*/
} @Override
protected void onStart()
{
super.onStart();
Log.i(TAG, "onStart: ");
} @Override
protected void onResume()
{
super.onResume();
Log.i(TAG, "onResume: ");
} @Override
protected void onPause()
{
super.onPause();
Log.i(TAG, "onPause: ");
} @Override
protected void onStop()
{
super.onStop();
Log.i(TAG, "onStop: ");
} @Override
protected void onDestroy()
{
super.onDestroy();
Log.i(TAG, "onDestroy: ");
} private void initView()
{
btn_show_dialog_fragment = findViewById(R.id.btn_show_dialog_fragment);
btn_show_dialog_fragment.setOnClickListener(this); btn_show_normal_dialog = findViewById(R.id.btn_show_normal_dialog);
btn_show_normal_dialog.setOnClickListener(this);
} /**
* Show a normal dialog use Dialog API.
*/
private void showNormalDialog() {
mNormalDialog = new MyDialog(DialogFragmentApiUseDemoActivity.this);
mNormalDialog.show();
} private void showDialogFragment() {
MyDialogFragment dialogFragment = new MyDialogFragment();
dialogFragment.show(getSupportFragmentManager(), "dialogFragment");
} @Override
public void onClick(View v)
{
switch (v.getId()) {
case R.id.btn_show_dialog_fragment:
showDialogFragment();
break;
case R.id.btn_show_normal_dialog:
showNormalDialog();
break;
default:
break;
}
} static class MyDialog extends Dialog { private String TAG = "xp.chen-Dialog"; public MyDialog(@NonNull Context context)
{
super(context);
setContentView(R.layout.dialog_normal);
} @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate: MyDialog->onCreate()");
} @Override
protected void onStart()
{
super.onStart();
Log.i(TAG, "onStart: MyDialog->onStart()");
} @Override
protected void onStop()
{
super.onStop();
Log.i(TAG, "onStop: MyDialog->onStop()");
} @Override
public void cancel() {
super.cancel();
Log.i(TAG, "cancel: MyDialog->cancel()");
} @Override
public void dismiss()
{
super.dismiss();
Log.i(TAG, "dismiss: MyDialog->dismiss()");
}
} }
DialogFragmentApiUseDemoActivity.java
主界面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
> <Button
android:id="@+id/btn_show_normal_dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Normal Dialog"
android:textAllCaps="false"
/> <Button
android:id="@+id/btn_show_dialog_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Dialog Fragment"
android:textAllCaps="false"
/> </LinearLayout>
activity_dialog_fragment_api_use_demo.xml
DialogFragment的对话框:
package com.yongdaimi.android.androidapitest.view; import android.app.Dialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager; import com.yongdaimi.android.androidapitest.R; public class MyDialogFragment extends DialogFragment
{ private static final String TAG = "xp.chen-DialogFragment"; @Override
public void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate: ");
// setStyle(DialogFragment.STYLE_NO_TITLE, R.style.DialogFullScreen);
} @Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
Log.i(TAG, "onCreateView: ");
//去掉dialog的标题,需要在setContentView()之前
this.getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
View view = inflater.inflate(R.layout.dialog_fragment_use_demo, container, false);
return view;
} @NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState)
{
return super.onCreateDialog(savedInstanceState);
} @Override
public void onStart()
{
super.onStart();
Window dialogWindow = getDialog().getWindow();
if (dialogWindow != null) {
dialogWindow.getDecorView().setPadding(0, 0, 0, 0);
dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.BOTTOM;
lp.windowAnimations = android.R.style.Animation_InputMethod;
dialogWindow.setAttributes(lp);
}
} @Override
public void onDetach()
{
super.onDetach();
Log.i(TAG, "onDetach: ");
} @Override
public void onDestroy()
{
super.onDestroy();
Log.i(TAG, "onDestroy: ");
} }
MyDialogFragment.java
对话框布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F0F0F0"
android:orientation="vertical"> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:gravity="center"
android:minHeight="50dip"
android:text="请选择指定的类型"
android:textColor="#CCC" /> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dip"
android:background="@drawable/btn_action_sheet_item_selector"
android:gravity="center"
android:minHeight="44dip"
android:text="ActionSheet 1"
android:textAllCaps="false"
android:textColor="#333"
style="?android:attr/borderlessButtonStyle"
/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dip"
android:background="@drawable/btn_action_sheet_item_selector"
android:gravity="center"
android:minHeight="44dip"
android:text="ActionSheet 2"
android:textAllCaps="false"
android:textColor="#333"
style="?android:attr/borderlessButtonStyle"
/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dip"
android:background="@drawable/btn_action_sheet_item_selector"
android:gravity="center"
android:minHeight="44dip"
android:text="ActionSheet 3"
android:textAllCaps="false"
android:textColor="#333"
style="?android:attr/borderlessButtonStyle"
/> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dip"
android:background="@drawable/btn_action_sheet_item_selector"
android:gravity="center"
android:minHeight="44dip"
android:text="取消"
android:textAllCaps="false"
android:textColor="@android:color/holo_red_light"
style="?android:attr/borderlessButtonStyle"
/> </LinearLayout>
dialog_fragment_use_demo.xml
最终效果:
从图上也能很明显看出,不管屏幕如何切换,使用DialogFragment创建出来的Dialog都能保持原样。
DialogFragment: DialogFragment的一些理解的更多相关文章
- Square:从今天開始抛弃Fragment吧!
原文链接 : Advocating Against Android Fragments 原文作者 : Pierre-Yves Ricau 译文出自 : 开发技术前线 www.devtf.cn 译者 : ...
- Android面试二之Fragment
基本概念 Fragment,简称碎片,是Android 3.0(API 11)提出的,为了兼容低版本,support-v4库中也开发了一套Fragment API,最低兼容Android 1.6. F ...
- Fragment(一)--Fragment用法常见问题
fragment notes fragment相关内容包括 基本定义与使用 回退栈内部实现 fragment通信(与activity 与fragment) DialogFragment VP + Fr ...
- 详细解读DialogFragment
原博客地址:http://www.cnblogs.com/tianzhijiexian/p/4161811.html 相信看这篇文章的人都应该知道android中的Dialog了吧,如果对于Dialo ...
- Android 官方推荐 : DialogFragment 创建对话框
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37815413 1. 概述 DialogFragment在android 3.0时 ...
- DialogFragment is gone after returning back from another activity
基本情景如下: 在DialogFragment中单击一个按钮跳转到another Activity做一些逻辑处理,然后将返回的结果回显到该DialogFragment上. 处理逻辑是: 在Dialog ...
- Momo自定义DialogFragment
在Fragnment弹窗提示 XML <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&q ...
- [Android Pro] Android 官方推荐 : DialogFragment 创建对话框
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37815413 1. 概述 DialogFragment在android 3.0时 ...
- DialogFragment 自定义弹窗
layout文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:a ...
随机推荐
- 使用Cloudera Manager搭建zookeeper集群及HDFS HA实战篇
使用Cloudera Manager搭建zookeeper集群及HDFS HA实战篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.使用Cloudera Manager搭建zo ...
- Type mismatch: cannot convert from element type Object to String 解决办法
首先放上我的源码,看看你的代码是不是我这个类似的. @Test void predicateTest() throws Exception { List<String> languages ...
- node-mysql-promise 操作
使用node操作数据库做顺序操作很麻烦,为了保证执行顺序需要使用promise. 可以直接封装,也可以使用封装好的,比如node-mysql-promise 操作文档见https://www.npmj ...
- IDEA实用教程(六)—— 全局设置的两种方式
五. 全局设置的两种方式 在启动界面进入全局设置 在编码界面进入全局设置 本项目配置 上面的这种设置仅对本项目生效,不会对其他项目生效.请特别注意!!!
- python3 生成二维码并存入word文档
#二维码的制作与解析 import qrcode,zxing,os s='https:////www.baidu.com/' res=qrcode.make(data=s) res.show() re ...
- docker安装之mariadb
https://hub.docker.com/_/mariadb?tab=description Supported tags and respective Dockerfile links 10.4 ...
- [转]Serverless实践
转载的,原文: https://www.cnblogs.com/middleware/p/9470533.html ------------------------------------------ ...
- webpack loader和插件的编写原理
webpack自定义loader和插件的api网址:https://www.webpackjs.com/api/loaders/ 点击顶部API,看左侧api: 1. 如何编写一个loader 实现的 ...
- CF600E Lomsat gelral 和 CF741D Dokhtar-kosh paths
Lomsat gelral 一棵以\(1\)为根的树有\(n\)个结点,每个结点都有一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号(若有数量一样的,则求编号和). \(n \le 10^ ...
- nginx配置静态资源:配置绝对路径
nginx配置静态资源:配置绝对路径 项目都是html格式的文件,我的项目路径:E:\javaservice\nginx-1.15.7\html assets:静态资源 html:站点文件 uploa ...