移动开发作业二: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'
要实现recycleview
和cardview
要分别导入这两个依赖。此篇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
),常常需要创建ViewHolder
,ViewHolder
就是一个持有者的类,他里面一般没有方法,只有属性,作用就是一个临时的储存器,把你getView
方法中每次返回的View
存起来,可以下次再用。这样做的好处就是不必每次都到布局文件中去拿到你的View
,提高了效率。
实现步骤:
- 创建Adapter:创建一个继承
RecyclerView.Adapter<VH>
**的Adapter类(VH是ViewHolder的类名) - 创建ViewHoler:在Adapter中创建一个继承
RecyclerView.ViewHolder
**的静态内部类,记为VH。 - 在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()
:初始化数据
}