简体   繁体   English

Java中的片段Android之间的通信

[英]Communicating between Fragments Android in Java

ok, I have a simple question.好的,我有一个简单的问题。 Following is a very simple and basic approach to communicate between Fragments, but I've never seen anyone use it.以下是在 Fragment 之间进行通信的一种非常简单且基本的方法,但我从未见过有人使用它。 I'm wondering what are the cons of using this approach.我想知道使用这种方法有什么缺点。 If anyone has any comments please let me know in the answer.如果有人有任何意见,请在答案中告诉我。 Thank you谢谢

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);
}
}

The code for fragment one:片段一的代码:

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);
}

And the code for fragmentTwo以及 fragmentTwo 的代码

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 please ignore the indentation etc, I didn't copy this code I actually typed the whole thing. PS请忽略缩进等,我没有复制这段代码,我实际上输入了整个内容。

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

This will break after low-memory condition , when Android recreates your Fragment instances using their no-arg constructor, rather than using the instances you're creating here.当 Android 使用它们的无参数构造函数而不是使用您在此处创建的实例重新创建您的 Fragment 实例时,这将在低内存条件后中断。

Although technically in this particular case, you are using fragmentManager.beginTransaction().replace().commit() , therefore your instance will win, and the one recreated by the system will be overridden.尽管从技术上讲,在这种特殊情况下,您使用的是fragmentManager.beginTransaction().replace().commit() ,因此您的实例将获胜,系统重新创建的实例将被覆盖。 As you used replace instead of add , you also won't end up with "multiple overlapping fragments".当您使用replace而不是add时,您也不会得到“多个重叠片段”。

However, you lose the ability to restore the Fragment via Fragment.onSaveInstanceState / Fragment.onCreate(Bundle) , as you will be overwriting the system-recreated fragment using your own uninitialized fragment.但是,您将无法通过Fragment.onSaveInstanceState / Fragment.onCreate(Bundle)恢复 Fragment,因为您将使用自己的未初始化片段覆盖系统重新创建的片段。

Basically, this approach results in state-loss.基本上,这种方法会导致状态丢失。


Out of the box, the intended solution is to use开箱即用,预期的解决方案是使用

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();
    }
}

And you could potentially find the fragments using findFragmentByTag .您可能会使用findFragmentByTag找到片段。


Although I personally don't like the Fragment backstack, I believe it's much easier if you track identifiers that describe the fragments you should have, these identifiers can provide you the given tag, and you can set up the fragments to whatever state you want them to be without necessarily removing the fragment (and therefore losing its view + state) just because you navigated forward.虽然我个人不喜欢 Fragment backstack,但我相信如果您跟踪描述您应该拥有的片段的标识符会容易得多,这些标识符可以为您提供给定的标签,并且您可以将片段设置为您想要的任何 state不必仅仅因为您向前导航就删除片段(因此失去其视图+状态)。

You can check out the approach I tend to use for fragments here .您可以在此处查看我倾向于用于片段的方法。

Using the following code, I could set up any Fragment to be in any state that I wanted, without relying on addToBackStack .使用以下代码,我可以将任何 Fragment 设置为我想要的任何 state 中,而无需依赖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();
    }
}

And then I could use this like 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();
    }
}

Can't really imagine Fragments anymore without the convenience of saying backstack.goTo(SomeScreen()) instead of juggling transactions.如果没有方便地说backstack.goTo(SomeScreen())而不是杂耍交易,就无法真正想象 Fragments。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM