java - 在 Java 中的 Android fragment 之间进行通信

标签 java android android-activity fragment communication

好的,我有一个简单的问题。以下是 Fragment 之间进行通信的一种非常简单和基本的方法,但我从未见过有人使用它。我想知道使用这种方法的缺点是什么。如果有人有任何意见,请在答案中告诉我。谢谢

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

fragment 一的代码:

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

还有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;
}
}

附言请忽略缩进等,我没有复制这段代码我实际上输入了整个内容。

最佳答案

FragmentOne fragmentOne;
FragmentTwo fragmentTwo;


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

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

这将在 low-memory condition 之后中断,此时 Android 使用它们的无参数构造函数重新创建您的 Fragment 实例,而不是使用您在此处创建的实例。

尽管从技术上讲,在这种特殊情况下,您使用的是 fragmentManager.beginTransaction().replace().commit(),因此您的实例将获胜,系统重新创建的实例将被覆盖。当您使用 replace 而不是 add 时,您也不会以“多个重叠 fragment ”结束。

但是,您将失去通过 Fragment.onSaveInstanceState/Fragment.onCreate(Bundle) 恢复 fragment 的能力,因为您将使用以下方法覆盖系统重新创建的 fragment 你自己的未初始化 fragment 。

基本上,这种方法会导致状态丢失。


开箱即用,预期的解决方案是使用

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 找到 fragment 。


虽然我个人不喜欢 Fragment backstack,但我相信如果你跟踪描述你应该拥有的 fragment 的标识符会容易得多,这些标识符可以为你提供给定的标签,你可以将 fragment 设置为任何状态您希望它们不必仅仅因为您向前导航就删除 fragment (并因此丢失其 View + 状态)。

您可以查看我倾向于使用 fragment here 的方法。

使用以下代码,我可以将任何 Fragment 设置为我想要的任何状态,而无需依赖 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()) 而不是杂耍事务,就无法再想象 Fragments。

关于java - 在 Java 中的 Android fragment 之间进行通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58289746/

相关文章:

java - Request[/UsrAction] 不包含名为 'method' 的处理程序参数。这可能是由标签文本中的空格引起的

java - JAX-RS 与 RESTeasy Factory

java - Android fragment - 空对象引用

android - 轮廓脸,戴眼镜检测不好

android - 从 parseObject 获取文件

android - FLAG_ACTIVITY_CLEAR_TOP 没有按预期工作

java - 安卓开发 : java NullPointerException When Trying To getStringExtra()

java - Java 中的双重调度示例

java - 插页式广告关闭后如何执行 Action ?

android - 更改布局字体的生命周期事件