DialogFramegtLearning Sharing

DialogFramegtLearning

1. Why should we use DialogFragment?

  1. Life cycle management: DialogFragment has its own life cycle, which is independent of the life cycle of Activity. This allows DialogFragment to better handle state saving and restoration in cases such as screen rotation or configuration changes. And Dialog needs to manually manage these life cycle events.

  2. Flexible interface management: DialogFragment can define its own user interface in the layout file, making the creation and management of the interface more flexible. You can use XML layouts or build the interface programmatically. Dialog usually needs to use code programmatically to create and configure the interface.

  3. Interact with Fragment: DialogFragment can interact with the Activity and other Fragments it belongs to. It can communicate with the Activity and other Fragments through the callback interface, the observer mode, or using Fragment methods. And Dialog usually cannot directly interact with Activity and other Fragments.

  4. Device compatibility: DialogFragment provides better device compatibility. It adapts to screens of different sizes and provides a consistent user experience across tablets and phones. However, the style and size of Dialog may be displayed inconsistently on different devices.

  5. Management and reuse: DialogFragment can be managed by FragmentManager, making it easier to display, hide, replace and remove. It can also be identified and found by label, so that it can be redisplayed or manipulated when needed. However, Dialog usually needs to manually manage its display and hiding, and it is difficult to achieve reuse.

To sum up, DialogFragment has the advantages of better life cycle management, interface flexibility, interaction with Fragment, device compatibility and management reuse than Dialog. Therefore, in most cases, it is recommended to use DialogFragment to implement dialog-style user interface.

2. Simple to use

The essence of DialogFragment is to use Fragmnet to manage the life cycle of Dialog.

Step 1: Create a dialog layout fileldialig_fragment_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="#ECE1E1"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingTop="20dp">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="第一个Dialog"
        android:textSize="20sp"
        android:textStyle="bold" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginTop="20dp"
        android:background="#999999" />

    <LinearLayout

        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_close"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="bottom"
            android:layout_weight="1"
            android:gravity="center"
            android:text="取消"
            android:textSize="18sp" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="#999999" />

        <TextView
            android:id="@+id/tv_confirm"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_gravity="bottom"
            android:layout_weight="1"
            android:gravity="center"
            android:text="确定"
            android:textColor="#3085CE"
            android:textSize="18sp" />
    </LinearLayout>
</LinearLayout>

Note: The settings in the root layout here do not take effect, we need to set them in the code.

Step 2: Customize DialogFragmnet

public class CustomDialogFragment extends DialogFragment {
    
    

    private View mView;

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

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


    @Override
    public void onResume() {
    
    
        super.onResume();
    }

    @Override
    public void onDismiss(@NonNull DialogInterface dialog) {
    
    
        super.onDismiss(dialog);
    }

    private void initWindow() {
    
    
        if (getDialog() != null) {
    
    
            //初始化window相关表现
            Window window = getDialog().getWindow();
            //设置window宽高(单位px)
            window.getAttributes().width = 700;
            //设置window位置:居中
            window.getAttributes().gravity = Gravity.CENTER;
        }
    }
    private void initView() {
    
    
        TextView button1 = mView.findViewById(R.id.tv_confirm);
        button1.setOnClickListener(v -> {
    
    
            Toast.makeText(getContext(), "mgs", Toast.LENGTH_SHORT).show();
            dismiss();
        });
    }
}

My customization here is very simple, and it should not be difficult to understand.

Step 3: Display DialogFragment in Activity

CustomDialogFragment customDialogFragment = new CustomDialogFragment();
customDialogFragment.showNow(getSupportFragmentManager(), "customDialogFragment");

The above three steps can realize the pop-up of the dialog.

Other effects:

  1. Click the back button does not disappearDialogFragment

     customDialogFragment.getDialog().setOnKeyListener(new DialogInterface.OnKeyListener() {
          
          
                @Override
                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
          
          
                    return keyCode == KeyEvent.KEYCODE_BACK;
                }
            });
    
  2. Click outside the pop-up window, the pop-up window disappears

      if (customDialogFragment.getDialog() != null) {
          
          
                customDialogFragment.getDialog().setCancelable(true);
            }
    

    There are many more... such as pop-up animation, you can search online according to your needs

3. Analyze the source code

1. The function of the method: it is used to perform the dismiss operation inside the DialogFragment.

  1. boolean allowStateLoss : Whether to allow state loss
  2. boolean fromOnDismiss: Whether it is triggered by the onDismiss method
 private void dismissInternal(boolean allowStateLoss, boolean fromOnDismiss) {
    
    
        if (mDismissed) {
    
    
            return;
        }
      //mDismissed标志设置为 true,表示已经进行了 dismiss 操作
      //mShownByMe 标志设置为 false,表示 DialogFragment 不再处于显示状态。
        mDismissed = true;
        mShownByMe = false;
        if (mDialog != null) {
    
    
            mDialog.setOnDismissListener(null);
            mDialog.dismiss();
            if (!fromOnDismiss) {
    
    
                //判断当前线程是否为主线程
                if (Looper.myLooper() == mHandler.getLooper()) {
    
    
                    onDismiss(mDialog);
                } else {
    
    
                    mHandler.post(mDismissRunnable);
                }
            }
        }
      //mViewDestroyed 标志设置为 true,表示 DialogFragment 的视图已被销毁
        mViewDestroyed = true;
      //DialogFragment 在回退栈中有对应的 ID,进行弹出栈,否则提交事物
      //
        if (mBackStackId >= 0) {
    
    
            getParentFragmentManager().popBackStack(mBackStackId,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            mBackStackId = -1;
        } else {
    
    
            FragmentTransaction ft = getParentFragmentManager().beginTransaction();
            ft.remove(this);
            if (allowStateLoss) {
    
    
                ft.commitAllowingStateLoss();
            } else {
    
    
                ft.commit();
            }
        }
    }

1. What is a submission?

If you only give a noun for submitting things, you can't understand this piece of code. So what is a commit thing?

Committing a transaction is a way to apply Fragment operations (add, remove, replace, etc.) to the Fragment manager. Transactions are used to encapsulate a series of Fragment operations together and ensure that these operations are executed atomically, thereby maintaining the consistency of the Fragment manager.

  getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, fragment)
                    .commit();

Does the above code look familiar, dynamic replacement of fragments.

By committing transactions, we can guarantee the atomicity, batch execution, transaction rollback and state preservation of Fragment operations, providing a reliable and consistent way to manage operations such as adding, removing, and replacing Fragments, and ensuring that Fragment Manager correctness and stability.

2.commitAllowingStateLoss()和commit()

Both of these are submitted transactions, but there are some differences. From our allowStateLoss judgment, it can be seen that for the transaction, it is commitAllowingStateLoss()allowed to submit the transaction when the state is lost, and try to restore the transaction state after the Activity is rebuilt. For example, after our screen is rotated, it will be in the same state as before we rotated. For commit()the case, if the Activity's state saving process (such as screen rotation) or the process is destroyed and rebuilt before the transaction is committed, then when the state is restored, if the transaction has not been executed, an IllegalStateException will be thrown, resulting in the loss of the transaction.

- commit()When committing a transaction, if a state save or process rebuild occurs before the transaction commits, it may cause the transaction to be lost and throw an exception.
- commitAllowingStateLoss()When committing a transaction, it is possible to commit the transaction even if the state is lost, but it may cause some side effects. Operations that cannot be undone by transaction rollback
- Should be preferred if the state of the transaction is critical to the correctness of the application, commit()with appropriate handling to avoid state loss. Only consider using it if you are sure that loss of state will not cause serious problems commitAllowingStateLoss().

3. Why do you still need to submit things when the fragment is no longer in the rollback stack?

  • If you look at the code carefully, you will find the above problem, the fragment is not in the rollback stack, then the current instance should have been destroyed, and the life cycle has ended. Why are we still submitting things?

The purpose of committing a transaction is not to maintain DialogFragmentthe lifecycle of the but to ensure that FragmentManageroperations on the are performed correctly.

Even DialogFragmentafter the has been destroyed, it is still necessary to call the method FragmentTransactionof the . remove()This is because the operation will notify FragmentManagerthe removal related Fragment, and update its internal state, so that it will be correctly processed when the next transaction commits or rolls back the stack operation.

Committing a transaction is not only used to process the currently existing Fragmentinstance, it is also used to update FragmentManagerthe state of the and maintain its internal data structures. This is necessary for subsequent transaction operations, rollback stack management, and other operations. Therefore, DialogFragmentcommitting a transaction is still required even after it has been destroyed.

To sum up, the purpose of committing a transaction is not to maintain DialogFragmentthe life cycle of , but to ensure that FragmentManagerthe operations on can be correctly executed and synchronized. Even DialogFragmentif is not on the back stack and has been destroyed, committing the transaction is still necessary to ensure that FragmentManagerthe state and internal data structures of the are consistent and reliable.

  • Then when will we execute the following method of submitting things?

A situation I understand: it may happen in the case of continuous dismiss operations. Because this code block is not locked. So there may be synchronous operations.

2. The role of the method: display dialogFragmnet

public void showNow(@NonNull FragmentManager manager, @Nullable String tag) {
    
    
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commitNow();
}

He also has several similar methods:

public void show(@NonNull FragmentManager manager, @Nullable String tag) {
    
    
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();
}
public int show(@NonNull FragmentTransaction transaction, @Nullable String tag) {
    
    
    mDismissed = false;
    mShownByMe = true;
    transaction.add(this, tag);
    mViewDestroyed = false;
    mBackStackId = transaction.commit();
    return mBackStackId;
}
  • The achieved effect is the same, we give priority to using the showNow method here, as for why?

1. show is slightly "slower" than showNow, which causes the view in the dialog to be modified immediately after calling show (for example, textView to modify the character content) will crash, and showNow will not first add the method to FirstDialogFragment
:

    fun setContent(text: String) {
    
    
        tv_content.text = text
    }

The following code will crash:

        val firstDialog = FirstDialogFragment()
        firstDialog.show(supportFragmentManager, "First")
        firstDialog.setContent("Hello")

The following code is executed normally (the content of the dialog box is changed to Hello):

        val firstDialog = FirstDialogFragment()
        firstDialog.showNow(supportFragmentManager, "First")
        firstDialog.setContent("Hello")

Guess you like

Origin blog.csdn.net/qq_43867812/article/details/131435353