• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
随—风
博客园    首页    新随笔    联系   管理    订阅  订阅

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最初加入这里”

 

posted @ 2019-11-11 16:37  随—风  阅读(451)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3