zoukankan      html  css  js  c++  java
  • 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上也会报如下错误:

    2019-09-25 14:58:29.996 24394-24394/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:622)
            at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:391)
            at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:129)
            at android.app.Dialog.show(Dialog.java:471)
            at com.yongdaimi.android.androidapitest.DialogFragmentApiUseDemoActivity.showNormalDialog(DialogFragmentApiUseDemoActivity.java:129)
            at com.yongdaimi.android.androidapitest.DialogFragmentApiUseDemoActivity.onClick(DialogFragmentApiUseDemoActivity.java:141)
            at android.view.View.performClick(View.java:6648)
            at android.view.View.performClickInternal(View.java:6620)
            at android.view.View.access$3100(View.java:787)
            at android.view.View$PerformClick.run(View.java:26167)
            at android.os.Handler.handleCallback(Handler.java:891)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:207)
            at android.app.ActivityThread.main(ActivityThread.java:7539)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)

    虽说并不影响使用,程序也不会崩溃,但至少说明这里是有问题的,解决这个问题的办法也很简单,在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都能保持原样。

  • 相关阅读:
    ssm接收界面提交的参数为空
    Servlet[jsp]的Servlet.service()引发了具有根本原因的异常无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]
    Invalid bound statement (not found)
    Data truncation: Incorrect datetime value: '' for column 'create_time' at row 1 问题
    4月29日:毕业设计计划
    4月25日:毕业设计计划
    4月24日:毕业设计计划
    4月23日:毕业设计计划
    2020年寒假学习进度(二)
    2020年寒假学习进度(一)
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/11589588.html
Copyright © 2011-2022 走看看