今天在做检查更新功能的时候遇到一个问题,是关于DialogFragment实现自定义对话框样式(主要是宽度)的,这里记录一下。
需要实现的效果
设计图如下
在检查到有新版本之后需要弹出一个对话框,内容自定义,这里用AlertDialog比较困难了,因此我用的是DialogFragment实现。
最初的实现
首先定义一个XML布局文件,如下所示
<?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:gravity="center_horizontal" android:orientation="vertical" android:paddingLeft="16dp" android:paddingRight="16dp">
<TextView android:id="@+id/version_tip_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:layout_marginTop="16dp" android:gravity="center" android:text="1.2版本发布" android:textColor="@color/primary_dark" android:textSize="20sp"/>
<TextView android:id="@+id/version_description_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="首页百变" android:textColor="@color/primary"/>
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="32dp" android:src="@mipmap/pic_eab"/>
<Button android:id="@+id/submit_button" style="@style/Button" android:text="立即更新"/>
</LinearLayout>
以上代码只是列出基本结构,样式定义什么的没有贴出。
有了自定义布局文件,然后自定义 DialogFragment 类代码如下
package com.witmoon.eab.ui.fragment.dialog;
import android.app.Dialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import com.witmoon.eab.R;
/** * 检查更新时有新版本发布弹出对话框 * Created by zhyh on 2015/11/16. */
public class NewVersionDialogFragment extends DialogFragment {
private String mVersionCode;
private String mDescription;
private OnUpdateListener mOnUpdateListener;
public void setOnUpdateListener(OnUpdateListener onUpdateListener) {
mOnUpdateListener = onUpdateListener;
}
public interface OnUpdateListener {
void onUpdate(Dialog dialog);
}
public static NewVersionDialogFragment newInstance(String versionCode, String description) {
NewVersionDialogFragment fragment = new NewVersionDialogFragment();
Bundle bundle = new Bundle();
bundle.putString("version", versionCode);
bundle.putString("description", description);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
mVersionCode = bundle.getString("version");
mDescription = bundle.getString("description");
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return super.onCreateDialog(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
View view = inflater.inflate(R.layout.dialog_new_version, null);
TextView versionTipText = (TextView) view.findViewById(R.id.version_tip_text);
versionTipText.setText(String.format("%s版本发布", mVersionCode));
TextView versionDescriptionText = (TextView) view.findViewById(R.id.version_description_text);
versionDescriptionText.setText(mDescription);
Button button = (Button) view.findViewById(R.id.submit_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnUpdateListener != null) {
mOnUpdateListener.onUpdate(getDialog());
}
}
});
return view;
}
}
根据设计图,Dialog不能有标题,因此在代码中用getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
去掉了对话框标题栏,这时如果运行程序,显示效果如下所示
很明显能看出对话框布局是WRAP_CONTENT,这样显得很拥护,如果版本描述内容更简短的话,这个对话框还会更窄。这样显示效果很难看,因此,我想要是能让对话框占用固定比例的屏幕宽度会显得更美观一些。
下面记录一下在网络上搜到的各种解决方式,代码都是基于上面完整的自定义 DialogFragment 并略有改动。
使对话框充满屏幕宽度
这种方式是使对话框充满屏幕的宽度,通过设置 Diaglog 样式实现;在onCreate方法中修改如下:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
mVersionCode = bundle.getString("version");
mDescription = bundle.getString("description");
setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog_MinWidth);
}
注意最后一行代码,这里设置了无标题样式(此时onCreateView的getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
就没有必要了,可以删掉了),并保证对话框的最小宽度,效果如下
可以看到,这种对话框其实就是默认的AlertDialog样式,宽度充满屏幕,左右保留一些外边距。
但是对照着设计图看还不太满足要求,这种效果显得太宽了,也不美观,最好是能让对话框占用屏幕宽度的一定比例,比如70%的屏幕宽度。
占用屏幕宽度一定比例
后来又找到另外一种方式,这种方式是自己计算屏幕宽度并设置到对话框,重写onStart方法,实现如下
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
DisplayMetrics dm = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
dialog.getWindow().setLayout((int) (dm.widthPixels * 0.75), ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
从代码可以看出,首先计算屏幕的宽度,然后将宽度的75%设置到对话框的Window上,高度为WRAP_CONTENT。
记得要在onCreateView方法中加上
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
不然标题栏又要出现了。
这时实现的效果如下图所示:
这样就显得很接近设计图的样式了。
对话框圆角
如果要实现自定义对话框圆角,可以加入下面的代码使对话框背景透明
getDialog().getWindow().setBackgroundDrawableResource(android.R.color.transparent);
然后将自定义布局设置为圆角就可以了,在drawable目录中定义XML如下
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="8dp"/>
<stroke android:color="#FDFFFFFF"/>
<solid android:color="#FDFFFFFF"/>
</shape>
然后将自定义布局的根布局background属性设置为该xml就可以了。