•前言
在學習《第一行代碼》,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; } }到這,就大功告成了;
運行一下,你會發現,成功實現內部類的實現效果;
•聲明
參考資料: