Как правильно получить значение DatePicker в родительском фрагменте вместо родительской активности - PullRequest
0 голосов
/ 10 января 2020

У меня сложное приложение с тоннами фрагментов и субфрагментов, и мне нужно, чтобы слушатели слушали фрагмент, а не активность. Обычно это работает, но не с указателями даты или времени.

Вот пример приложения с активностью с одним раздутым фрагментом - TestFragmentMain. Этот раздутый фрагмент имеет еще два фрагмента - один с одним EditText (TestFragment_InputBox) и другой с одним TextView (TestFragment_DateBox), и он используется для прослушивания событий (TextChangeListener и DateChangeListener).

При изменении текста в первом фрагменте, все работает как брелок, основной фрагмент получает результат.

Однако при изменении даты я получаю сообщение об ошибке:

java.lang.NullPointerException: Attempt to invoke interface method 'void com.gmail.xxx.xxx.test.DateChangeListener.dateChanged(int, int, int)' on a null object reference
    at com.gmail.xxx.xxx.test.TestFragment_DatePicker.onDateSet(TestFragment_DatePicker.java:32)

Я действительно не понимаю, почему. Любая помощь приветствуется.

Основная деятельность:

public class TestClass extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);
        TestFragmentMain testFragmentMain = new TestFragmentMain();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.test_fragment_container, testFragmentMain);
        ft.commitAllowingStateLoss();
    }
}

Основной фрагмент со слушателями

public class TestFragmentMain extends Fragment implements TextChangeListener, DateChangeListener  {

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

        TestFragment_InputBox  testFragmentInputBox = new TestFragment_InputBox();
        FragmentTransaction ft = getChildFragmentManager().beginTransaction();
        ft.replace(R.id.test_fragment_inputbox_container, testFragmentInputBox);
        ft.commitAllowingStateLoss();

        TestFragment_DateBox  testFragmentDateBox = new TestFragment_DateBox();
        ft = getChildFragmentManager().beginTransaction();
        ft.replace(R.id.test_fragment_date_container, testFragmentDateBox);
        ft.commitAllowingStateLoss();

        return view;
    }

    @Override
    public void onAttach(@NotNull Context context) {
        super.onAttach(context);
    }

    @Override
    public void textChanged() {
        Log.d("LISTEN","Text has been changed...");
    }

    @Override
    public void dateChanged(int year, int month, int day) {
        Log.d("LISTEN","Date has been changed to ...");
    }
}

Фрагмент с полем ввода:

public class TestFragment_InputBox extends Fragment {

    private TextChangeListener textChangeListener;

    @Override
    public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.test_input_box, container, false);
        EditText editText = view.findViewById(R.id.input_view);

        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                textChangeListener.textChanged();
            }
        });
        return view;
    }

    @Override
    public void onAttach(@NotNull Context context) {
        super.onAttach(context);
        try {
            textChangeListener = (TextChangeListener) getParentFragment();
        } catch (ClassCastException e) {
            e.printStackTrace();
        }
    }
}

Фрагмент с полем с датой:

public class TestFragment_DateBox extends Fragment implements DateChangeListener {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.test_date_box, container, false);
        TextView textView = view.findViewById(R.id.date_view);

        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DialogFragment datePicker = new TestFragment_DatePicker();
                datePicker.show(getActivity().getSupportFragmentManager(), "datePicker");
            }
        });
        return view;
    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }

    @Override
    public void dateChanged(int year, int month, int day) {
        Log.d("LISTEN","Date has been changed in box fragment with box ...");

    }
}

Фрагмент выбора даты

public class TestFragment_DatePicker extends DialogFragment implements DatePickerDialog.OnDateSetListener {

    public DateChangeListener dateChangeListener;

    @NotNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);
        return new DatePickerDialog(Objects.requireNonNull(getContext()), this, year, month, day);
    }

    @Override
    public void onDateSet(DatePicker view, int year, int month, int day) {
        dateChangeListener.dateChanged(year,month,day);
    }

    @Override
    public void onAttach(@NotNull Context context) {
        super.onAttach(context);
        try {
            dateChangeListener = (DateChangeListener) getParentFragment();
        } catch (ClassCastException e) {
            e.printStackTrace();
        }
    }
}

И слушатели:

public interface TextChangeListener {
    void textChanged();
}

public interface DateChangeListener {
    void dateChanged(int year,int month,int day);
}

Ответы [ 2 ]

0 голосов
/ 10 января 2020

Я нашел решение.

В TestFragmentMain необходимо выполнить TestFragment_DateBox с тем же FragmentManager, что и DatePicker. И это код в TestFragmentMain:

    TestFragment_DateBox testFragmentDateBox = new TestFragment_DateBox();
    FragmentTransaction  ft2 = this.getChildFragmentManager().beginTransaction();
    ft2.replace(R.id.test_fragment_date_container, testFragmentDateBox);
    ft2.commitAllowingStateLoss();

Изменение также в TestFragment_DateBox при открытии DatePicker:

    DialogFragment datePicker = new TestFragment_DatePicker();
    datePicker.setTargetFragment(this, 0);
    datePicker.show(this.getFragmentManager(), "datePicker");

Для работы необходимо запустить setTargetFragment.

Теперь этот тест работает.

0 голосов
/ 10 января 2020

Проблема в том, что вы не раздуваете TestFragment_DatePicker, который является DialogFragment, который является Fragment, но возвращаете DatePickerDialog, который является AlertDialog, который является Dialog, который не имеет mParentFragment. Вот почему getParentFragment() возвращает ноль.

Это похоже на плохой дизайн архитектуры, который мы унаследовали от старых версий android.

...