Android学习之在Adapter中调用Fragment
•前言
在学习《第一行代码》,4.5 小节——一个简易版的新闻应用的时候🔗;
在为 RecyclerView 创建适配器的时候;
作者直接在 NewsTitleFragment.java 中新建了一个内部类 NewsAdapter 来作为 RecyclerView 的适配器;
我就在想,是不是可以将 NewsAdapter 摘出来,作为一个独立的类来使用;
本着求知若渴的态度,便产生了这篇博客;
•前行必备
在简易版的新闻应用中,为了区分平板和手机,新建了一个 layout-sw600dp 文件夹;
而本节的重点是如何在 Adapter 中调用 Fragment,所以一切从简;
新建一个项目,我命名为 TestFragment,并选择 Empty Activity;
这样 Android Studio 自动生成了 MainActivity.java 和 activity_main.xml 文件;
•渐入佳境
首先,准备好一个新闻实体类,新建类 News;
News.java
public class News { String title; String content; News(String title,String content){ this.title = title; this.content = content; } public String getTitle() { return title; } public String getContent() { return content; } }title 表示新闻标题,content 表示新闻内容;
接着新建布局文件 right_fragment.xml,用于作为新闻内容的布局;
right_fragment.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp" android:background="@color/yello"> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textSize="30sp" android:textColor="@color/black" /> <View android:layout_width="match_parent" android:layout_height="2dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:background="@color/black"/> <TextView android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textSize="20sp" android:textColor="@color/gray" /> </LinearLayout>该代码的布局和新闻应用中的 news_content_frag.xml 布局大同小异,这里不再赘述;
在新建一个 RightFragment 类,继承自 Fragmet;
RightFragment.java
public class RightFragment extends Fragment { View view; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { view = inflater.inflate(R.layout.right_fragment,container,false); return view; } public void refresh(String title,String content){ TextView mTitle = view.findViewById(R.id.title); TextView mContent = view.findViewById(R.id.content); mTitle.setText(title); mContent.setText(content); } }该代码的功能同新闻应用中的 NewsContentFragment 相同;
接下来创建一个用于显示新闻列表的布局,新建 left_fragment.xml;
left_fragment.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/green" android:padding="10dp"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/left_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>功能同新闻应用中的 news_title_frag;
新建 item.xml 作为 RecyclerView 子项的布局;
item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/item_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:textStyle="bold" /> </LinearLayout>该代码中仅放置了一个 TextView 用于展示标题;
接下来我们就需要一个用于展示新闻列表的地方;
新建一个 LeftFragment 类作为展示新闻列表的碎片;
LeftFragment.java
public class LeftFragment extends Fragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.left_fragment, container, false); return view; } }在 onCreateView() 方法中加载了 left_fragment 布局;
接下来就是为 RecyclerView 添加适配器,有两种方式:
- 在 LeftFragment.java 中新建一个内部类 NewsAdapter 来作为 RecyclerView 的适配器
- 将 NewsAdapter 作为一个独立的类作为 RecyclerView 的适配器
•内部类方式
修改 LeftFragment.java 中的代码;
LeftFragment.java
public class LeftFragment extends Fragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.left_fragment, container, false); RecyclerView rv = view.findViewById(R.id.left_recycler_view); LinearLayoutManager manager = new LinearLayoutManager(null); rv.setLayoutManager(manager); NewsAdapter adapter = new NewsAdapter(getData()); rv.setAdapter(adapter); return view; } class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{ private List<News> mList; class ViewHolder extends RecyclerView.ViewHolder{ TextView title; public ViewHolder(@NonNull View view) { super(view); title = view.findViewById(R.id.item_text_view); } } public NewsAdapter(List<News> list){ mList = list; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false); ViewHolder holder = new ViewHolder(view); view.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { News news = mList.get(holder.getAdapterPosition()); FragmentManager manager = getFragmentManager(); RightFragment fragment = (RightFragment) manager.findFragmentById(R.id.right_fragment); fragment.refresh(news.getTitle(),news.getContent()); } }); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { News news = mList.get(position); holder.title.setText(news.getTitle()); } @Override public int getItemCount() { return mList.size(); } } private List<News> getData() { List<News> list = new ArrayList<>(); for (int i = 1; i <= 5; i++) { list.add(new News("Title" + i, "Content" + i)); } return list; } }该方法同新闻应用中的添加适配器的方式相同,在此不再赘述;
运行效果
•独立类方式
新建一个 NewsAdapter 类,添加如下代码;
NewsAdapter.java
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> { private List<News> mList; public class ViewHolder extends RecyclerView.ViewHolder { TextView title; public ViewHolder(@NonNull View view) { super(view); title = view.findViewById(R.id.item_text_view); } } public NewsAdapter(List<News> list) { mList = list; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); ViewHolder holder = new ViewHolder(view); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { News news = mList.get(holder.getAdapterPosition()); /** * 此处会报错 * 因为 NewsAdapter 并没有继承自 Fragment * 所以就无法使用 getFragmentManager() 方法获取到 FragmentManager */ // FragmentManager manager = getFragmentManager(); } }); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { News news = mList.get(position); holder.title.setText(news.getTitle()); } @Override public int getItemCount() { return mList.size(); } }仔细品读,你会发现,这和内部类中的 NewsAdapter 代码相差无几;
其实不然,在内部类 NewsAdapter 中,由于外部类 LeftFragment 继承自 Fragment;
所以,在 view.setOnClickListener() 的 onClick() 中就可以使用 FragmentManager manager = getFragmentManager(); ;
但是,将 NewsAdapter 摘出来作为一个独立的类,其并没有继承自 Fragment,所以,不能使用 getFragmentManager() 方法;
那我们是不是可以这样做,在点击 item 的时候,通过 onClick() 方法中的 News news = mList.get(holder.getAdapterPosition()); 方法,将被点击项的 title 和 content 传递到 LeftFragment 中;
然后,在 LeftFragment 中进行 Fragment 的一系列操作?
你别说,还真行,这就需要通过 Java 的回调机制来实现;
鄙人不才,之前学了一点回调的知识,特此献上🔗;
首先,我们在 NewsAdapter.java 中添加一个回调接口 CallBack,并在 NewsAdapter.java 中声明一个 CallBack;
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> { private List<News> mList; private CallBack callBack;//声明一个 CallBack public class ViewHolder extends RecyclerView.ViewHolder {...} public NewsAdapter(List<News> list) {...} @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {...} @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) {...} @Override public int getItemCount() {...} } //添加一个回调接口 CallBack interface CallBack{ void onClick(News news); }接下来就是定义 setCallBack() 方法了;
修改 NewsAdapter.java 中的代码;
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> { private List<News> mList; private CallBack callBack;//声明一个 CallBack public class ViewHolder extends RecyclerView.ViewHolder {...} public NewsAdapter(List<News> list) {...} //定义 setCallBack() 方法 public void setCallBack(CallBack callBack){ this.callBack = callBack; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {...} @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) {...} @Override public int getItemCount() {...} } //添加一个回调接口 CallBack interface CallBack{ void onClick(News news); }然后,就是通过 onClick() 方法传递被点击项 title 和 content 了;
修改 view.setOnClickListener() 中的 onClick() 方法;
@NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { ... view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { News news = mList.get(holder.getAdapterPosition()); /** * 此处会报错 * 因为 NewsAdapter 并没有继承自 Fragment * 所以就无法使用 getFragmentManager() 方法获取到 FragmentManager */ // FragmentManager manager = getFragmentManager(); /** * 把 news.getTitle() 和 news.getContent() 传递到 LeftFragment 中 * 这就需要使用回调方法 */ callBack.onClick(news); } }); return holder; }通过 callBack.onClick(news); 方法传递 title 和 content 信息;
接下来,就是在 LeftFragment.java 中通过回调接收 news 了;
修改 LeftFragment.java 中的代码;
public class LeftFragment extends Fragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.left_fragment, container, false); RecyclerView rv = view.findViewById(R.id.left_recycler_view); LinearLayoutManager manager = new LinearLayoutManager(null); rv.setLayoutManager(manager); NewsAdapter adapter = new NewsAdapter(getData()); CallBack callBack = new CallBack() { @Override public void onClick(News news) { FragmentManager manager1 = getFragmentManager(); RightFragment fragment = (RightFragment) manager1.findFragmentById(R.id.right_fragment); fragment.refresh(news.getTitle(),news.getContent()); } }; adapter.setCallBack(callBack); rv.setAdapter(adapter); return view; } private List<News> getData() { List<News> list = new ArrayList<>(); for (int i = 1; i <= 5; i++) { list.add(new News("Title" + i, "Content" + i)); } return list; } }到这,就大功告成了;
运行一下,你会发现,成功实现内部类的实现效果;
•声明
参考资料: