【问题标题】:Communicating between Fragments Android in Java用 Java 在 Fragments Android 之间进行通信
【发布时间】:2019-10-08 15:42:04
【问题描述】:

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

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

以及 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 不能用于不同的活动(会有一个 NPE),这对我来说违背了将其作为片段开始的目的:你可以只使用视图布局。
  • 同意,但其他活动也可以有一个 FragmentOne 对象,并且 FragmentOne 可以有多个活动对象并根据哪个活动托管它来执行方法。在我的应用程序中,用户登录后,他总是在进行一项活动。除此之外还有什么?

标签: java android android-activity fragment communication


【解决方案1】:
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.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 backstack,但我相信如果您跟踪描述您应该拥有的 Fragment 的标识符会容易得多,这些标识符可以为您提供给定的标签,并且您可以将 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。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-05
    • 2019-08-10
    • 2010-11-08
    相关资源
    最近更新 更多