DialogFragment自定义布局和大小踩坑记

前言

最近需要做一个弹出框,里面包含两个可以左右滑动的fragment的,采用dialogFragment来实现的,在使用dialogFragment的时候遇到了一些坑,所以抽时间好好梳理一些dialogFragment的使用和注意的地方。

效果图

先给出实现的效果图
在这里插入图片描述
从效果图中可以看到,弹出框中包含全部和收藏两个fragment支持滑动和点击实现fragment的切换。弹出框的大小是自定义的。

采坑

dialogFragment支持两种方式来显示布局:
1、重写onCreateDialog创建Dialog
通过扩展 DialogFragment 类并在 onCreateDialog() 回调方法中创建 AlertDialog

public class MyDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("title")
               .setMessage("message")
               .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       
                   }
               })
               .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       
                   }
               });
        return builder.create();
    }
    
}

第一次我使用的是AlertDialog来实现布局,因为布局种的两个fragment的滑动的点击要用到ViewPager+PagerSlidingTabStrip来实现,需要获取FragmentManager,当重写onCreateDialog来实现时,因为AlertDialog是系统的弹框,所以传入FragmentManagerAlertDialog是无效的,因此实现不了fragment的滑动和点击。
2、重写onCreateView实现自定义布局
onCreateView中自定义布局,这样布局就属于当前的dialogFragment,获取当前dialogFragment中的FragmentManager就可以对布局中的子fragment进行管理。如下重写onCreateView方法。

 @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.message_share_websit_dialog, container);     
        initView(view);
        return view;
    }

坑1自定义弹框的大小
在自定义布局中设置的大小是不起作用的,要设置自定义布局的大小只有在代码中动态设置,在onStart中重写布局大小,在onCreat或者onCreateView中无效

    /**
     * 修改布局的大小
     */
    @Override
    public void onStart() {
        super.onStart();
        XLLog.d(TAG, "onStart");
        resizeDialogFragment();

    }

    private void resizeDialogFragment() {
        Dialog dialog = getDialog();
        if (null != dialog) {
            Window window = dialog.getWindow();
            WindowManager.LayoutParams lp = getDialog().getWindow().getAttributes();
            lp.height = (25 * ScreenUtil.getScreenHeight(getContext()) / 32);//获取屏幕的宽度,定义自己的宽度
            lp.width = (8 * ScreenUtil.getScreenWidth(getContext()) / 9);
            if (window != null) {
                window.setLayout(lp.width, lp.height);
            }
        }
    }

ScreenUtil

public abstract class ScreenUtil {
    /**
     * 获取屏幕的高度(单位像素)
     */
    public static int getScreenHeight(Context context) {
        if (context != null) {
            DisplayMetrics dm = new DisplayMetrics();
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                wm.getDefaultDisplay().getRealMetrics(dm);
            } else {
                wm.getDefaultDisplay().getMetrics(dm);
            }
            return dm.heightPixels;
        }
        return 0;
    }

    /**
     * 获取状态栏高度(单位像素)
     *
     * @author zengnianan
     */
    public static int getStatusBarHeight(Context context) {
        int height = 0;
        try {
            Class c = Class.forName("com.android.internal.R$dimen");

            Object obj = c.newInstance();
            Field field = c.getField("status_bar_height");
            int resId = Integer.parseInt(field.get(obj).toString());
            height = context.getResources().getDimensionPixelSize(resId);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return height;
    }
    }


这里不得不提到的坑是,看起来已经动态设置了自己想要的布局大小。但实际运行出来的大小和定义的尺寸有偏差。上面代码中设置的宽度是屏幕的8/9,运行代码是得到的 lp.width=960,但我用Layout Inspector检测出来自定义的布局宽度仅仅是876,这中间差了84。所以肯定是系统在自定义的布局外面又包了一层其他的东西,导致设置出来的宽度和实际显示的不一样。
通过

        Dialog dialog = getDialog();
        if (null != dialog) {
            Window window = dialog.getWindow();
            Log.d(TAG, "padding.................." + window.getDecorView().getPaddingLeft() + "............................." + window.getDecorView().getPaddingTop());
//结果:padding..................42.............................42

由上面结果可知,在自定义布局外面还有一个padding的距离,这个padding 距离四周的距离都是42,876+42*2=960,正好和设置的宽度相同。
检测布局
用Android studio 中的Layout Inspector检测布局
在这里插入图片描述
由上图可以看到布局结构:
在这里插入图片描述
整个弹出框的根布局是DecorView,DecorView里面包含了一个FragmentLayout,FragmentLayout里面包含两个布局一个是content,就是我们自定义的布局,Action_mode_bar_stub这个是actionBar,我们这里的actionBar布局为null什么都没有。
其中DecorView的定义可以看一段英文:

The DecorView is the view that actually holds the window’s background drawable. Calling getWindow().setBackgroundDrawable() from your Activity changes the background of the window by changing the DecorView‘s background drawable. As mentioned before, this setup is very specific to the current implementation of Android and can change in a future version or even on another device.

其中的padding=42就是DecorView与其他布局的间距,所以获取到DecorView再设置它的padding就好了。
解决方案
方案1:设置透明背景
要在onCreateView中设置背景为透明,原来dialogFragment在自定义的布局外加了一个背景,设置为透明背景后,尺寸就和设置的尺寸一样了。加上

getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
 @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        View view = inflater.inflate(R.layout.message_share_websit_dialog, container);
        return view;

    }

搞定
总结
通过这次积累了一些经验,在项目中用善于利用工具,之前设计给出960的宽度,我代码里面debug出来的lp.width=960,但是设计一直说我的布局窄了,我很纳闷,明明设置的是960怎么会窄了。后来利用Android 自带的Layout Inspector工具测一下就测出来了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章