当前位置:网站首页>Some experience of using dialogfragment and anti stepping pit experience (getactivity and getdialog are empty, cancelable is invalid, etc.)

Some experience of using dialogfragment and anti stepping pit experience (getactivity and getdialog are empty, cancelable is invalid, etc.)

2022-04-23 14:10:00 Senzhiqianshou

Preface

There are many ways to create dialog boxes in Android .Dialog、AlertDialog、DialogFragment、PopupWindow even to the extent that Activity Set the theme to Dialog You can also create dialog boxes . among ,AlertDialog and DialogFragment It's all based on Dialog Created .AlertDialog It can be regarded as an official Dialog Wrapper class .DialogFragment Is in Fragment There's an embedded Dialog object , Its biggest advantage is that it can ensure that the previous state remains unchanged when the screen direction is switched , And the official also recommends DialogFragment, So it is gradually used in the project . however , There are also many pits , This article is specially written here to talk about , Also as future notes .

One 、 How to style a dialog box

because dialogFragment There's a built-in dialog Entity , Through this dialog Entity , We can use dialog All the ways to configure this form :

	Window window=getDialog().getWindow();
	window.setWindowAnimations(R.style.BottomWindowAnim);// Set form animation style 
	WindowManager.LayoutParams lp = window.getAttributes();
	lp.gravity = Gravity.BOTTOM;// Set window position 
	lp.dimAmount = 0.7f;// Set the transparency of the mask  0~1
	lp.width = (int) (getResources().getDisplayMetrics().widthPixels*0.8);// Set the width of the form ( It can be a specific pixel value , It can also be WRAP_CONTENT And so on. )
	lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;// Set the height of the form 
	getDialog().onWindowAttributesChanged(lp);// Manual trigger , Application configuration 

Two 、 How to set click the mask or return key to prevent the form from disappearing

Use as before dialog Experience in , We should know ,dialog You can set it like this :

	dialog.setCancelable(false); // Set and click the return key to not disappear 
	dialog.setCanceledOnTouchOutside(false);// Set the click mask not to disappear 

However , When you set it like this , You'll find that , Only by clicking on the mask will it take effect . Clicking the back button can still disappear .

For the processing of the return key , We should call dialogFragment Inside setCancelable To achieve :

	dialogFragment.setCancelable(cancelable);

In fact, the reason for this design is also well understood .dialog Inside cancelable It can only control itself , And its host fragment It's out of control . So this method should be written in fragment Inside .

3、 ... and 、 call getDialog() return null

For example, in the above question , Set up canceledOnTouchOutside The time has come getDialog() Null pointer exception . To understand the problem , Let's take a brief look at dialogFragment What key stages will you go through when creating .

	@Override
    public void onAttach(@NonNull Context context) {
    
        super.onAttach(context);
        Log.d(TAG, "onAttach.getDialog->" + getDialog());
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate.getDialog->" + getDialog());
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
    
        Log.d(TAG, "onCreateDialog.getDialog->" + getDialog());
        return super.onCreateDialog(savedInstanceState);
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    
        Log.d(TAG, "onCreateView.getDialog->" + getDialog());
        rootView = inflater.inflate(getLayoutId(), null);
        return rootView;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG, "onViewCreated.getDialog->" + getDialog());
    }

I put the log tag in the above code , Let's take a look at :

2021-09-18 09:51:56.779 15075-15075/com.cjs.kotlinapp D/BDF: onAttach.getDialog->null
2021-09-18 09:51:56.780 15075-15075/com.cjs.kotlinapp D/BDF: onCreate.getDialog->null
2021-09-18 09:51:56.780 15075-15075/com.cjs.kotlinapp D/BDF: onCreateDialog.getDialog->null
2021-09-18 09:51:56.786 15075-15075/com.cjs.kotlinapp D/BDF: onCreateView.getDialog->android.app.Dialog@8983aa6
2021-09-18 09:51:56.836 15075-15075/com.cjs.kotlinapp D/BDF: onViewCreated.getDialog->android.app.Dialog@8983aa6

It's not hard to see. ,getDialog() from onCreateView Start , It's worth it .
actually ,getDialog The return is onCreateDialog In return dialog.
 The truth
 Insert picture description here

in other words , The earliest we can get dialog Value stage , Namely onCreateDialog in , But write it in this form :

	@NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
    
        Dialog dialog = super.onCreateDialog(savedInstanceState);
  		// Here you can write some dialog Configuration operation of , for example 
  		dialog.setCanceledOnTouchOutside(false);
        return dialog;
    }

therefore , The earliest we can set canceledOnTouchOutside The stage of , This is it . thus , There will be no null pointer exception .

Four 、 Abnormal display of form size

1、 Problem recurrence

for instance , When customizing the dialog box , The form you see after configuration is like this :
 The form displays abnormally
Window layout :

<?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:orientation="vertical" android:padding="10dp">

    <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical" android:drawableLeft="@drawable/ic__info" android:drawablePadding="2dp" android:textColor="@color/dialog_title" android:textSize="18sp" />

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:gravity="center" android:orientation="vertical">

        <TextView android:id="@+id/tv_msg" android:minLines="2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/dialog_msg" android:textSize="14sp" />
    </LinearLayout>

    <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right" android:orientation="horizontal">
        <!-- The buttons here use TextView Rather than using Button The reason is that Button There will be a default background inside , Own width and height , It's not easy to format -->
        <TextView android:visibility="gone" android:id="@+id/btn_cancel" style="@style/DialogButton" android:textColor="@color/dialog_button_cancel" />

        <TextView android:id="@+id/btn_submit" style="@style/DialogButton"/>
    </LinearLayout>
    
</LinearLayout>

It's simple , It's an external one LinearLayout. Then the outer layout is wide and high match_parent.

2、 Problem analysis & solve

The reason for the abnormal display of the above form is that when you use the first point of this article to configure the dialog box , Those codes are filled in the wrong place . Let's look at the first line of code :

Window window=getDialog().getWindow();

First , Need to ensure getDialog() Can't be empty . Based on the previous analysis , As early as onCreateDialog Get value in it . But if you write the code of the first point here , congratulations , Successfully captured an abnormal pop-up window .
Why? ? Think about our line of code , We don't operate dialog, It's inside window object . We set the width and height of the dialog box to match_parent. Therefore, it will involve the width and height of its host layout . The host of the dialog box is fragment,fragment When creating a layout, it is in onCreateView in .view When the creation is completed, it is in onViewCreated(View v) in , Inside v Namely onCreateView The one returned in .view After you create , Naturally, there is the width and height of the host layout , Then call the method of the first point , Can solve the problem .
 Normal pop-up window

	@Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    
        super.onViewCreated(view, savedInstanceState);
        initWindow(getDialog().getWindow());
    }

there initWindow The method is the content of the first point .

5、 ... and 、getActivity() return null

1、 Problem recurrence

dialogFragment There's a way to :
show
This method is used to display a dialogFragment. These two methods actually correspond to Fragment Some processes during creation . among , first fragmentManager Parameters , In general , We can use activity Of getSupportFragmentManager Come and get it , So , We can encapsulate a simple show Method :

	public void show(){
    
        show(getActivity().getSupportFragmentManager(),"XXXX");
    }

Suppose we customize one MsgDialog Pop-up windows , To display this pop-up window , We can write this way :

MsgDialog d=new MsgDialog();
d.show();

look , It is much simpler than the official method call . But once you run , Will report getActivity() Null pointer exception .

2、 Problem analysis & solve

First , We'll settle it according to the getDialog Think about the empty solution , have a look , Isn't there a step in the life cycle that doesn't get getActivity.

	@Override
    public void onAttach(@NonNull Context context) {
    
        super.onAttach(context);
        Log.d(TAG, "onAttach.getActivity->" + getActivity());
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate.getActivity->" + getActivity());
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
    
        Log.d(TAG, "onCreateDialog.getActivity->" + getActivity());
        return super.onCreateDialog(savedInstanceState);
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    
        Log.d(TAG, "onCreateView.getActivity->" + getActivity());
        rootView = inflater.inflate(getLayoutId(), null);
        return rootView;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG, "onViewCreated.getActivity->" + getActivity());
    }

But a look at the log , Just stunned :

2021-09-18 10:48:06.645 15631-15631/com.cjs.kotlinapp D/BDF: onAttach.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.645 15631-15631/com.cjs.kotlinapp D/BDF: onCreate.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.645 15631-15631/com.cjs.kotlinapp D/BDF: onCreateDialog.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.648 15631-15631/com.cjs.kotlinapp D/BDF: onCreateView.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.678 15631-15631/com.cjs.kotlinapp D/BDF: onViewCreated.getActivity->com.cjs.kotlinapp.MainActivity@97996d1

All life cycles can get values .onAttach It's the earliest , You can get values here , Naturally, the back can .
Is there anything better than attach Earlier ? Think about it. , only MsgDialog The constructor for ., So , I also printed it in the constructor , As a result, the cause of the problem was found :

2021-09-18 10:48:06.628 15631-15631/com.cjs.kotlinapp D/BDF:  structure 
2021-09-18 10:48:06.645 15631-15631/com.cjs.kotlinapp D/BDF: onAttach.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.645 15631-15631/com.cjs.kotlinapp D/BDF: onCreate.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.645 15631-15631/com.cjs.kotlinapp D/BDF: onCreateDialog.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.648 15631-15631/com.cjs.kotlinapp D/BDF: onCreateView.getActivity->com.cjs.kotlinapp.MainActivity@97996d1
2021-09-18 10:48:06.678 15631-15631/com.cjs.kotlinapp D/BDF: onViewCreated.getActivity->com.cjs.kotlinapp.MainActivity@97996d1

We show the operation of the form , First new Again show. only new When , Will find , Not going dialogFragment Any life cycle of , If you leave , Must be calling show after . Then I went to see show Source code :

	/** * Display the dialog, adding the fragment to the given FragmentManager. This * is a convenience for explicitly creating a transaction, adding the * fragment to it with the given tag, and {@link FragmentTransaction#commit() committing} it. * This does <em>not</em> add the transaction to the fragment back stack. When the fragment * is dismissed, a new transaction will be executed to remove it from * the activity. * @param manager The FragmentManager this fragment will be added to. * @param tag The tag for this fragment, as per * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. */
    public void show(@NonNull FragmentManager manager, @Nullable String tag) {
    
        mDismissed = false;
        mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();
    }

Found only when calling show Only when I go to FragmentTransaction Add fragment And then commit. So the answer is clear , only new It's only equivalent to creating a hair dryer , Only a call show Plug in the power , The wind will turn .
So let's be honest where to call , Just where fragmentManager.

版权声明
本文为[Senzhiqianshou]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231405036982.html