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

  到这,就大功告成了;

  运行一下,你会发现,成功实现内部类的实现效果;

•声明

  参考资料:

  【adapter调用fragment中的方法

  【在适配器Adapter中回调他的点击事件到activity或者fragment当中

 

posted @ 2021-03-09 19:11  MElephant  阅读(2702)  评论(0编辑  收藏  举报