在 Java 中的片段 Android 之间进行通信
Communicating between Fragments Android in Java
好的,我有一个简单的问题。以下是 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);
}
}
片段一代码:
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;
}
}
P.S。请忽略缩进等,我没有复制这段代码我实际上输入了整个内容。
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
时,您也不会以 "multiple overlapping fragments".
结束
但是,您将失去通过 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
.
找到片段
虽然我个人不喜欢 Fragment backstack,但我相信如果你跟踪描述你应该拥有的片段的标识符会容易得多,这些标识符可以为你提供给定的标签,你可以将片段设置为无论您希望它们处于什么状态,而不必仅仅因为您向前导航就删除片段(并因此失去其视图 + 状态)。
您可以查看我倾向于使用片段的方法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。
好的,我有一个简单的问题。以下是 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);
}
}
片段一代码:
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;
}
}
P.S。请忽略缩进等,我没有复制这段代码我实际上输入了整个内容。
FragmentOne fragmentOne; FragmentTwo fragmentTwo; public void showFragmentOne() { if(fragmentOne == null){ fragmentOne = FragmentOne(); } // ... } public void showFragmentTwo(String name) { if(fragmentTwo == null) { fragmentTwo = new FragmentTwo(); } // ...
这将在
虽然从技术上讲在 这个 特殊情况下,您使用的是 fragmentManager.beginTransaction().replace().commit()
,因此 您的 实例将获胜,并且系统重新创建的将被覆盖。当您使用 replace
而不是 add
时,您也不会以 "multiple overlapping fragments".
但是,您将失去通过 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
.
虽然我个人不喜欢 Fragment backstack,但我相信如果你跟踪描述你应该拥有的片段的标识符会容易得多,这些标识符可以为你提供给定的标签,你可以将片段设置为无论您希望它们处于什么状态,而不必仅仅因为您向前导航就删除片段(并因此失去其视图 + 状态)。
您可以查看我倾向于使用片段的方法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。