该文章主要记录,Rv中的多布局使用。
目录
1. 继承自RecyclerView.Adapter实现聊天多布局
1. 继承自RecyclerView.Adapter实现聊天多布局
实现该类多布局,主要是重写了getItemViewType,然后才是根据不同的数据类型去返回各自相对应的Item子布局。
而在onCreateViewHolder()中才是加载对应的itemType转换为对应的view即可。

/**
* @author crazyZhangxl on 2019/1/15.
* Describe: 多布局实现聊天通信 纯UI
*/
public class SessionAdapter extends RecyclerView.Adapter<OneViewHolder>{
private Context mContext;
private List<Message> mMessagesList;
private static final int SEND_TEXT = R.layout.item_text_send;
private static final int RECEIVE_TEXT = R.layout.item_text_receive;
private static final int UNDEFINE_MSG = R.layout.item_no_support_msg_type;
/**
* 构造函数----
* @param context
* @param messagesList
*/
public SessionAdapter(Context context, List<Message> messagesList) {
mContext = context;
mMessagesList = messagesList;
}
/**
* 创建ViewHolder
* @param viewGroup 父亲组件
* @param viewType 文本布局资源文件[对应getItemViewType返回的类型]
* @return
*/
@NonNull
@Override
public OneViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
return new OneViewHolder(LayoutInflater.from(mContext).inflate(viewType, viewGroup,false), mContext);
}
/**
* 绑定ViewHolder设置初始化
* @param viewHolder
* @param position
*/
@Override
public void onBindViewHolder(@NonNull OneViewHolder viewHolder, int position) {
// 设置时间
setTime(viewHolder,position);
setName(viewHolder,position);
setContent(viewHolder,position);
}
private void setName(OneViewHolder viewHolder, int position) {
TextView tvName = viewHolder.mConvertView.findViewById(R.id.tvName);
tvName.setText(mMessagesList.get(position).getSendName());
}
private void setContent(OneViewHolder viewHolder, int position) {
Message message = mMessagesList.get(position);
if (message instanceof TextMessage){
TextView tvMessage = viewHolder.mConvertView.findViewById(R.id.tvText);
TextMessage textMessage = (TextMessage) mMessagesList.get(position);
tvMessage.setText(textMessage.getTextInfo());
}
}
private void setTime(OneViewHolder viewHolder, int position) {
TextView tvTime = viewHolder.mConvertView.findViewById(R.id.tvTime);
Message message = mMessagesList.get(position);
long sendTime = message.getSendTime();
if (position > 0){
long preTime = mMessagesList.get(position - 1).getSendTime();
if (sendTime - preTime > 5*60*1000){
tvTime.setVisibility(View.VISIBLE);
tvTime.setText(TimeUtils.getInstance().longToTime(sendTime));
}else {
tvTime.setVisibility(View.GONE);
}
}else {
tvTime.setVisibility(View.VISIBLE);
tvTime.setText(TimeUtils.getInstance().longToTime(sendTime));
}
}
/**
* 对应各种消息类型[多布局的核心]
* @param position 对应的下标position
* @return
*/
@Override
public int getItemViewType(int position) {
int viewType = UNDEFINE_MSG;
Message message = mMessagesList.get(position);
boolean isSend = message.getDirection() == MessageDirection.SEND.integerValue;
switch (message.getMessageType()){
case TEXT:
viewType = isSend?SEND_TEXT:RECEIVE_TEXT;
break;
case IMAGE:
break;
default:
break;
}
return viewType;
}
@Override
public int getItemCount() {
if (mMessagesList == null){
return 0;
}
return mMessagesList.size();
}
}
/**
* @author crazyZhangxl on 2019/1/15.
* Describe: 基本的ViewHolder
*/
public class OneViewHolder extends RecyclerView.ViewHolder{
protected View mConvertView;
private Context mContext;
public OneViewHolder(@NonNull View itemView) {
super(itemView);
}
public OneViewHolder(@NonNull View itemView, Context context) {
super(itemView);
mConvertView = itemView;
mContext = context;
}
public View getConvertView() {
return mConvertView;
}
public void setConvertView(View convertView) {
mConvertView = convertView;
}
public Context getContext() {
return mContext;
}
public void setContext(Context context) {
mContext = context;
}
}
拓展内容
可以学习下优秀的人封装的ViewHolder,特点是对View的操作更加简洁了,而且封装了item的各种操作事件处理。还有一个知识点是 SparseArray<Views>的学习以及使用,优化的缓存方案。
public class LQRViewHolder extends RecyclerView.ViewHolder {
protected Context mContext;
protected View mConvertView;
protected SparseArray<View> mViews;
protected int mMyPosition;//LQRViewHolderForAbsListView专用(另一个自带getPosition方法)
protected OnItemClickListener mOnItemClickListener;
protected OnItemLongClickListener mOnItemLongClickListener;
protected OnItemTouchListener mOnItemTouchListener;
public LQRViewHolder(View itemView) {
super(itemView);
}
/**
* 根据id得到布局中的View(使用SparseArray保管,提高效率)
*/
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
* 得到当前item对应的View
*/
public View getConvertView() {
return mConvertView;
}
public int getMyPosition() {
return mMyPosition;
}
public void setMyPosition(int myPosition) {
mMyPosition = myPosition;
}
public OnItemClickListener getOnItemClickListener() {
return mOnItemClickListener;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
public OnItemLongClickListener getOnItemLongClickListener() {
return mOnItemLongClickListener;
}
public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
mOnItemLongClickListener = onItemLongClickListener;
}
public OnItemTouchListener getOnItemTouchListener() {
return mOnItemTouchListener;
}
public void setOnItemTouchListener(OnItemTouchListener onItemTouchListener) {
mOnItemTouchListener = onItemTouchListener;
}
/*================== 一切有可能的操作控件的方法 begin ==================*/
/**
* 设置TextView文字,并返回this
*/
public LQRViewHolder setText(int viewId, String text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
/**
* 设置TextView的文字颜色,并返回this
*/
public LQRViewHolder setTextColor(int viewId, int colorId) {
TextView tv = getView(viewId);
tv.setTextColor(mContext.getResources().getColor(colorId));
return this;
}
/**
* 设置ImageView的图片,并返回this
*/
public LQRViewHolder setImageResource(int viewId, int resId) {
ImageView iv = getView(viewId);
iv.setImageResource(resId);
return this;
}
/**
* 设置ImageView的图片,并返回this
*/
public LQRViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
ImageView iv = getView(viewId);
iv.setImageBitmap(bitmap);
return this;
}
/**
* 设置ImageView的图片,并返回this
*/
public LQRViewHolder setImageFileResource(int viewId, String path) {
ImageView iv = getView(viewId);
Bitmap bitmap = BitmapFactory.decodeFile(path);
iv.setImageBitmap(bitmap);
return this;
}
/**
* 设置背景颜色,并返回this
*/
public LQRViewHolder setBackgroundColor(int viewId, int colorId) {
View view = getView(viewId);
view.setBackgroundColor(mContext.getResources().getColor(colorId));
return this;
}
/**
* 设置背景资源,并返回this
*/
public LQRViewHolder setBackgrounResource(int viewId, int resId) {
View view = getView(viewId);
view.setBackgroundResource(resId);
return this;
}
/**
* 设置显隐,并返回this
*/
public LQRViewHolder setViewVisibility(int viewId, int visibility) {
View view = getView(viewId);
view.setVisibility(visibility);
return this;
}
/**
* 设置是否可用,并返回this
*/
public LQRViewHolder setEnabled(int viewId, boolean enabled) {
View view = getView(viewId);
view.setEnabled(enabled);
return this;
}
/**
* 设置是否可获取焦点,并返回this
*/
public LQRViewHolder setFocusable(int viewId, boolean focusable) {
View view = getView(viewId);
view.setFocusable(focusable);
return this;
}
/*================== 一切有可能操作控件的方法 end ==================*/
}
public class LQRViewHolderForRecyclerView extends LQRViewHolder {
public LQRViewHolderForRecyclerView(Context context, View itemView) {
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<>();
mConvertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(LQRViewHolderForRecyclerView.this, null, v, getPosition());
}
}
});
mConvertView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
return mOnItemLongClickListener.onItemLongClick(LQRViewHolderForRecyclerView.this, null, v, getPosition());
}
return false;
}
});
mConvertView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (mOnItemTouchListener != null) {
return mOnItemTouchListener.onItemTouch(LQRViewHolderForRecyclerView.this, v, event, getPosition());
}
return false;
}
});
}
}
SparseArray的学习及使用
用途:当数据结构中key的类型是int时可用此结构来替代hashmap来进行优化
特点:查找操作时使用的是二分法查找,时间复杂度时O(N);存储采用的时数组,占用的内存空间小
HashMap是数组和链表的结合体,被称为链表散列.
SparseArray是单纯数组的结合.被称为稀疏数组,对数据保存的时候,不会有额外的开销
获取以及设置的使用方式
/**
* 根据id得到布局中的View(使用SparseArray保管,提高效率)
*/
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
.9图的简单制作,右边3图效果依次为: 上下拉伸/左右拉伸/上下左右都拉伸的效果展示

2. 使用BRVAH Adapter帮助类实现多布局

private void initData() {
mMulCityBeanList.add(new MulCityBean("101","南京","12","晴"));
mMulCityBeanList.add(new MulCityBean("101","北京","15","阴"));
mMulCityBeanList.add(new MulCityBean("101","天津","18","小雨"));
mMulCityBeanList.add(new MulCityBean());
}
private void initAdapter() {
mCityRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
mMulCityAdapter = new MulCityAdapter(mMulCityBeanList,this);
// 设置adapter
mCityRecyclerView.setAdapter(mMulCityAdapter);
}
核心数据bean,必须得实现MultiItemEntity
public class MulCityBean implements MultiItemEntity{
/**
* 具体城市信息
*/
public static final int CITY = 1;
/**
* 添加的按钮
*/
public static final int ADD_SYMBOL = 2;
/**
* 用于返回给itemType的数值
* 在构造方法中可进行初始化
*/
private int itemType;
// 对应的每个Item的具体数值
public String cityId;
// 就是江宁
public String cityName;
public String temp;
public String weatherStatus;
/**
* 是否显示删除的按钮
*/
private boolean isShowDelete;
/**
* 是否显示本地的城市
*/
private boolean isNowCity;
private boolean isChecked;
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
public boolean isShowDelete() {
return isShowDelete;
}
public void setShowDelete(boolean showDelete) {
isShowDelete = showDelete;
}
public boolean isNowCity() {
return isNowCity;
}
public void setNowCity(boolean nowCity) {
isNowCity = nowCity;
}
/**
* 无参数构造方法那么默认该bean就是添加按钮
*/
public MulCityBean() {
itemType = ADD_SYMBOL;
}
/**
* 多惨构造方法即是城市的信息
* @param cityId
* @param cityName
* @param temp
* @param weatherStatus
*/
public MulCityBean(String cityId, String cityName, String temp, String weatherStatus) {
this.cityId = cityId;
this.cityName = cityName;
this.temp = temp;
this.weatherStatus = weatherStatus;
itemType = CITY;
}
@Override
public int getItemType() {
return itemType;
}
}
adapter实现BaseMultiItemQuickAdapter
public class MulCityAdapter extends BaseMultiItemQuickAdapter<MulCityBean,BaseViewHolder>{
private Context context;
public static final int[] BLUR_IMAGE = {
R.mipmap.ic_city_blur0,
R.mipmap.ic_city_blur1,
R.mipmap.ic_city_blur2,
R.mipmap.ic_city_blur3,
R.mipmap.ic_city_blur4,
R.mipmap.ic_city_blur5};
private Drawable mDrawableLocation;
private ImageView mImageView;
private TextView mTv_status;
private ImageView mView_del;
private View mView_hover;
private TextView mText_city_name;
private ImageView mMChecked;
/**
* Same as QuickAdapter#QuickAdapter(Context,int) but with
* some initialization data.
*
* @param data A new list is created out of this one to avoid mutable list
*/
public MulCityAdapter(List<MulCityBean> data,Context context) {
super(data);
// 对应类型绑定不同的布局
addItemType(MulCityBean.CITY, R.layout.item_city_followed_city);
addItemType(MulCityBean.ADD_SYMBOL, R.layout.item_city_add_city);
this.context = context;
initDrawable();
}
/**
* 初始化Drawable-----
*/
private void initDrawable() {
mDrawableLocation = ContextCompat.getDrawable(context,R.mipmap.ic_location);
mDrawableLocation.setBounds(0,0,dpToPx(context,14),dpToPx(context,14));
}
@Override
protected void convert(BaseViewHolder helper, MulCityBean item) {
switch (helper.getItemViewType()){
case MulCityBean.CITY:
if (item == null){
return;
}
mImageView = helper.getView(R.id.image);
mImageView.setImageResource(BLUR_IMAGE[helper.getAdapterPosition()% BLUR_IMAGE.length]);
helper.setText(R.id.city_temp,item.temp);
mText_city_name = helper.getView(R.id.city_name);
mText_city_name.setText(item.cityName);
mTv_status = helper.getView(R.id.city_status);
mTv_status.setText(item.weatherStatus);
mView_del = helper.getView(R.id.delete);
helper.addOnClickListener(R.id.delete);
// 设置删除按钮的显示
mView_del.setVisibility(item.isShowDelete()?View.VISIBLE:View.GONE);
// 设备阴影的显示
mView_hover = helper.getView(R.id.hover);
mView_hover.setVisibility(item.isShowDelete()?View.VISIBLE : View.GONE);
// 是否是当前
mMChecked = helper.getView(R.id.checked);
mMChecked.setVisibility(item.isChecked()?View.VISIBLE:View.GONE);
break;
case MulCityBean.ADD_SYMBOL:
break;
default:
break;
}
}
private int dpToPx(Context context,int dp){
if (context == null){
return 0;
}
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
return (int) (displayMetrics.density*(dp+0.5f));
}
}
3. 通过基本的隐藏显示实现仿微信朋友列表
不需要多布局,设置要素隐藏显示即可实现特定的效果。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:background="#fff"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvIndex"
android:layout_width="match_parent"
android:layout_height="23dp"
android:background="#E5E5E5"
android:gravity="center_vertical"
android:paddingLeft="12.5dp"
android:text="A"
android:textColor="#989898"
android:textSize="12.5dp"
android:visibility="gone"/>
<LinearLayout
android:paddingLeft="12.5dp"
android:paddingRight="12.5dp"
android:paddingTop="7.5dp"
android:paddingBottom="7.5dp"
android:layout_width="match_parent"
android:layout_height="50dp">
<ImageView
android:src="@mipmap/ic_launcher"
android:layout_width="30dp"
android:layout_height="30dp" />
<RelativeLayout
android:layout_marginLeft="15dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvUserName"
android:layout_centerVertical="true"
android:text="我是谁"
android:textColor="#000"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<View
android:id="@+id/line"
android:background="#D2D2D2"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="0.5dp"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
@Override
protected void convert(BaseViewHolder helper, FriendBean item) {
helper.setText(R.id.tvUserName,item.getName());
int position = helper.getAdapterPosition();
String nowSpelling = item.getNameSpelling();
TextView tvIndex = helper.getView(R.id.tvIndex);
View viewLine = helper.getView(R.id.line);
String index = "";
if (position == 0){
index = nowSpelling;
}else {
String beforeSpelling = mFriendBeanList.get(position - 1).getNameSpelling();
if (!beforeSpelling.equals(nowSpelling)){
index = nowSpelling;
}
}
int nextIndex = position + 1;
if (nextIndex < mFriendBeanList.size() - 1) {
//得到下一个字母
String nextLetter = mFriendBeanList.get(nextIndex).getNameSpelling();
//如果和下一个字母的首字母不同则隐藏下划线
if (!nextLetter.equalsIgnoreCase(nowSpelling)) {
viewLine.setVisibility(View.INVISIBLE);
} else {
viewLine.setVisibility(View.VISIBLE);
}
} else {
viewLine.setVisibility(View.VISIBLE);
}
if (position == mFriendBeanList.size() - 1) {
viewLine.setVisibility(View.INVISIBLE);
}
if (TextUtils.isEmpty(index)){
tvIndex.setVisibility(View.GONE);
}else {
tvIndex.setText(index);
tvIndex.setVisibility(View.VISIBLE);
}
}
4. Grid复杂布局实现
如上三点我们针对的都是单条目LinearLayoutManager,这一条我们针对的是GridLayoutManager中分3行 1行 2行显示的情况。主要实现的是 onAttachedToRecyclerView 里面有设置一个item占几行的属性。
// size / x = 目标
// 假如目标 = 3 / 那么 x = 2
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager){
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int i) {
int itemViewType = getItemViewType(i);
if (LEFT == itemViewType){
return 2;
}else if (CENTER == itemViewType){
return 6;
}else if (RIGHT == itemViewType){
return 3;
}
return 6;
}
});
}
}

拓展效果:

参考资料:
RecyclerView的那些开源炫酷的LayoutManager
本文详细介绍了在Android中使用RecyclerView实现多布局的方法,包括继承RecyclerView.Adapter、使用BRVAH Adapter辅助类,以及通过基本的隐藏显示来创建仿微信朋友列表。文中还提到了SparseArray的优化作用,.9图的制作教程,以及Grid复杂布局的实现。并提供了相关开源库和项目的参考链接。
513

被折叠的 条评论
为什么被折叠?



