Android RecyclerView簡單通用介面卡
一直都想寫一個通用的RecyclerView介面卡,但是一直都無從下手,後來看了鴻洋大神的部落格後才知道怎麼寫,並且在此基礎上添加了點自己的東西,終於算是大功告成。先上程式碼看看
public class ViewHolder extends RecyclerView.ViewHolder {
private Context context;
private SparseArray<View> mViews;
private View mConvertView;
public ViewHolder(Context context, View itemView, ViewGroup parent) {
super (itemView);
this.context=context;
this.mConvertView=itemView;
mViews=new SparseArray<>();
}
//獲得holder物件
public static ViewHolder getHolder(Context context,ViewGroup parent,int layoutId){
View itemView= LayoutInflater.from(context).inflate(layoutId,parent,false );
ViewHolder holder=new ViewHolder(context,itemView,parent);
return holder;
}
//通過id獲取控制元件
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;
}
/******************************以下是輔助方法******************************/
//設定文字的方法
public ViewHolder setTexts(int viewId,String text){
TextView tv=getView(viewId);
tv.setText(text);
return this;
}
//設定圖片的方法
public ViewHolder setImage(int viewId,int resId){
ImageView iv=getView(viewId);
iv.setImageResource(resId);
return this;
}
//設定點選事件
public ViewHolder setClickListener(int viewId, View.OnClickListener listener){
View view=getView(viewId);
view.setOnClickListener(listener);
return this;
}
}
這是一個通用Viewholder,大家可以看到程式碼很簡單,包括建構函式總共也就三個函式而已其他兩個分別是getHolder(獲得holder物件的方法)和getView(通過id獲得view的方法),在這個通用holder中定義了三個變數,其中SparseArray是用來存放View的,這個東西可有可無,用了比不用更有效率,不用的話這樣寫就好了(getView方法中):View view=mConvertView.findViewById(viewId); return (T)view;
,其他兩個也沒什麼異議的了,相信大家都懂的。以上這些就是通用ViewHolder主要的部分了。至於後面的三個輔助方法則是為了更方便的設定圖片文字等,大家有需要的可以自行加上去即可。接下來我們說說getView方法,這個方法就是我們找到item裡的控制元件的方法,大家可以看到,我們只需要將控制元件的id傳入即可。看的不是很明白的同學可以點開我上面連結的鴻洋大神的部落格來看。
ViewHolder先到這裡,現在看看RecyclerView的Adapter。
public abstract class CommentAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
protected Context mContext;
protected int mLayoutId;
protected List<T> mDatas;
protected LayoutInflater mInflater;
/**********設定FooterView和HeaderView********************/
private static final int TYPE_NORMAL = 0;
private static final int TYPE_FOOTER = 1;
private static final int TYPE_HEADER = 2;
private int headerViewId = 0;
private int footerViewId = 0;
public void setFooterView(int footerViewId) {
this.footerViewId = footerViewId;
notifyItemInserted(getItemCount() - 1);
}
public int getFooterView() {
return footerViewId;
}
public void setHeaderView(int headerViewId) {
this.headerViewId = headerViewId;
notifyItemInserted(0);
}
public int getHeaderViewId() {
return headerViewId;
}
/********************** Item點選介面 *********************/
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
private OnItemClickListener onItemClickListener;
//設定item點選時間監聽的方法
public void setOnItemClickLitener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
/************************構造器****************************/
public CommentAdapter(Context context, int layoutId, List<T> datas) {
mContext = context;
mInflater = LayoutInflater.from(context);
mLayoutId = layoutId;
mDatas = datas;
}
/***************載入更多資料方法**************/
public void onLoadMoreDatas(List<T> datas) {
if (mDatas == null) {
mDatas = datas;
notifyDataSetChanged();
} else {
mDatas.addAll(datas);
notifyDataSetChanged();
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (headerViewId != 0 && viewType == TYPE_HEADER) {
return ViewHolder.getHolder(mContext, parent, headerViewId);
} else if (footerViewId != 0 && viewType == TYPE_FOOTER) { //假如footerViewId不等於0和viewType等於TYPE_FOOTER則返回footerView
return ViewHolder.getHolder(mContext, parent, footerViewId);
}
ViewHolder viewHolder = ViewHolder.getHolder(mContext, parent, mLayoutId);
return viewHolder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final int pos = holder.getLayoutPosition();//獲得item的位置
if (getItemViewType(pos) == TYPE_HEADER) {
convert(holder, null, position, false, true);
} else if (getItemViewType(pos) == TYPE_FOOTER) {
convert(holder, null, position, true, false);
} else {
if (getHeaderViewId() != 0) {
convert(holder, mDatas.get(position - 1), position, false, false);
} else {
convert(holder, mDatas.get(position), position, false, false);
}
}
if (onItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(holder.itemView, pos);
}
});
}
}
@Override
public int getItemViewType(int position) {
if (footerViewId == 0 && headerViewId == 0) return TYPE_NORMAL;
if (headerViewId != 0) {
if (position == 0) {
return TYPE_HEADER;
}
}
if (footerViewId != 0) {
if (position == getItemCount() - 1)
return TYPE_FOOTER;
}
return TYPE_NORMAL;
}
@Override
public int getItemCount() {
if (footerViewId != 0 && headerViewId != 0) {
return mDatas.size() + 2;
} else if (footerViewId != 0 && headerViewId == 0) {
return mDatas.size() + 1;
} else if (footerViewId == 0 && headerViewId != 0) {
return mDatas.size() + 1;
}
return mDatas.size();
}
public abstract void convert(ViewHolder holder, T t, int position, boolean isFooter, boolean isHeader);
}
大家可以看到,我這個通用Adapter中提供了插入Header和Footer的方法和Item點選的方法,插入Footer和Header方法也只需要提供佈局id即可,關於這個插入footer和header,最主要的是判斷返回item數目,相信大家都可以看的懂的。其他的我就不說那麼多了,主要就是通過泛型接收資料並設定上去,載入更多資料方法也是,接收要新增的資料並通知更新即可,在這裡有個點要提醒大家,有人說Recyclerview使用notifyDataSetChanged()不如直接使用Listview,對此我並不是很懂是為什麼,反正我只會用notifyDataSetChanged()來通知更新批量資料。還有很多不完善的地方,我這裡只是提供個例子供大家參考,不喜勿噴。最後貼一下呼叫介面卡的例子,
public class Test extends Activity {
//這裡是找id,沒用過這個框架的同學用傳統的方法即可
@BindView(R.id.rv_test)
RecyclerView mTest;
@BindView(R.id.bt_addHeader)
Button mAddHeader;
@BindView(R.id.bt_addFooter)
Button mAddFooter;
@BindView(R.id.bt_addDatas)
Button mAddDatas;
TextView loadMore;
ProgressBar pb;
TextView loadMore2;
ProgressBar pb2;
CommentAdapter<String> commentAdatper = null;
private List<String> datas = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = View.inflate(Test.this, R.layout.activity_mvc, null);
ButterKnife.bind(Test.this, view);
setContentView(view);
init();
}
private void init() {
addDatas();
initListener();
mTest.setLayoutManager(new LinearLayoutManager(this));
mTest.setAdapter(commentAdatper = new CommentAdapter<String>(Test.this, R.layout.item_test, datas) {
@Override
public void convert(final ViewHolder holder, final String s, final int position, boolean isFooter, boolean isHeader) {
if (isHeader) {
loadMore2 = holder.getView(R.id.tv_loadMore);
pb2 = holder.getView(R.id.pb_loading);
} else if (isFooter && position == commentAdatper.getItemCount() - 1) {
loadMore = holder.getView(R.id.tv_loadMore);
pb = holder.getView(R.id.pb_loading);
} else {
holder.setTexts(R.id.tv_test, position + s);
}
}
});
mTest.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
commentAdatper.setOnItemClickLitener(new CommentAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(Test.this, "點選了" + position + "項", Toast.LENGTH_SHORT).show();
if (commentAdatper.getHeaderViewId() != 0 && position == 0) {
loadMore2.setVisibility(View.GONE);
pb2.setVisibility(View.VISIBLE);
}
if (commentAdatper.getFooterView() != 0 && position == commentAdatper.getItemCount() - 1) {
loadMore.setVisibility(View.GONE);
pb.setVisibility(View.VISIBLE);
}
}
});
}
private void initListener() {
mAddHeader.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
commentAdatper.setHeaderView(R.layout.item_footerview);
}
});
mAddFooter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
commentAdatper.setFooterView(R.layout.item_footerview);
}
});
mAddDatas.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
commentAdatper.onRefreshDatas(newDatas());
}
});
}
private void addDatas() {
datas.add("123");
datas.add("是領導看看菲拉斯");
datas.add("我我我");
datas.add("lsjd");
datas.add("sdf");
datas.add("fgj");
datas.add("rty");
datas.add("jh,");
datas.add("123");
datas.add("是領導看看菲拉斯");
datas.add("我我我");
datas.add("lsjd");
datas.add("sdf");
datas.add("fgj");
datas.add("rty");
datas.add("jh,");
datas.add("123");
}
private List<String> newDatas() {
List<String> datas = new ArrayList<>();
datas.add("東東");
datas.add("南南");
datas.add("西西");
datas.add("北北");
return datas;
}
}
最後是對應的佈局檔案
<?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="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/bt_addHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="新增Header"
/>
<Button
android:id="@+id/bt_addFooter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:text="新增Footer"
/>
<Button
android:id="@+id/bt_addDatas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:text="新增Datas"
/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_test"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
item的佈局檔案大家可以自行寫,這裡就不貼了。我寫的通用介面卡有很多不完善的地方,希望能給大家提供個參考,不懂的地方可以在下面留言,我一定全力解答。我旨在提供個例子,讓有需要的人蔘考。希望大家能夠共同進步,共同學習,祝早日走上人生巔峰。