在片段之间调用方法时,必须经过activity吗?

When calling a method between fragments, must it go through an activity?

我想从A fragment打开一个dialog fragment,并在对话片段结束时将数据传递给A片段。

为此,我在对话框片段中定义了一个接口并在 A 片段中实现了它。

结果是侦听器为空。

这是因为 onAttach() 使用了 context 并且上下文意味着 activity

activity没有实现接口。

当然,在activity中实现接口可能很好,但我不想要它,因为方法角色与activity不匹配。

没有办法吗?

WriteRoutineFragment.java(片段)

public class WriteRoutineFragment extends Fragment implements 
        WorkoutListDialogFragment.OnAddRoutineListener {

    RecyclerView routine_rv;
    List<RoutineModel> items;
    List<String> titleData;
    RoutineListAdapter listAdapter;


    public static WriteRoutineFragment newInstance(Bundle data) {
        WriteRoutineFragment f = new WriteRoutineFragment();
        f.setArguments(data);
        return f;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.activity_write_routine, container, false);

        titleData = getArguments().getStringArrayList("title");
        listAdapter = new RoutineListAdapter(diffUtil2);
        items = new ArrayList<>();
//        routineAdapter = new RoutineAdapter();
        routine_rv.setAdapter(listAdapter);

        // ADD Routine
        listAdapter.setOnAddRoutineClickListener(new RoutineListAdapter.OnAddRoutineClickListener() {
            @Override
            public void onAddRoutineClick() {
                WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
                routineDialog.show(getActivity().getSupportFragmentManager(), "RoutineListDialog");
            }
        });

        return rootView;
    }

    @Override
    public void onAddRoutine(String routine) {
        RoutineModel routineModel = new RoutineModel(routine);
        RoutineDetailModel routineDetailModel = new RoutineDetailModel();
        routineModel.addDetail(routineDetailModel);

        items.add(routineModel);
        List<Object> list = getUpdatedList();
        listAdapter.submitList(list);
    }
}

DialogFragment.java

public class WorkoutListDialogFragment extends DialogFragment {
    static final String TAG = "RoutineListDialog";
    WorkoutListAdapter workoutListAdapter;
    OnAddRoutineListener listener;
    
    public interface OnAddRoutineListener {
        public void onAddRoutine(String routine);
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        if(context != null && context instanceof OnAddRoutineListener) {
            listener = (OnAddRoutineListener) context; // null
        }
        return;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_workout_list_dialog, container, false);
        routineListRecycler = view.findViewById(R.id.routine_list_recycler);
        rountineChipGroup = view.findViewById(R.id.routine_chipgroup);

        workoutListAdapter.setOnWorkoutListClickListener(new WorkoutListAdapter.OnWorkoutListItemClickListener() {
            @Override
            public void OnItemClick(String routine) {
                if(listener != null) {
                    listener.onAddRoutine(routine);
                    dismiss();
                }
            }
        });
        return view;
    }
}

为了处理这类问题,实时数据 上场了。通过使用实时数据,我们可以毫无问题地在片段之间传递数据。只需创建一个 View 模型,如下所示:

    public class RoutineViewModel extends ViewModel {

    private MutableLiveData<String> mRoutine;

    public RoutineViewModel() {
        mRoutine = new MutableLiveData();
    }
    public void setRoutine(String data){
        mRoutine.setValue(data);
    }

    public LiveData<String> getRoutine(){ return mRoutine;}
}

并在两个片段中使用此 View Model 以获得 Live Data 的最新更新值。

WriteRoutineFragment 中使用 View ModelLive Data 如下所示:

private RoutineViewModel routineViewModel;

public View onCreateView(@NonNull LayoutInflater inflater,
                         ViewGroup container, Bundle savedInstanceState) {
    routineViewModel =
            new ViewModelProvider(requireActivity()).get(RoutineViewModel.class);
    View root = inflater.inflate(R.layout.fragment_home, container, false);

    routineViewModel.getRoutine().observe(getViewLifecycleOwner(), new Observer<String>() {
        @Override
        public void onChanged(String s) {
            Log.d("TAG==>>","Routine changed = "+s);
        }
    });
    listAdapter.setOnAddRoutineClickListener(new RoutineListAdapter.OnAddRoutineClickListener() {
        @Override
        public void onAddRoutineClick() {
            WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
            routineDialog.show(getChildFragmentManager(), "RoutineListDialog");
        }
    });

    return root;
}

WorkoutListDialogFragment 中使用 View ModelLive Data 如下所示:

    public class WorkoutListDialogFragment extends DialogFragment {
    private RoutineViewModel routineViewModel;
    WorkoutListAdapter workoutListAdapter; 
    public WorkoutListDialogFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        routineViewModel =
                new ViewModelProvider(requireActivity()).get(RoutineViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_workout_list_dialog, container, false);
    }
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState){
        workoutListAdapter.setOnWorkoutListClickListener(new WorkoutListAdapter.OnWorkoutListItemClickListener() {
            @Override
            public void OnItemClick(String routine) {
                routineViewModel.setRoutine(routine);
                dismiss();
            }
        });
    }
}

在显示对话框时使用 getChildFragmentManager() 而不是 getActivity().getSupportFragmentManager(),现在上下文将绑定到片段并且侦听器不会为 null

listAdapter.setOnAddRoutineClickListener(new RoutineListAdapter.OnAddRoutineClickListener() {
        @Override
        public void onAddRoutineClick() {
            WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
            routineDialog.show(getChildFragmentManager(), "RoutineListDialog");
        }
    });