Общение между фрагментами Android в Java - PullRequest
2 голосов
/ 08 октября 2019

хорошо, у меня простой вопрос. Ниже приведен очень простой и простой подход к взаимодействию между фрагментами, но я никогда не видел, чтобы кто-нибудь использовал его. Мне интересно, каковы недостатки использования этого подхода. Если у кого-то есть какие-либо комментарии, пожалуйста, дайте мне знать в ответ. Спасибо

public class MyActivity extends Activity {
FragmentOne fragmentOne;
FragmentTwo fragmentTwo;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

}

private void showFragment(Fragment fragment) {
    getSupportFragmentManager().beginTransaction().replace(R.id.fragment, fragment).commit();
}

public void showFragmentOne() {
    if(fragmentOne == null){
        fragmentOne = FragmentOne();
    }
    showFragment(fragmentOne);
}

public void showFragmentTwo(String name) {
    if(fragmentTwo == null)
        fragmentTwo = new FragmentTwo();
    fragmentTwo.setData(name);
    showFragment(fragmentTwo);
}
}

Код для фрагмента один:

public class FragmentOne extends Fragment {
private MyActivity myActivity;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    myActivity = getActivity() instanceOf MyActivity ? (MyActivity) getActivity() : null;
    button.setOnClickListener(() -> {
        if(myActivity != null)
          myActivity.showFragmentTwo(editText.getText().toString()); //assuming there's an edit text
    }

    return inflater.inflate(R.layout.fragment_one, container, false);
}

И код для фрагмента Два

public class FragmentTwo extends Fragment {
private String name;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    textView.setText(name); //assuming there's a textview
    return inflater.inflate(R.layout.fragment_two, container, false);
}

public void setData(String name){
    this.name = name;
}
}

PS Пожалуйста, игнорируйте отступ и т. Д., Я не сделалне копируя этот код, я на самом деле набрал все это.

1 Ответ

0 голосов
/ 08 октября 2019
FragmentOne fragmentOne;
FragmentTwo fragmentTwo;


public void showFragmentOne() {
    if(fragmentOne == null){
        fragmentOne = FragmentOne();
    }
    // ...
}

public void showFragmentTwo(String name) {
    if(fragmentTwo == null) {
        fragmentTwo = new FragmentTwo();
    }
    // ...

Это будет прерываться после состояния нехватки памяти , когда Android воссоздает ваши экземпляры Fragment, используя их конструктор без аргументов, вместо использования создаваемых вами здесь экземпляров. .

Хотя технически в этом конкретном случае вы используете fragmentManager.beginTransaction().replace().commit(), следовательно, ваш экземпляр победит, а тот, который воссоздан системой, будет переопределен. Поскольку вы использовали replace вместо add, вы также не получите «несколько перекрывающихся фрагментов».

Однако вы потеряете способность восстановить фрагмент с помощью Fragment.onSaveInstanceState / Fragment.onCreate(Bundle), так как вы будете перезаписывать воссозданный системой фрагмент, используя свой собственный неинициализированный фрагмент.

По сути, этот подход приводит к потере состояния.


Из коробки, предполагаемое решениеэто использовать

public class MyActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if(savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction().replace(R.id.fragment, new FragmentOne(), "one").commit();
        }        
    }

    public void showFragmentTwo(String name) {
        FragmentTwo fragmentTwo = new FragmentTwo();
        Bundle bundle = new Bundle();
        bundle.putString("name", name);
        fragmentTwo.setArguments(bundle);
        getSupportFragmentManager().beginTransaction()
                  .replace(R.id.fragment, fragmentTwo, "two")
                  .addToBackStack(null)
                  .commit();
    }
}

И вы можете найти фрагменты, используя findFragmentByTag.


Хотя мне лично не нравится обратный стек фрагментов, я считаю, что это гораздо проще, есливы отслеживаете идентификаторы, которые описывают фрагменты, которые у вас должны быть, эти идентификаторы могут предоставить вам данный тег, и вы можете установить фрагменты в любое желаемое состояние, не обязательно удаляя фрагмент (и, следовательно, теряя его представление + состояние), простопотому что вы двинулись вперед.

Вы можете проверить подход, который я склонен использовать для фрагментов здесь .

Используя следующий код, я мог установить любой Фрагмент в любое нужное состояние, не полагаясь на addToBackStack.

public class FragmentStateChanger {
    private FragmentManager fragmentManager;
    private int containerId;

    public FragmentStateChanger(FragmentManager fragmentManager, int containerId) {
        this.fragmentManager = fragmentManager;
        this.containerId = containerId;
    }

    public void handleStateChange(StateChange stateChange) {
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.disallowAddToBackStack();

        // here you could animate based on direction
        List<Key> previousKeys = stateChange.getPreviousKeys();
        List<Key> newKeys = stateChange.getNewKeys();
        for(Key oldKey : previousKeys) {
            Fragment fragment = fragmentManager.findFragmentByTag(oldKey.getFragmentTag());
            if(fragment != null) {
                if(!newState.contains(oldKey)) {
                    fragmentTransaction.remove(fragment);
                } else if(!fragment.isDetached()) {
                    fragmentTransaction.detach(fragment);
                }
            }
        }
        for(Key newKey : newKeys) {
            Fragment fragment = fragmentManager.findFragmentByTag(newKey.getFragmentTag());
            if(newKey.equals(stateChange.topNewKey())) {
                if(fragment != null) {
                    if(fragment.isRemoving()) {
                        fragment = newKey.createFragment();
                        fragmentTransaction.replace(containerId, fragment, newKey.getFragmentTag());
                    } else if(fragment.isDetached()) {
                        fragmentTransaction.attach(fragment);
                    }
                } else {
                    fragment = newKey.createFragment();
                    fragmentTransaction.add(containerId, fragment, newKey.getFragmentTag());
                }
            } else {
                if(fragment != null && !fragment.isDetached()) {
                    fragmentTransaction.detach(fragment);
                }
            }
        }
        fragmentTransaction.commitAllowingStateLoss();
    }
}

И затем яможет использовать это как this :

public class MainActivity
        extends AppCompatActivity
        implements StateChanger {
    private static final String TAG = "MainActivity";

    @BindView(R.id.fragment)
    ViewGroup root;

    FragmentStateChanger fragmentStateChanger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        fragmentStateChanger = new FragmentStateChanger(getSupportFragmentManager(), R.id.fragment);

        Navigator.configure()
                .setStateChanger(this)
                .install(this, root, History.single(FragmentOneKey.create()));
    }

    @Override
    public void onBackPressed() {
        if(!Navigator.onBackPressed(this)) {
            super.onBackPressed();
        }
    }


    public void showSecondFragment(String data) {
         Navigator.getBackstack(this).goTo(FragmentTwoKey.create(data));
    }

    // this handles navigation from any nav state to any other nav state
    @Override
    public void handleStateChange(@NonNull StateChange stateChange, @NonNull Callback completionCallback) {
        if(stateChange.isTopNewKeyEqualToPrevious()) {
            completionCallback.stateChangeComplete();
            return;
        }
        fragmentStateChanger.handleStateChange(stateChange);
        completionCallback.stateChangeComplete();
    }
}

Не могу больше представлять фрагменты без удобства произносить backstack.goTo(SomeScreen()) вместо жонглирования транзакциями.

...