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

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