前言: 自從上次忘記了某事之后圆仔,我就決定了在網(wǎng)上記錄下自己學(xué)習(xí)的一些筆記(有錯(cuò)誤請(qǐng)?jiān)u論告訴我,謝謝
今天突然想起來之前寫過的一個(gè)項(xiàng)目产徊,首頁用的是ScrollView的嵌套,其中里面還嵌套了RecyclerView恬汁,在滑動(dòng)到RecyclerView那一部分的時(shí)候有明顯的卡頓,覺得應(yīng)該把整體當(dāng)作一個(gè)RecyclerView不應(yīng)該用嵌套
原項(xiàng)目效果圖:
開始自定義一個(gè)可以添加Header和Footer的RecyclerView辜伟,由于RecyclerView沒有自己的addHeaderView方法氓侧,所以參考了ListView的代碼,整體的代碼如下:
CustomRecyclerView 繼承自RecyclerView
public class CustomRecyclerView extends RecyclerView {
private ArrayList<ViewConfig> mHeadCouListInfo; //保存頭部的view
private ArrayList<ViewConfig> mFootCouListInfo; //保存尾部的view
private int headCount; //記錄head的個(gè)數(shù)
private int footCount; //記錄foot的個(gè)數(shù)
private Adapter mAdapter; //adapter导狡,可能是customadapter约巷, 可能是自定義adapter
private Context mContext;
public CustomRecyclerView(@NonNull Context context) {
this(context, null);
}
public CustomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mHeadCouListInfo = new ArrayList<>();
mFootCouListInfo = new ArrayList<>();
mContext = context;
}
/**
* 添加HeadView的方法
*
* @param view
*/
public void addHeadView(View view) {
this.addHeadView(view, 0);
}
public void addHeadView(View view, int index) {
headCount++;
setHeadViewConfig(view, ViewConfig.HEADVIEW, headCount, 100000);
if (mAdapter != null) {
if (!(mAdapter instanceof CustomAdapter)) {
wrapHeadAdapter();
}
}
}
public void addFootView(View view) {
this.addFootView(view, 0);
}
public void addFootView(View view, int index) {
footCount++;
setFootViewConfig(view, ViewConfig.FOOTVIEW, footCount, 100000);
if (mAdapter != null) {
if (!(mAdapter instanceof CustomAdapter)) {
wrapHeadAdapter();
}
}
}
/**
* 將adapter構(gòu)建為customadapter用來填充頭部尾部布局
*/
private void wrapHeadAdapter() {
mAdapter = new CustomAdapter(mHeadCouListInfo, mFootCouListInfo, mAdapter, mContext);
}
@Override
public void setAdapter(@Nullable Adapter adapter) {
if (mHeadCouListInfo.size() > 0 || mFootCouListInfo.size() > 0) {
mAdapter = new CustomAdapter(mHeadCouListInfo, mFootCouListInfo, adapter, mContext);
} else {
mAdapter = adapter;
}
super.setAdapter(mAdapter);
}
/**
* 配置頭部view的信息
*
* @param view
* @param type
* @param count
* @param headCount
*/
private void setHeadViewConfig(View view, String type, int count, int headCount) {
ViewConfig viewConfig = new ViewConfig();
viewConfig.setTag(view.getClass() + type + count);
viewConfig.setType(headCount);
viewConfig.setView(R.layout.item_tv);
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
parent.removeView(view);
}
viewConfig.setContentView(view);
mHeadCouListInfo.add(viewConfig);
}
/**
* 配置尾部view的信息
*
* @param view
* @param type
* @param count
* @param headCount
*/
private void setFootViewConfig(View view, String type, int count, int headCount) {
ViewConfig viewConfig = new ViewConfig();
viewConfig.setTag(view.getClass() + type + count);
viewConfig.setType(headCount);
viewConfig.setView(R.layout.item_tv);
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
parent.removeView(view);
}
viewConfig.setContentView(view);
mFootCouListInfo.add(viewConfig);
}
public CustomAdapter getHeadAndFootAdapter() {
return (CustomAdapter) mAdapter;
}
public void setCustomClickListener(ICustomClickListener customClickListener) {
getHeadAndFootAdapter().setCustomClickListener(customClickListener);
}
}
setHeadViewConfig和setFootdViewConfig兩個(gè)方法作用一致,只是兩個(gè)添加的元素不一樣
wrapHeadAdapter方法的作用是用于構(gòu)造CustomAdapter用來處理頭部尾部的view旱捧,如果處理不了独郎, 將會(huì)自動(dòng)轉(zhuǎn)交給自定義的Adapter處理
類里部分參數(shù)沒用到,但是我感覺后面我會(huì)用到的
下面是CustomAdapter的代碼
CustomAdapter 繼承自 RecyclerView.Adapter<? extends ViewHolder>
public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {
private List<ViewConfig> headConfig;
private List<ViewConfig> footConfig;
private ArrayList<ViewConfig> EMPTY_LIST = new ArrayList<>();
private LayoutInflater inflater;
private RecyclerView.Adapter mAdapter;
private int headcount = 0;
private int footcount = 0;
private Context mContext;
private ICustomClickListener customClickListener;
public CustomAdapter(List<ViewConfig> headConfig, List<ViewConfig> footConfig, RecyclerView.Adapter mAdapter, Context mContext) {
this.mAdapter = mAdapter;
this.inflater = LayoutInflater.from(mContext);
this.mContext = mContext;
if (headConfig == null) {
this.headConfig = EMPTY_LIST;
} else {
this.headConfig = headConfig;
}
if (footConfig == null) {
this.footConfig = EMPTY_LIST;
} else {
this.footConfig = footConfig;
}
}
/**
* 設(shè)置監(jiān)聽事件
*
* @param customClickListener
*/
public void setCustomClickListener(ICustomClickListener customClickListener) {
this.customClickListener = customClickListener;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int index) {
LogUtils.e("CustomAdapter", "index:" + index);
if (index == ViewConfig.HEADVIEW_TYPE) {
FrameLayout contentView = (FrameLayout) inflater.inflate(R.layout.item_head_foot_parent, viewGroup, false);
View cView = headConfig.get(headcount++).getContentView();
ViewGroup vg = (ViewGroup) cView.getParent();
if (vg != null) {
vg.removeView(cView);
}
contentView.addView(cView);
return new CustomViewHolder(contentView);
} else if (index == ViewConfig.FOOTVIEW_TYPE) {
FrameLayout contentView = (FrameLayout) inflater.inflate(R.layout.item_head_foot_parent, viewGroup, false);
View cView = footConfig.get(footcount++).getContentView();
ViewGroup vg = (ViewGroup) cView.getParent();
if (vg != null) {
vg.removeView(cView);
}
contentView.addView(cView);
return new CustomViewHolder(contentView);
} else {
return mAdapter.onCreateViewHolder(viewGroup, index);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, final int position) {
CustomViewHolder customViewHolder;
if (viewHolder instanceof CustomViewHolder) {
customViewHolder = (CustomViewHolder) viewHolder;
View mParent = customViewHolder.mParent;
//設(shè)置view的點(diǎn)擊事件
if (mParent instanceof ViewGroup) {
ViewGroup parentGroup = (ViewGroup) mParent;
int childCount = parentGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
parentGroup.getChildAt(i).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (customClickListener != null) {
if (position < getHeadSize()) {
customClickListener.onClick(v, position, 0);
} else {
customClickListener.onClick(v, position - getHeadSize() - mAdapter.getItemCount(), 1);
}
}
}
});
}
}
mParent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (position < getHeadSize()) {
customClickListener.onClick(v, position, 0);
} else {
customClickListener.onClick(v, position - getHeadSize() - mAdapter.getItemCount(), 1);
}
}
});
} else {
mAdapter.onBindViewHolder(viewHolder, position - getHeadSize());
}
}
@Override
public int getItemCount() {
return mAdapter != null
? headConfig.size() + mAdapter.getItemCount() + footConfig.size()
: headConfig.size() + footConfig.size();
}
@Override
public void onClick(View v) {
LogUtils.e("CustomAdapter", "點(diǎn)擊");
}
public class CustomViewHolder extends RecyclerView.ViewHolder {
View mParent;
public CustomViewHolder(@NonNull View itemView) {
super(itemView);
mParent = itemView;
}
}
@Override
public int getItemViewType(int position) {
int headSize = getHeadSize();
int adapterCount = getItemCount();//獲取實(shí)際的個(gè)數(shù)
if (position < headSize) {
return ViewConfig.HEADVIEW_TYPE;
} else if (position >= headSize + mAdapter.getItemCount() && position < adapterCount) {
return ViewConfig.FOOTVIEW_TYPE;
}
return -1;
}
public int getHeadSize() {
return headConfig.size();
}
public int getFootSize() {
return footConfig.size();
}
}
getItemViewType中根據(jù)position當(dāng)前的值返回類型
position < headsize (那么證明現(xiàn)在就是頭部
position >= headsize && position < headSize + mAdapter.getItemCount (證明這個(gè)時(shí)候是實(shí)際的itemview
position >= headSize + mAdapter.getItemCount() && position < getItemCount (這個(gè)時(shí)候就是尾部了
在代碼中 我們只要判斷頭部和尾部就行了枚赡, 其他一律返回-1 (其他數(shù)也可以)
onCreateViewHolder()
在這個(gè)方法中根據(jù)index判斷view的類型氓癌,當(dāng)然這個(gè)index只是在我的電腦上顯示, 在你的上面顯示可能是i也可能type贫橙,等等贪婉。
起先我用了
View contentView = inflater.inflate(headConfig.get(0).getView(), viewGroup, false);
contentView.addView(contentView);
這樣的代碼, 但是拋出了一個(gè)異常卢肃,大體意思就是復(fù)用的錯(cuò)誤 后來改用了這樣的寫法
FrameLayout contentView = (FrameLayout) inflater.inflate(R.layout.item_head_foot_parent, viewGroup, false);
View cView = headConfig.get(headcount++).getContentView();
ViewGroup vg = (ViewGroup) cView.getParent();
if (vg != null) {
vg.removeView(cView);
}
contentView.addView(cView);
先得到一個(gè)FrameLayout疲迂,再將View添加進(jìn)去星压, 但是在添加進(jìn)去之前需要先判斷view有沒有parent,有的話要先用parent移除掉view鬼譬, 不然會(huì)拋出
You must call removeView() on the child's parent first
這個(gè)異常
onBindViewHolder
這個(gè)方法中會(huì)先判斷ViewHolder, 如果ViewHolder是CustomViewHolder的話會(huì)得到里面唯一的view逊脯, 再去判斷該view是普通View還是ViewGroup优质。(正常情況來說都是ViewGroup,因?yàn)閂iewholder里面的View實(shí)際就是FrameLayout
在得出FrameLayout中的子view军洼, 為子view添加了點(diǎn)擊事件和長按事件
這次的代碼大概就這么多巩螃,還沒來得及好好測(cè)一測(cè),后面會(huì)在添加頭部尾部的基礎(chǔ)上添加上拉加載下拉刷新的邏輯匕争。
部分demo調(diào)用代碼和效果圖如下
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.mData = new ArrayList<>();
this.mCustomRv = findViewById(R.id.m_customrv);
mCustomRv.setLayoutManager(new LinearLayoutManager(this));
RvAdapter rvAdapter = new RvAdapter(mData, this);
mCustomRv.addHeadView(getTextView("頭部9527", Color.parseColor("#CA66F0")));
mCustomRv.addHeadView(getTextView("頭部17131", Color.parseColor("#A6A5F1")));
mCustomRv.addFootView(getTextView("尾部9527", Color.parseColor("#05F066")));
mCustomRv.addFootView(getTextView("尾部17131", Color.parseColor("#90C56F")));
mCustomRv.setAdapter(rvAdapter);
mCustomRv.setCustomClickListener(new ICustomClickListener() {
@Override
public void onClick(View view, int position,int type, Object... data) {
LogUtils.e("MainActivity", "position:" + position);
}
@Override
public void onLongClick(View view, int position,int type, Object... data) {
}
});
/**
* 添加數(shù)據(jù)源
*/
for (int i = 0; i < 5; i++) {
mData.add("mData --- " + i);
}
}
項(xiàng)目已經(jīng)發(fā)布到ghithub 并有g(shù)radle依賴
CustomRecyclerView