RecyclerView的实现

本文详细介绍了如何在移动开发中使用RecyclerView展示聊天记录,包括添加依赖、布局设计、数据绑定、长按删除功能和自定义组件。从引入RecyclerView到创建Adapter,再到实现长按菜单,一步步带你完成一个完整的聊天界面开发。
摘要由CSDN通过智能技术生成

移动开发作业二:RecyclerView的实现

1. 实验内容及展示

在原有作业1上选择一个tab添加recyclerview显示。

演示:

在这里插入图片描述

2. 使用RecycleView实现Layout设计

2.1 导入依赖

build.gradle中导入相关依赖

implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'

要实现recycleviewcardview要分别导入这两个依赖。此篇blog暂只用到recycleview

2.2 将RecycleView放入Fragment

在移动开发作业一中,我们紧紧将一个普通的TextView放入了Fragemnt中,现在我们将其替换成RecyclerView。下面以微信栏为例子

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".WechatFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rv_wechat"/>

</FrameLayout>

2.3 设计RecycleView中每一个单元的布局

创建一个新的布局(LinearLayout)来实现单一单元的布局效果。相关的设计方法可看移动开发作业一类微信UI设计

代码略

效果如下:

自定义组件

系统自带ImageView是无法设置图片圆角的,如果想实现自定义圆角图片,便要重写这个组件。可参考这个博客超简单的自定义ImageView,支持圆角和直角

2.4 构造数据类javaBean

构建用来展示聊天框的实体类,显然要用四个变量来分别表示头像,名字,消息内容,消息时间

public class WechatContent {
    private Integer weChatAvaterId;
    private String weChatName;
    private String weChatContent;
    private String weChatTime;

    public WechatContent(Integer weChatAvaterId, String weChatName, String weChatContent, String weChatTime) {
        this.weChatAvaterId = weChatAvaterId;
        this.weChatName = weChatName;
        this.weChatContent = weChatContent;
        this.weChatTime = weChatTime;
    }

    public Integer getWeChatAvaterId() {
        return weChatAvaterId;
    }

    public void setWeChatAvaterId(Integer weChatAvaterId) {
        this.weChatAvaterId = weChatAvaterId;
    }

    public String getWeChatName() {
        return weChatName;
    }

    public void setWeChatName(String weChatName) {
        this.weChatName = weChatName;
    }

    public String getWeChatContent() {
        return weChatContent;
    }

    public void setWeChatContent(String weChatContent) {
        this.weChatContent = weChatContent;
    }

    public String getWeChatTime() {
        return weChatTime;
    }

    public void setWeChatTime(String weChatTime) {
        this.weChatTime = weChatTime;
    }
}

2.5 构造Adapter

Adapter概念:Adapter是链接后端和前端显示的适配器接口,书数据和UI(view)之间一个重要的枢纽。

在这里插入图片描述

当我们编写Adapter时需要继承相关控件的Adapter(此处继承RecyclerView.Adapter),常常需要创建ViewHolderViewHolder 就是一个持有者的类,他里面一般没有方法,只有属性,作用就是一个临时的储存器,把你getView方法中每次返回的View存起来,可以下次再用。这样做的好处就是不必每次都到布局文件中去拿到你的View,提高了效率。

实现步骤:

  1. 创建Adapter:创建一个继承RecyclerView.Adapter<VH>**的Adapter类(VH是ViewHolder的类名)
  2. 创建ViewHoler:在Adapter中创建一个继承RecyclerView.ViewHolder**的静态内部类,记为VH。
  3. 在Adapter中实现3个方法

代码如下:

public class WechatRVAdapter extends RecyclerView.Adapter<WechatRVAdapter.WeChatViewHolder> {

    private Context context;   //上下文
    private List<WechatContent> wechatData;   //展示聊天列表的list数组

    public WechatRVAdapter(Context context, List<WechatContent> wechatData) {
        this.context = context;
        this.wechatData = wechatData;
    }

    @NonNull
    @Override
    public WeChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(this.context).inflate(R.layout.wechat_content, parent, false);
        return new WeChatViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull WeChatViewHolder holder, int position) {
        holder.wechatAvaterView.setImageResource(wechatData.get(position).getWeChatAvaterId());
        holder.wechatNameView.setText(wechatData.get(position).getWeChatName());
        holder.wechatContentView.setText(wechatData.get(position).getWeChatContent());
        holder.wechatTimeView.setText(wechatData.get(position).getWeChatTime());
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context,"你点击的是:" + wechatData.get(position).getWeChatName(),Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return wechatData == null ? 0:wechatData.size();
    }

    //根据位置删除wechatDate里的数据
    public void onRemoveItem(int position) {
        if (position < 0 || position > getItemCount()) {
            return;
        }

        wechatData.remove(position);
        notifyItemRemoved(position);

        if (position != wechatData.size()){
            notifyItemRangeChanged(position,wechatData.size() - position);
        }
    }


    static class WeChatViewHolder extends RecyclerView.ViewHolder{
        TextView wechatNameView,wechatContentView,wechatTimeView;
        ImageView wechatAvaterView;

        public WeChatViewHolder(@NonNull View itemView) {
            super(itemView);
            wechatAvaterView = itemView.findViewById(R.id.iv_avatar);
            wechatNameView = itemView.findViewById(R.id.tv_wechat_name);
            wechatContentView = itemView.findViewById(R.id.tv_wechat_content);
            wechatTimeView = itemView.findViewById(R.id.tv_we_chat_time);
        }
    }
}

相关方法解释:

  • onCreateViewHolder():初始化ViewHolder。每当RecyclerView需要创建新的ViewHolder时就会执行此方法。
  • onBindViewHolder():将数据绑定到ViewHolder上。
  • getItemCount():获取list的大小
  • onRemoveItem():自己写的方法,用来删除list里的数据,当要删除聊天列表时会用到

2.6 实现长按弹出菜单并删除聊天框

第一步,在WechatRVAdapter中写删除Item的方法onRemoveItem(),代码在2.5中

第二步,在res创建menu文件夹并创建menu_item.xml文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/removeItem"
        android:title="删除"
        app:showAsAction="never"
     ></item>
</menu>

第三步,为recyclerview添加item点击监听。recyclerview没有自己的点击监听,只能自己写代码实现。在Adapter的文件中实现这个功能。(思路为设置item点击和长按监听器的接口供外部使用,在类中设置监听的方法并在ViewBind里进行绑定)。相关代码如下,完整代码见gitee

public interface OnItemOnClickListener{
    void onItemOnClick(View view,int pos);
    void onItemLongOnClick(View view ,int pos);
}
private OnItemOnClickListener mOnItemOnClickListener;
public void setOnItemOnClickListener(OnItemOnClickListener listener){
    this.mOnItemOnClickListener = listener;
}

@Override
public void onBindViewHolder(@NonNull WeChatViewHolder holder, int position) {
 holder.wechatAvaterView.setImageResource(wechatData.get(position).getWeChatAvaterId());
    holder.wechatNameView.setText(wechatData.get(position).getWeChatName());
    holder.wechatContentView.setText(wechatData.get(position).getWeChatContent());
    holder.wechatTimeView.setText(wechatData.get(position).getWeChatTime());
    if (mOnItemOnClickListener!=null){
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mOnItemOnClickListener.onItemOnClick(holder.itemView,position);
                Toast.makeText(context,"你点击的是:" + wechatData.get(position).getWeChatName(),Toast.LENGTH_SHORT).show();
            }
        });

        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                mOnItemOnClickListener.onItemLongOnClick(holder.itemView,position);
                return false;
            }
        });
    }
}

第四步,在WechatFragment文件的initView()中加入让我们这个自定义的点击事件

weChatRVAdapter.setOnItemOnClickListener(new WechatRVAdapter.OnItemOnClickListener() {
    @Override
    public void onItemOnClick(View view, int pos) {
        Toast.makeText(context, "点击"+pos, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onItemLongOnClick(View view, int pos) {
        showPopMenu(view,pos);   //要用到弹出菜单的方法
    }
});

//弹出菜单的方法
public void showPopMenu(View view,final int pos){
    PopupMenu popupMenu = new PopupMenu(context,view);
    popupMenu.getMenuInflater().inflate(R.menu.menu_item,popupMenu.getMenu());
    popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            weChatRVAdapter.onRemoveItem(pos);
            return false;
        }
    });
    popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
        @Override
        public void onDismiss(PopupMenu menu) {
            Toast.makeText(context.getApplicationContext(), "关闭PopupMenu", Toast.LENGTH_SHORT).show();
        }
    });
    popupMenu.show();
}

2.7 Fragment对应类实现

将之前为recyclerview所准备的数据,adapter等加入到Fragemnt

public class WechatFragment extends Fragment {

    private List<WechatContent> weChatData = new ArrayList<>();
    private Context context;
    private RecyclerView recyclerView;
    private WechatRVAdapter weChatRVAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_wechat, container, false);
        recyclerView = view.findViewById(R.id.rv_wechat);
        initData();
        initView();
        return view;
    }
    private void initView(){
        context = this.getActivity();
        weChatRVAdapter = new WechatRVAdapter(context,weChatData);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        weChatRVAdapter.setOnItemOnClickListener(new WechatRVAdapter.OnItemOnClickListener() {
            @Override
            public void onItemOnClick(View view, int pos) {
                Toast.makeText(context, "点击"+pos, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onItemLongOnClick(View view, int pos) {
                showPopMenu(view,pos);
            }
        });

        recyclerView.setAdapter(weChatRVAdapter);
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setHasFixedSize(true);
        recyclerView.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
    }

    private void initData() {
        List<String> nameList = new ArrayList<>();
        nameList.add("打工");
        nameList.add("理解");
        nameList.add("优势");
        nameList.add("压迫感");
        nameList.add("话语权");
        nameList.add("起夜急的李姐");
        nameList.add("我那么大优势");
        nameList.add("有状态就是硬气");
        for (int i = 0; i < nameList.size(); i++) {
            WechatContent weChatContent = new WechatContent();
            weChatContent.setWeChatAvaterId(R.drawable.jie_niao);
            weChatContent.setWeChatName(nameList.get(i));
            weChatContent.setWeChatContent(nameList.get(i) + "与你聊天");
            weChatContent.setWeChatTime("中午12:00");
            weChatData.add(weChatContent);
        }
    }

    public void showPopMenu(View view,final int pos){
        PopupMenu popupMenu = new PopupMenu(context,view);
        popupMenu.getMenuInflater().inflate(R.menu.menu_item,popupMenu.getMenu());
        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                weChatRVAdapter.onRemoveItem(pos);
                return false;
            }
        });
        popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
            @Override
            public void onDismiss(PopupMenu menu) {
                Toast.makeText(context.getApplicationContext(), "关闭PopupMenu", Toast.LENGTH_SHORT).show();
            }
        });
        popupMenu.show();
    }
}

属性解释:

  • private List<WechatContent> weChatData = new ArrayList<>(); 展示内容的数据集
  • private Context context; 声明上下文
  • private RecyclerView recyclerView; 声明recyclerView,在后面onCreateView()函数中通过view.findViewById()进行赋值
  • private WechatRVAdapter weChatRVAdapter;

方法解释:

  • initView():初始化View,为RecyclerView配置适配器,管理器,为Adapter设置自定义的监听器
  • initData():初始化数据

}

gitee地址

https://gitee.com/xiaolong2612/android_MyWeChat

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值