1. 程式人生 > >DialogFragment自定義佈局和大小踩坑記

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工具測一下就測出來了。