场景
RecyclerView
RecyclerView是Android一个更强大的控件,其不仅可以实现和ListView同样的效果,还有优化了ListView中的各种不足。其可以实现数据纵向滚动,也可以实现横向滚动(ListView做不到横向滚动)。
因为RecyclerView属于新增的控件,Android将RecyclerView定义在support库里。若要使用RecyclerView,第一步是要在build.gradle中添加对应的依赖库。
Adapter适配器
顾名思义,就是把一些数据给弄得适当,适合以便于在View上显示。可以看作是界面数据绑定的一种理解。它所操纵的数据一般都是一些比较复杂的数据,如数组,链表,数据库,集合等。适配器就像显示器,把复杂的东西按人可以接受的方式来展现。
数据源是各种各样的,而ListView所展示数据的格式则是有一定的要求的。数据适配器正是建立了数据源与ListView之间的适配关系,将数据源转换为ListView能够显示的数据格式,从而将数据的来源与数据的显示进行解耦,降低程序的耦合性。这也体现了Android的适配器模式的使用
RecycleView是对ListView以及GridView的升级,在使用的时候同源更新需要使用Adapter适配器。但是RecycleView使用的适配器并不是之前的BaseAdapter了。RecycleView使用的适配器需要继承RecyclerView.Adapter<VH extends RecycleView.ViewHolder>
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
实现
创建RecyclerView
下面以一个示例入手看看怎样使用一个Adapter给RecyclerView进行赋值。
首先新建一个项目,然后修改activity_main.xml的页面布局如下
最终要实现的效果是点击下面的imageView实现录音,并将录音文件的路径显示在上面的RecyclerView中。
关于录音的实现不做讲解,主要介绍录音完成后使用Adapter给上面的RecyclerView添加数据源,这里以显示录音文件的路径为例。
布局页面activity_main.xml的代码如下
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintBottom_toTopOf="@+id/layout_bottom" app:layout_constraintTop_toTopOf="parent" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="101dp" android:id="@+id/layout_bottom" android:background="#F7F7F7" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"> <ImageView android:layout_width="66dp" android:layout_height="66dp" android:id="@+id/img_voice" android:background="@mipmap/ic_launcher" android:layout_centerInParent="true" /> </RelativeLayout> </androidx.constraintlayout.widget.ConstraintLayout>
注意上面添加RecyclerView的全路径以及添加id属性。
添加依赖
然后添加依赖。打开项目下的build.gradle,添加依赖
implementation 'com.android.support:recyclerview-v7:30.0.0'
注意这里的版本要与你上面的compileSdkVersion对应
创建item项的布局文件
然后为RecyclerView的item创建统一的布局文件,在layout下-New-XML-Layout XML File,这里命名为layout_chat_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="50dp" android:layout_gravity="center" android:orientation="horizontal"> <TextView android:id="@+id/chat_item" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" /> </LinearLayout>
你可以在这里任意设计视图,它将会应用在你RecyclerView中每个项上。
这里为了演示,值简单添加了一个TextView,记得给这个TextView设置ID
创建Adapter
我们在项目下新建一个包adapter,并在包下新建类ChatAdapter。
这个CharAdapter需要继承RecycleView.Adapter<>,并且泛型里面的类型是自定义的继承自RecycleView.ViewHolder的类。
关于ViewHolder
ViewHolder的主要任务:容纳View视图。Adapter连接了后端数据和前端显示,viewHolder的作用就是提供前端的视图文件。
这个自定义ViewHolder可以是外部类,但是为了方便,将其作为ChatAdaprer的内部类
需要在自定义的ViewHolder中绑定视图文件,就好像在MainActivity中绑定activity_main.xml布局文件一样
这里需要绑定我们在上面新建的每一项的布局视图,即layout_chat_item.xml,并且获取到每一项布局视图的控件。
所以此时的ChatAdapter的代码如下
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> { class ChatViewHolder extends RecyclerView.ViewHolder{ private TextView mText; public ChatViewHolder(View itemView) { super(itemView); mText = (TextView) itemView.findViewById(R.id.chat_item); } } }
注意此时还没有重写指定的三个方法。
此时我们先创建一个数据实体类ChatBean
package com.badao.audiodemo.bean; import androidx.annotation.NonNull; import java.util.List; public class ChatBean { private String msg; private int code; @NonNull private String id = ""; private List<ChatItem> data; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public List<ChatItem> getData() { return data; } public void setData(List<ChatItem> data) { this.data = data; } @NonNull public String getId() { return id; } public void setId(@NonNull String id) { this.id = id; } public static class ChatItem { private int id; private String msgNum; private String content; //语音消息服务器地址 private String remoteContent; private String sender; private String receiver; private String type; private boolean canReceived; private String sendTime; private String receivedTime; //语音时长 private int voiceDuration; private boolean isRead; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMsgNum() { return msgNum; } public void setMsgNum(String msgNum) { this.msgNum = msgNum; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } public String getType() { return type; } public void setType(String type) { this.type = type; } public boolean isCanReceived() { return canReceived; } public void setCanReceived(boolean canReceived) { this.canReceived = canReceived; } public String getSendTime() { return sendTime; } public void setSendTime(String sendTime) { this.sendTime = sendTime; } public String getReceivedTime() { return receivedTime; } public void setReceivedTime(String receivedTime) { this.receivedTime = receivedTime; } public int getVoiceDuration() { return voiceDuration; } public void setVoiceDuration(int voiceDuration) { this.voiceDuration = voiceDuration; } public String getRemoteContent() { return remoteContent; } public void setRemoteContent(String remoteContent) { this.remoteContent = remoteContent; } public boolean isRead() { return isRead; } public void setRead(boolean read) { isRead = read; } } }
实体类的内容可以自己定义,这里是包含了语音消息实体的相关属性。
然后再回到ChatAdapter,此时重写其三个方法,然后新建上面实体的list作为数据源,并编写其set方法
private List<ChatBean.ChatItem> mEntityList = new ArrayList<>(); public void setmEntityList(List<ChatBean.ChatItem> mEntityList) { this.mEntityList = mEntityList; //一定要记得加,否则视图不会更新!!! notifyDataSetChanged(); }
重写onCreateViewHolder方法
返回我们的内部类MyViewHolder ,此处为将我们的item布局文件和adapter绑定。
@NonNull @Override public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_chat_item, parent, false); return new ChatViewHolder(view); }
重写onBindViewHolder方法
对每一项的TextView进行赋值,取出实体的某一属性进行显示,position为下标。
@Override public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) { holder.mText.setText(mEntityList.get(position).getContent().toString()); }
重写getItemCount方法
获得实体list的长度,即显示项的个数
@Override public int getItemCount() { return mEntityList == null?0:mEntityList.size(); }
此时完整的ChatAdapter的代码为:
package com.badao.audiodemo.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.badao.audiodemo.R; import com.badao.audiodemo.bean.ChatBean; import java.util.ArrayList; import java.util.List; public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> { private List<ChatBean.ChatItem> mEntityList = new ArrayList<>(); public void setmEntityList(List<ChatBean.ChatItem> mEntityList) { this.mEntityList = mEntityList; //一定要记得加,否则视图不会更新!!! notifyDataSetChanged(); } @NonNull @Override public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_chat_item, parent, false); return new ChatViewHolder(view); } @Override public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) { holder.mText.setText(mEntityList.get(position).getContent().toString()); } @Override public int getItemCount() { return mEntityList == null?0:mEntityList.size(); } class ChatViewHolder extends RecyclerView.ViewHolder{ private TextView mText; public ChatViewHolder(View itemView) { super(itemView); mText = (TextView) itemView.findViewById(R.id.chat_item); } } }
使用Adapter给RecycleView赋值
在MainActivity中先声明所需对象
public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private List<ChatBean.ChatItem> chatItemList; private ChatAdapter chatAdapter;
然后在onCreate方法中初始化
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initRecyclerView(); } /** * 初始化RecyclerView */ private void initRecyclerView(){ mRecyclerView = findViewById(R.id.recycler); chatItemList = new ArrayList<>(); chatAdapter = new ChatAdapter(); // 定义一个线性布局管理器 LinearLayoutManager manager = new LinearLayoutManager(this); // 设置布局管理器 mRecyclerView.setLayoutManager(manager); // 设置adapter mRecyclerView.setAdapter(chatAdapter); }
然后就是在需要对RecycleView进行赋值的地方,这里为例是在录音结束后的回调方法中,也是在MainActivity中
@Override public void onFinish(Uri uri, int i) { File file = new File(uri.getPath()); ChatBean.ChatItem chatItem = new ChatBean.ChatItem(); chatItem.setId((int) System.currentTimeMillis()); chatItem.setSendTime(new Date().toString()); chatItem.setContent(file.getAbsolutePath()); chatItemList.add(chatItem); chatAdapter.setmEntityList(chatItemList); }
直接将实体类对象添加到list中并调用chatAdapter的set方法即可,就会自动在recycleView中更新数据