• Android 官方推荐: DialogFragment 创建对话框


    源代码参考:360云盘中---自己的学习资料---Android总结过的项目---DialogFragmentDemo.rar
    一、概述
    
    DialogFragment 在 android 3.0 时被引入。是一种特殊的 Fragment,用于在 Activity 的内容之上展示一个模态的对话框。典型的用于:展示警告框,输入框,确认框等等。
    在 DialogFragment 产生之前,我们创建对话框:一般采用 AlertDialog 和 Dialog。注:官方不推荐直接使用 Dialog 创建对话框。
    
    --------------------------------------------------------------------------------------------
    二、好处与用法
    
    使用 DialogFragment 来管理对话框,当旋转屏幕和按下后退键时可以更好的管理其声明周期,他和 Fragment 有着基本一致的声明周期。且 DialogFragment 也允许开发者把Dialog 作为内嵌的组件进行重用,类似 Fragment(可以在大屏幕和小屏幕显示出不同的效果)。上面会通过例子展示这些好处。
    
    使用 DialogFragment 至少需要实现 onCreateView 或者 onCreateDialog 方法。onCreateView 即使用定义的 xml 布局文件展示 Dialog。onCreateDialog 即利用AlertDialog 或者 Dialog 创建出 Dialog。
    
    --------------------------------------------------------------------------------------------
    三、重写 onCreateView 创建 Dialog
    
    1)布局文件,我们创建一个设置名称的布局文件:
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content" >  
      
        <TextView  
            android:id="@+id/tvLabelYourName"  
            android:layout_width="wrap_content"  
            android:layout_height="32dp"  
            android:gravity="center_vertical"  
            android:text="Your name:" />  
      
        <EditText  
            android:id="@+id/etYourName"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:layout_toRightOf="@id/tvLabelYourName"  
            android:imeOptions="actionDone"  
            android:inputType="text" />  
      
        <Button  
            android:id="@+id/btnEditName"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:layout_alignParentRight="true"  
            android:layout_below="@id/etYourName"  
            android:text="ok" />  
      
    </RelativeLayout> 
    
    2)继承 DialogFragment,重写 onCreateView 方法
    /**
     * 编辑名字 DialogFragment
     */
    public class EditNameDialogFragment extends DialogFragment {
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            
            View tView=inflater.inflate(R.layout.dialog_fragment_edit_name, null);
            
            return tView;
        }
    }
    
    3)测试运行:
    
    Main方法中调用:
    
    /**
     * 编辑名字主界面
     */
    public class EditNameActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            
            EditNameDialogFragment tEdFragment=new EditNameDialogFragment();
            tEdFragment.show(getFragmentManager(), "EditNameDialog");
        }
    }
    
    效果图:
    
    
    可以看到,对话框成功创建并显示出来,不过默认对话框有个讨厌的标题,我们怎么去掉呢:可以在 onCreateView 中调用 getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);即可去掉。即:
    
    /**
     * 编辑名字 DialogFragment
     */
    public class EditNameDialogFragment extends DialogFragment {
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
    
            getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); //去标题
            View tView = inflater.inflate(R.layout.dialog_fragment_edit_name, null);
    
            return tView;
        }
    }
    
    效果图:
    
    
    很完美的去掉了讨厌的标题。
    
    --------------------------------------------------------------------------------------------
    四、重写 onCreateDialog 创建 Dialog
    
    在 onCreateDialog 中一般可以使用 AlertDialog 或者 Dialog 创建对话框,不过既然 Google 不推荐直接使用 Dialog,我们就使用 AlertDialog 来创建一个登录的对话框。
    
    1)布局文件
    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:orientation="vertical" >  
      
        <ImageView  
            android:layout_width="match_parent"  
            android:layout_height="64dp"  
            android:background="#FFFFBB33"  
            android:contentDescription="@string/app_name"  
            android:scaleType="center"  
            android:src="@drawable/title" />  
      
        <EditText  
            android:id="@+id/etUserName"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:layout_marginBottom="4dp"  
            android:layout_marginLeft="4dp"  
            android:layout_marginRight="4dp"  
            android:layout_marginTop="16dp"  
            android:hint="input username"  
            android:inputType="textEmailAddress" />  
      
        <EditText  
            android:id="@+id/etPassWord"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:layout_marginBottom="16dp"  
            android:layout_marginLeft="4dp"  
            android:layout_marginRight="4dp"  
            android:layout_marginTop="4dp"  
            android:fontFamily="sans-serif"  
            android:hint="input password"  
            android:inputType="textPassword" />  
      
    </LinearLayout>
    
    2)继承 DialogFragment 重写 onCreateDialog 方法
    
    /**
     * 登录 DialogFragment
     */
    public class LoginDialogFragment extends DialogFragment {
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
    
            AlertDialog.Builder tBuilder = new AlertDialog.Builder(getActivity());
    
            // Get the layout inflater
            LayoutInflater tLInflater = getActivity().getLayoutInflater();
    
            View tView = tLInflater.inflate(R.layout.dialog_fragment_login, null);
    
            // Inflate and set the layout for the dialog
            // Pass null as the parent view because its going in the dialog layout
            tBuilder.setView(tView)
                    .setPositiveButton("Sign in",
                            new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog,
                                        int which) {
    
                                }
                            }).setNegativeButton("Cancel", null);
    
            return tBuilder.create();
        }
    }
    
    3)调用:
    
        private void showLoginDialog() {
    
            LoginDialogFragment tLDFragment = new LoginDialogFragment();
            tLDFragment.show(getFragmentManager(), "loginDialog");
        }
    
    4)效果图:
    
    
    可以看到通过重写 onCreateDialog 同样可以实现创建对话框,效果还是很 nice 的。
    
    --------------------------------------------------------------------------------------------
    五、传递数据给 Activity
    
    从 Dialog 传递数据给 Activity,可以使用“fragment interface pattern”的方式,下面通过一个改造上面的登录框来展示这种模式。(这里我自己说明一下,其实就是监听模式,只不过在 Fragment 中可以直接使用 getActivity() 方法调用接口中的方法,不过还不如果直接使用原监听模式,要进行判断的。)
    
                                    if (getActivity() instanceof LoginInputListener) {
    
                                        ((LoginInputListener) getActivity())
                                                .onLoginInputComplete(mUserName
                                                        .getText().toString(),
                                                        mPassword.getText()
                                                                .toString());
                                    }
    
    效果是一样的,不过哪个好,自己考虑吧。(好吧这和下边代码一样呢,别乱想了,原监听模式在上边会实例接口的。)
    
    改动比较小,直接贴代码了:
    
    /**
     * 登录 DialogFragment
     */
    public class LoginDialogFragment extends DialogFragment {
    
        private EditText mUserName;
        private EditText mPassword;
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
    
            AlertDialog.Builder tBuilder = new AlertDialog.Builder(getActivity());
    
            // Get the layout inflater
            LayoutInflater tLInflater = getActivity().getLayoutInflater();
    
            View tView = tLInflater.inflate(R.layout.dialog_fragment_login, null);
    
            mUserName = (EditText) tView.findViewById(R.id.etUserName);
            mPassword = (EditText) tView.findViewById(R.id.etPassWord);
    
            // Inflate and set the layout for the dialog
            // Pass null as the parent view because its going in the dialog layout
            tBuilder.setView(tView)
                    .setPositiveButton("Sign in",
                            new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog,
                                        int which) {
    
                                    LoginInputListener tLIListener = (LoginInputListener) getActivity();
                                    tLIListener.onLoginInputComplete(mUserName
                                            .getText().toString(), mPassword
                                            .getText().toString());
                                }
                            }).setNegativeButton("Cancel", null);
    
            return tBuilder.create();
        }
    
        /**
         * 登录监听,用来与 Activity 传递数据
         */
        public interface LoginInputListener {
    
            void onLoginInputComplete(String username, String password);
        }
    }
    
    拿到 username 和 password 的引用,在点击登录的时候,把 Activity 强转为我们自定义的接口:LoginInputListener,然后将用户输入的数据返回。
    MainActivity 中需要实现我们的接口 LoginInputListener,实现我们的方法,就可以实现当用户点击登陆时,获得我们的帐号密码了:
    
    /**
     * 登录 Dialog 主页面
     */
    public class LoginDialogActivity extends Activity implements LoginInputListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            showLoginDialog();
        }
    
        private void showLoginDialog() {
    
            LoginDialogFragment tLDFragment = new LoginDialogFragment();
            tLDFragment.show(getFragmentManager(), "loginDialog");
        }
    
        @Override
        public void onLoginInputComplete(String username, String password) {
    
            StringBuffer tSBuffer = new StringBuffer();
            tSBuffer.append("账号:");
            tSBuffer.append(username);
            tSBuffer.append(",密码:");
            tSBuffer.append(password);
    
            Toast.makeText(this, tSBuffer.toString(), Toast.LENGTH_SHORT).show();
        }
    }
    
    这种方式就可以满足屏幕旋转依然保存 Dialog 的状态,并且不会报错,但是我把 showLoginDialog() 写在了 onCreate() 方法中了,所以每次旋转都会创建一个新的 Dilog ,下面贴出正确代码。
    
    /**
     * 登录 Dialog 主页面
     */
    public class LoginDialogActivity extends Activity implements LoginInputListener, OnClickListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_login_dialog);
            
            Button tBtnLoginDialog=(Button) this.findViewById(R.id.btnLoginDialog);
            tBtnLoginDialog.setOnClickListener(this);
        }
    
        public void showLoginDialog() {
    
            LoginDialogFragment tLDFragment = new LoginDialogFragment();
            tLDFragment.show(getFragmentManager(), "loginDialog");
        }
    
        @Override
        public void onLoginInputComplete(String username, String password) {
    
            StringBuffer tSBuffer = new StringBuffer();
            tSBuffer.append("账号:");
            tSBuffer.append(username);
            tSBuffer.append(",密码:");
            tSBuffer.append(password);
    
            Toast.makeText(this, tSBuffer.toString(), Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void onClick(View v) {
    
            showLoginDialog();
        }
    }
    
    其实加一个 Button 按钮,做个中转就好了,因为不按下 Button 按钮,是不会创建 Dialog 的(‘...’)
    
    --------------------------------------------------------------------------------------------
    六、DialogFragment 做屏幕适配
    
    我们希望,一个对话框在大屏幕上以对话框的形式展示,而小屏幕上则直接嵌入当前的 Actvity 中。这种效果的对话框,只能通过重写 onCreateView 实现。下面我们利用上面的 EditNameDialogFragment 来显示。
    
    EditNameDialogFragment 我们已经编写好了,直接在 MainActivity 中写调用
        /**
         * 加载适配版 DialogFragment
         */
        private void initEditNameAdapterDialog() {
    
            FragmentManager tFManager = getFragmentManager();
            EditNameDialogFragment tEDFragment = new EditNameDialogFragment();
    
            boolean tBolLargeLayout = getResources()
                    .getBoolean(R.bool.large_layout);
    
            Log.e("TAG", tBolLargeLayout + "");
    
            if (tBolLargeLayout) {
    
                // The device is using a large layout, so show the fragment as a
                // dialog
                tEDFragment.show(tFManager, "dialog");
            } else {
    
                // The device is smaller, so show the fragment fullscreen
                FragmentTransaction tFTransaction = tFManager.beginTransaction();
    
                // For a little polish, specify a transition animation
                tFTransaction
                        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                // To make it fullscreen, use the 'content' root view as the
                // container
                // for the fragment, which is always the root view for the activity
                tFTransaction.replace(R.id.rlEditName, tEDFragment).commit();
            }
        }
    
    可以看到,我们通过读取 R.bool.large_layout,然后根据得到的布尔值,如果是大屏幕则直接以对话框显示,如果是小屏幕则嵌入我们的 Activity 布局中
    这个 R.bool.large_layout 是我们定义的资源文件:
    
    1.在默认的 values 下新建一个 bools.xml
    
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <bool name="large_layout">false</bool>
    
    </resources>
    
    2.然后在 res 下新建一个 values-large,在 values-large 下再新建一个 bools.xml
    
    <resources>
    
        <bool name="large_layout">true</bool>
    
    </resources>
    
    注意在,EditNameDialogFragment 中必须加一个判断,否则 getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); 报空异常,好像是小屏幕时嵌入到屏幕里了,没有标题的概念,所以为空。
    
    /**
     * 编辑名字 DialogFragment
     */
    public class EditNameDialogFragment extends DialogFragment {
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
    
            if (getResources().getBoolean(R.bool.large_layout)) { // 判断是否为大屏幕
    
                getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); // 去标题
            }
    
            View tView = inflater.inflate(R.layout.dialog_fragment_edit_name, null);
    
            return tView;
        }
    }
    
    最后测试:
    
    
    左边为模拟器,右边为我的手机。
    
    --------------------------------------------------------------------------------------------
    七、屏幕旋转
    
    当用户输入帐号密码时,忽然旋转了一下屏幕,帐号密码不见了……是不是会抓狂
    
    传统的 new AlertDialog 在屏幕旋转时,第一不会保存用户输入的值,第二还会报异常,因为 Activity 销毁前不允许对话框未关闭。而通过 DialogFragment 实现的对话框则可以完全不必考虑旋转的问题。
    
    我们直接把上面登录使用 AlertDialog 创建的登录框,拷贝到 MainActivity 中直接调用:
    
    正确的屏幕旋转代码在上面已经有了,现在贴正常的 AlertDialog 并附上错误信息。
    /**
     * 传统 AlertDialog 实验,屏幕旋转
     */
    public class TraditionDialogActivity extends Activity implements
            OnClickListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_login_dialog);
    
            Button tBtnLoginDialog = (Button) this
                    .findViewById(R.id.btnLoginDialog);
            tBtnLoginDialog.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            
            showLoginDialogWithoutFragment();
        }
    
        public void showLoginDialogWithoutFragment() {
    
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            // Get the layout inflater
            LayoutInflater inflater = this.getLayoutInflater();
    
            // Inflate and set the layout for the dialog
            // Pass null as the parent view because its going in the dialog layout
            builder.setView(inflater.inflate(R.layout.dialog_fragment_login, null))
                    // Add action buttons
                    .setPositiveButton("Sign in",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int id) {
                                    // sign in the user ...
                                }
                            }).setNegativeButton("Cancel", null).show();
        }
    }
    
    Activity com.xjl.dialogfragmentdemo.tradition.TraditionDialogActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@40f7bbf8 that was originally added here
    
    活动窗口COM com.xjl.dialogfragmentdemo.tradition.traditiondialogactivity泄漏。Android内部政策实施。。。。phonewindow decorview美元40f7bbf8最初加入这里”
  • 相关阅读:
    CSRF 攻击原理及防护
    XSS 攻击原理及防护
    git 基础命令大全
    vue3.0 VS vue2.0
    Android eMMC Booting[wiki百科]
    Android Fastboot[wiki百科]
    Android View.onMeasure方法的理解[转]
    某android游戏逆向小记
    Android网络编程—同时上传参数和文件到服务器 [转载]
    apk自我保护的一种实现方式——运行时自篡改dalvik指令【转载】
  • 原文地址:https://www.cnblogs.com/zx-blog/p/11836382.html
Copyright © 2020-2023  润新知