引言
为了重复使用 Fragment 界面组件,您应将每个组件构建为一个完全独立的模块化组件,定义它自己的布局和行为。定义这些可重用的 Fragment 后,您可以将它们与 Activity 相关联,并将其与应用逻辑相关联以实现整个复合界面。
您经常需要一个 Fragment 与另一个 Fragment 通信,比如为了根据用户事件更改内容。所有 Fragment 到 Fragment 的通信都是通过共享的 ViewModel 或关联的 Activity 来完成的。两个 Fragment 绝不能直接通信。
如需在 Fragment 之间通信,建议创建一个共享的 ViewModel 对象。两个 Fragment 都可以通过所在的 Activity 访问 ViewModel。Fragment 可在 ViewModel 内更新数据,如果使用 LiveData 公开该数据,新状态会被推送至其他 Fragment(只要它正在从 ViewModel 观察 LiveData)。要了解如何实现这种通信机制,请参阅 ViewModel 指南中的“在 Fragment 之间共享数据”部分。
如果无法使用共享的 ViewModel 在 Fragment 之间进行通信,可以使用接口手动实现通信机制。但是,这种方式最终需要实现更多的工作,并且无法在其他 Fragment 中轻松重用。
以上这段文字来自 Android 官方文档中 Fragment 这一章节,以下实例我们是用接口手动实现通信机制的。
一、前言
<font face = 黑体>在 Fragment 与 Activity 之间的通信 这篇博客中,我们提了一下 Fragment 与 Activity 之间的最佳通信方式,但是我们没有深入的讲解,这篇博文我们就结合例子来讲一下这个知识。我们先来看一下我们今天的要做的实例的效果:
<font face = 黑体>这个实例就是一个 Activity 里面有两部分,这两部分分别加载了两个 Fragment,上面那个包含 EditText 的 Fragment 每次输入完数据后,按下回车键,要将数据在下面那个 ListFragment 里面展示出来,这就涉及到了 Fragment 与 Fragment 之间的通信。
二、ListFragment 简单介绍
- <font face = 黑体>ListFragment 包含一个 ListView;
- <font face = 黑体>使用 SimpleAdapter 或者 SimpleCursorAdapter 作为适配器;
- <font face = 黑体>直接使用 ListFragment 的 setListAdapter() 来设置适配器。
三、实现步骤
3.1、新建 Activity 并静态添加两个 Fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Index4Activity">
<fragment
android:id="@+id/fragment_new_item"
android:name="com.zjgsu.fragmentdemo.fragment.NewItemFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<fragment
android:id="@+id/fragment_to_do_list"
android:name="com.zjgsu.fragmentdemo.fragment.ToDoListFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
3.2、监听 EditText 的回车事件并获取数据
<font face = 黑体>这个方法就直接在 Fragment 的 onCreateView() 里面完成了,具体代码如下所示(完整代码文末给出):
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_item, container, false);
final EditText newItem = view.findViewById(R.id.et_new_item);
// 绑断按下操作
newItem.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
// 判断回车键
if (keyCode == KeyEvent.KEYCODE_ENTER) {
String content = newItem.getText().toString();
// 调用接口中的方法
onNewItemAddListener.newItemAdded(content);
newItem.setText("");
return true;
}
}
return false;
}
});
return view;
}
3.3、利用接口传递数据到附着的 Activity 中
<font face = 黑体>在获取到 EditText 输入的数据后,我们需要利用接口的方式将数据传递给其附着的 Activity。之所以要利用接口的方式先将数据传递给 Activity,而不是直接在 Fragment 里面利用附着的 Activity 获取到下面的 ListFragment 就是为了解耦合。即 Fragment 与 Activity 通信、Fragment 与另外一个 Fragment 通信,都应该依靠他们所附着的 Activity 来完成通信代码,而不是在 Fragment 直接获得这个引用来完成业务代码。
public class NewItemFragment extends Fragment {
// Fragment 与 Activity 通信
// Fragment 与 另外一个 Fragment 通信
// 都应该依靠他们所附着的 Activity 来完成通信代码,而不是在 Fragment 直接获得这个引用来完成业务代码
// 声明一个接口,定义你要向 Activity 传递的方法
public interface OnNewItemAddListener {
public void newItemAdded(String content);
}
// 声明一个接口引用变量
private OnNewItemAddListener onNewItemAddListener;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// 要求该 Fragment 所附着的 Activity 必须实现这个方法
onNewItemAddListener = (OnNewItemAddListener) context;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_new_item, container, false);
final EditText newItem = view.findViewById(R.id.et_new_item);
// 绑定按下操作
newItem.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
// 判断回车键
if (keyCode == KeyEvent.KEYCODE_ENTER) {
String content = newItem.getText().toString();
onNewItemAddListener.newItemAdded(content);
newItem.setText("");
return true;
}
}
return false;
}
});
return view;
}
}
3.4、在 Activity 中实现接口并将数据添加到 ListFragment 中
<font face = 黑体>在 Activity 中我们需要实现上述 Fragment 定义的接口获取数据,然后将数据添加到 ListFragment 中。
public class Index4Activity extends AppCompatActivity implements NewItemFragment.OnNewItemAddListener {
/**
* ListView
* Adapter
* data
*/
// 数据源
private ArrayList<String> data;
// 适配器
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index4);
// 初始化
data = new ArrayList<>();
// 构建适配器
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
// 在 Activity 中获得其管辖的 Fragment
ToDoListFragment fragment = (ToDoListFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_to_do_list);
if (fragment != null) {
fragment.setListAdapter(adapter);
}
}
@Override
public void newItemAdded(String content) {
// 构建数据源
data.add(content);
adapter.notifyDataSetChanged();
}
}
<font face = 黑体>ToDoListFragment 的代码如下所示:
public class ToDoListFragment extends ListFragment {
@Override
public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Toast mToast = Toast.makeText(getActivity(), "第 " + (position + 1) + " 项被选中了.", Toast.LENGTH_SHORT);
mToast.setGravity(Gravity.CENTER, 0, 0);
mToast.show();
}
}
四、源码
<font face = 黑体>源码已经上传至 github。下一篇博客我们来讲一个用 Fragment 实现的简易新闻实例,并适配手机和pad。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。