轉載請標明出處:
http://blog.csdn.net/developer_jiangqq/article/details/50087873
本文出自:【江清清的博客】
(一).前言:
【好消息】個人網站已經上線運行,後面博客以及技術乾貨等精彩文章會同步更新,請大家關注收藏:http://www.lcode.org
作爲Android L開始,Google更新了新控件RecyclerView和CardView,這兩個控件在之前的文章中已經做了詳細介紹和使用,同時在前面還對下拉刷新組件SwipeRefreshLayout進行相關講解。本來該專題不在更新了,正好昨天有一個羣友問到了怎麼樣結合SwipeRefreshLayout,RecyclerView,CardView這三種控件實現表格佈局界面並且加入下拉刷新和上拉加載更多的效果,那麼今天我們來實現並且一步步的完善Demo。
同時關於RecyclerView,CardView,SwipeRefreshLayout控件的使用講解如下:
- RecyclerView完全解析,讓你從此愛上它(二十八)
- RecyclerView完全解析之打造新版類Gallery效果(二十九)
- RecyclerView完全解析之結合AA(Android Annotations)注入框架實例(三十)
- RecyclerView完全解析之下拉刷新與上拉加載SwipeRefreshLayout(三十一)
- CardView完全解析與RecyclerView結合使用(三十二)
具體代碼已經上傳到下面的項目中,歡迎各位去star和fork一下。
FastDev4Android框架項目地址:https://github.com/jiangqqlmj/FastDev4Android
(二).需求介紹
以上三種控件結合需要實現的效果如下:
分析需要實現的界面的效果,首先是表格佈局(GirdView)的列表,並且加入下拉刷新和上拉加載更多效果。這邊對於表格中每一項我們可以使用CardView,然後列表這塊使用RecyclerView,刷新這塊我們採用Android給我們提供的SwipeRefreshLayout控件,下面我們來具體實現以下這個效果:
(三).實例實現(基礎):
實現的第一種方法,我們可以把界面中的每一項Item佈局都採用CardView來實現,那麼每一項最終形成一個表格佈局(GirdView),主列表採用RecyclerView,那麼這邊的佈局管理器需要採用GridLayoutManger並且每行兩列分佈即可,通過前面的文章我們知道SwipeRefreshLayout的使用方法在RecyclerView外部套用即可。
3.1.Item View的佈局文件如下:
<?xmlversion="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cardview="http://schemas.android.com/apk/res-auto"
android:id="@+id/instance_cardview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
cardview:cardBackgroundColor="@color/black"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/black"
android:gravity="center"
android:layout_gravity="center_horizontal"
>
<ImageView
android:layout_margin="3dp"
android:id="@+id/item_img"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="fitXY"
/>
<TextView
android:textSize="16sp"
android:layout_margin="3dp"
android:textColor="@color/white"
android:id="@+id/item_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:lines="2"/>
</LinearLayout>
</android.support.v7.widget.CardView>
該佈局主體爲一個CardView,在CardView內部爲一個圖片和標題TextView組成。
3.2.接着創建繼承自RecyclerView.Adapter的ComInstanceAdapter適配器即可。查看下面的代碼就可以知道,我在裏邊自定義兩個ViewHolder,一個專門承載Item View,另一個是承載列表底部Foot View(用來顯示上拉加載更多提示和進度的)。也就是我們會在佈局添加刷新的時候會在RecyclerView的底部多添加一個佈局View(Foot View)(該具體使用方法我們在前面的文章已經講過了,如不瞭解可以查看前文)。並且我們這邊已經也爲RecyclerView擴展的Item點擊事件了。唯一和以前實現代碼不一樣的爲:
@Override
public int getItemCount() {
if(mInstanceBeans.size()%2==0){
//偶數
return mInstanceBeans.size()+1;
}else{
return mInstanceBeans.size()+2;
}
}
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof ItemViewHolder){
if(position<mInstanceBeans.size()){
((ItemViewHolder)holder).item_img.setImageResource(mInstanceBeans.get(position).getImg());
((ItemViewHolder)holder).item_tv.setText(mInstanceBeans.get(position).getTitle());
holder.itemView.setTag(position);
holder.itemView.setClickable(true);
}else {
((ItemViewHolder)holder).item_img.setImageResource(R.drawable.moren);
((ItemViewHolder)holder).item_tv.setText("");
holder.itemView.setClickable(false);
}
}else if(holder instanceof FootViewHolder){
//上拉加載更多佈局數據綁定
}
}
上面數量我進行判斷需要綁定的數據是偶數還是奇數,因爲我們這邊的每一行是兩個數據,如果我們的數據是奇數的話,那麼這邊最後的FootView佈局會加在右邊瞭如下顯示:
這樣的效果是比較醜的,所以要把FootView移動到底部,奇數情況數據進行加1即可,最後數據綁定的時候控制顯示一下。
ComInstanceAdapter完成代碼如下:
public class ComInstanceAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private List<InstanceBean> mInstanceBeans;
private LayoutInflater mInflater;
//佈局新增一項類別
//普通ITEM
private static final int ITEM_VIEW=1;
//FOOT ITEM
private static final int FOOT_VIEW=2;
public ComInstanceAdapter(Context context,List<InstanceBean> pInstanceBeans){
this.mContext=context;
this.mInstanceBeans=pInstanceBeans;
mInflater=LayoutInflater.from(this.mContext);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
if (viewType == ITEM_VIEW) {
final View view =mInflater.inflate(R.layout.com_instance_item_layout, parent, false);
view.setOnClickListener(newView.OnClickListener() {
@Override
public void onClick(View v) {
if (onItemClickListener !=null) {
onItemClickListener.onItemClick(view, (int) view.getTag());
}
}
});
return new ItemViewHolder(view);
} else if (viewType == FOOT_VIEW) {
View view =mInflater.inflate(R.layout.instance_load_more_layout, parent, false);
return new FootViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof ItemViewHolder){
if(position<mInstanceBeans.size()){
((ItemViewHolder)holder).item_img.setImageResource(mInstanceBeans.get(position).getImg());
((ItemViewHolder)holder).item_tv.setText(mInstanceBeans.get(position).getTitle());
holder.itemView.setTag(position);
holder.itemView.setClickable(true);
}else {
((ItemViewHolder)holder).item_img.setImageResource(R.drawable.moren);
((ItemViewHolder)holder).item_tv.setText("");
holder.itemView.setClickable(false);
}
}else if(holder instanceofFootViewHolder){
//上拉加載更多佈局數據綁定
}
}
@Override
public int getItemViewType(int position) {
if (position + 1 == getItemCount()) {
return FOOT_VIEW;
} else {
return ITEM_VIEW;
}
}
@Override
public int getItemCount() {
if(mInstanceBeans.size()%2==0){
//偶數
return mInstanceBeans.size()+1;
}else{
return mInstanceBeans.size()+2;
}
}
public static class ItemViewHolder extends RecyclerView.ViewHolder{
private ImageView item_img;
private TextView item_tv;
public ItemViewHolder(View itemView) {
super(itemView);
item_img=(ImageView)itemView.findViewById(R.id.item_img);
item_tv=(TextView)itemView.findViewById(R.id.item_tv);
}
}
/**
* 底部FootView佈局
*/
public static class FootViewHolder extends RecyclerView.ViewHolder{
private TextView foot_view_item_tv;
public FootViewHolder(View view) {
super(view);
foot_view_item_tv=(TextView)view.findViewById(R.id.foot_view_item_tv);
}
}
/**
* Item 點擊監聽回調接口
*/
public interface OnItemClickListener {
void onItemClick(View view,intposition);
}
private OnItemClickListener onItemClickListener;
public OnItemClickListener getOnItemClickListener() {
return onItemClickListener;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener =onItemClickListener;
}
/**
* 進行下拉刷新數據添加 並且刷新UI
* @param pInstanceBeans
*/
public void addRefreshBeans(List<InstanceBean> pInstanceBeans){
List<InstanceBean> temp=new ArrayList<InstanceBean>();
temp.addAll(pInstanceBeans);
temp.addAll(mInstanceBeans);
mInstanceBeans.removeAll(mInstanceBeans);
mInstanceBeans.addAll(temp);
notifyDataSetChanged();
}
/**
* 進行上拉加載更多 並且刷新UI
* @param pInstanceBeans
*/
public void addMoreBeans(List<InstanceBean> pInstanceBeans){
mInstanceBeans.addAll(pInstanceBeans);
notifyDataSetChanged();
}
}
3.3.接下來是佈局RecyclerView和SwipeRefreshLayout佈局文件了,這個和之前的文章上面的一樣,我們在RecyclerView外部嵌套SwipeRefreshLayout組件即可,最後在Activity中獲取使用。
com_instance_layout.xml佈局完整代碼如下:
<?xmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black">
<includelayout="@layout/common_top_bar_layout"/>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/instance_swiperefreshlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none">
<android.support.v7.widget.RecyclerView
android:id="@+id/instance_recycler"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none"/>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
3.4.最後Activity中初始化SwipeRefreshLayout控件並且設置背景和刷新進度條的顏色。RecyclerView控件初始化以及設置佈局管理器(GirdLayoutManger)和適配器即可。
ComInstanceActivity完整代碼如下:
public class ComInstanceActivity extends BaseActivity {
private LinearLayout top_bar_linear_back;
private TextView top_bar_title;
private RecyclerView instance_recycler;
private ComInstanceAdapter adapter;
private SwipeRefreshLayout instance_swiperefreshlayout;
private int lastVisibleItem;
//是否正在加載更多的標誌
private boolean isMoreLoading=false;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.com_instance_layout);
top_bar_linear_back=(LinearLayout)this.findViewById(R.id.top_bar_linear_back);
instance_swiperefreshlayout=(SwipeRefreshLayout)this.findViewById(R.id.instance_swiperefreshlayout);
//設置刷新時動畫的顏色,可以設置4個
instance_swiperefreshlayout.setProgressBackgroundColorSchemeResource(android.R.color.white);
instance_swiperefreshlayout.setColorSchemeResources(android.R.color.holo_blue_light,
android.R.color.holo_red_light,android.R.color.holo_orange_light,
android.R.color.holo_green_light);
top_bar_linear_back.setOnClickListener(new CustomOnClickListener());
top_bar_title=(TextView)this.findViewById(R.id.top_bar_title);
top_bar_title.setText("綜合實例");
instance_recycler=(RecyclerView)this.findViewById(R.id.instance_recycler);
final GridLayoutManager gridLayoutManager=new GridLayoutManager(this,2);
instance_recycler.setLayoutManager(gridLayoutManager);
instance_recycler.setAdapter(adapter =new ComInstanceAdapter(this, InstanceDataUtils.getInstanceBeans()));
//添加Item點擊監聽事件
adapter.setOnItemClickListener(new ComInstanceAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view,int position) {
Toast.makeText(ComInstanceActivity.this,"點擊了第"+position+"項",Toast.LENGTH_SHORT).show();
}
});
//下拉刷新
instance_swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(newRunnable() {
@Override
public void run() {
List<InstanceBean> temp=new ArrayList<InstanceBean>();
for(inti=0;i<5;i++){
InstanceBean bean=new InstanceBean("我是楊穎Item"+i,R.drawable.baby);
temp.add(bean);
}
adapter.addRefreshBeans(temp);
instance_swiperefreshlayout.setRefreshing(false);
Toast.makeText(ComInstanceActivity.this, "更新了五條數據...", Toast.LENGTH_SHORT).show();
}
},3500);
}
});
//上拉加載更多
instance_recycler.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState ==RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 ==adapter.getItemCount()) {
if(!isMoreLoading){
isMoreLoading=true;
newHandler().postDelayed(new Runnable() {
@Override
public void run() {
List<InstanceBean> temp=new ArrayList<InstanceBean>();
for (int i = 0; i< 5; i++) {
InstanceBean bean=new InstanceBean("我是MoreItem"+i,R.drawable.meinv);
temp.add(bean);
}
adapter.addMoreBeans(temp);
Toast.makeText(ComInstanceActivity.this, "上拉加載了五條數據...", Toast.LENGTH_SHORT).show();
isMoreLoading=false;
}
},2000);
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView,dx, dy);
lastVisibleItem =gridLayoutManager.findLastVisibleItemPosition();
}
});
}
class CustomOnClickListener implementsView.OnClickListener{
@Override
public void onClick(View v) {
ComInstanceActivity.this.finish();
}
}
}
上面的代碼中要使用SwipeRefreshLayout實現下拉刷新只要設置OnRefreshListener監聽器即可,要實現上拉加載更多給RecyclerView添加OnScrollListener判斷是否已經下拉滑動的底部,然後開始加載更多數據。
3.5.運行效果如下:
(四).實例實現(改進版):
看到上面的效果實現,我們會發現一個問題:FootView雖然在底部了,但是表格一行是兩列的,所以FootView就會在底部的最左邊了,只會佔據一個CardView的空間。但是我們平時的效果應該是FootView是整一行實現的,這樣比較美觀。OK 下面我們來進行改進一下:
之前RecyclerView我們採用的是GirdLayoutManger佈局,這邊我們採用LinearLayoutManger垂直方向實現。這樣的話每一行爲單獨的Item了,並且該行Item中我直接放上一個CardView佈局,然後在內部添加兩個水平佈局的LinearLayout。但是這樣修改之後Item的點擊事件就要進行修改了,我們這邊把點擊事件添加在每一個LinearLayout佈局。
4.1.advance_com_instance_item_layout完成佈局代碼如下:
<?xmlversion="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cardview="http://schemas.android.com/apk/res-auto"
android:id="@+id/instance_cardview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
cardview:cardBackgroundColor="@color/black"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@color/black"
android:gravity="center"
android:layout_gravity="center_horizontal"
>
<LinearLayout
android:id="@+id/leftL"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:gravity="center">
<ImageView
android:src="@drawable/meinv"
android:layout_margin="3dp"
android:id="@+id/item_img_one"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="fitXY"
/>
<TextView
android:text="古代美女"
android:textSize="16sp"
android:layout_margin="3dp"
android:textColor="@color/white"
android:id="@+id/item_tv_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:lines="2"/>
</LinearLayout>
<LinearLayout
android:id="@+id/rightL"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_weight="1"
android:gravity="center">
<ImageView
android:src="@drawable/liuyan"
android:layout_margin="3dp"
android:id="@+id/item_img_two"
android:layout_width="150dp"
android:layout_height="150dp"
android:scaleType="fitXY"
/>
<TextView
android:text="柳巖"
android:textSize="16sp"
android:layout_margin="3dp"
android:textColor="@color/white"
android:id="@+id/item_tv_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:lines="2"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
我們看到上面的佈局中兩個LinearLayout分別加上了兩個id,用來後面獲取控件並且添加點擊監聽事件。
4.2.然後自定義適配器中的數據綁定方法和之前的有所不同:
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof ItemViewHolder){
AdvanceInstanceBean advanceInstanceBean=mAdvanceInstanceBeans.get(position);
if(advanceInstanceBean!=null){
final List<InstanceBean> instanceBeans=advanceInstanceBean.getInstanceBeans();
if(instanceBeans.size()==2){
((ItemViewHolder)holder).item_img_one.setImageResource(instanceBeans.get(0).getImg());
((ItemViewHolder)holder).item_tv_one.setText(instanceBeans.get(0).getTitle());
((ItemViewHolder)holder).item_img_two.setImageResource(instanceBeans.get(1).getImg());
((ItemViewHolder)holder).item_tv_two.setText(instanceBeans.get(1).getTitle());
((ItemViewHolder)holder).leftL.setOnClickListener(new View.OnClickListener() {
@Override
public voidonClick(View v) {
if(onItemClickListener != null) {
onItemClickListener.onItemClick(instanceBeans.get(0));
}
}
});
((ItemViewHolder)holder).rightL.setOnClickListener(new View.OnClickListener() {
@Override
public voidonClick(View v) {
if(onItemClickListener!=null){
onItemClickListener.onItemClick(instanceBeans.get(1));
}
}
});
}else {
((ItemViewHolder)holder).item_img_one.setImageResource(instanceBeans.get(0).getImg());
((ItemViewHolder)holder).item_tv_one.setText(instanceBeans.get(0).getTitle());
((ItemViewHolder)holder).item_img_two.setImageResource(R.drawable.moren);
((ItemViewHolder)holder).item_tv_two.setText("");
}
}
}else if(holder instanceof FootViewHolder){
//上拉加載更多佈局數據綁定
}
}
上面的數據綁定代碼中,給左右兩個佈局分別加入了onClick事件,來進行回調點擊數據傳遞。具體回調接口定義如下:
/**
* Item 點擊監聽回調接口
*/
public interface OnItemClickListener {
/**
* item回調的數據
* @param instanceBean
*/
void onItemClick(InstanceBean instanceBean);
}
具體完成代碼比較多就不貼了,到時候大家clone一下項目代碼:
FastDev4Android框架項目地址:https://github.com/jiangqqlmj/FastDev4Android
4.3.Acitivty中處理初始化設置的代碼就不貼了
4.4.運行效果如下:
(五).最後總結
今天我們通過SwipeRefreshLayout+RecyclerView+CardView實現的表格佈局以及下拉刷新,上拉加載更多的效果。
本次實例代碼因爲比較多,代碼全貼比較浪費篇幅,重點在於講解思路了。不過實例註釋過的全部代碼已經上傳到Github項目中了。同時歡迎大家去Github站點進行clone或者fork瀏覽整個開源快速開發框架項目~
https://github.com/jiangqqlmj/FastDev4Android
尊重原創,轉載請註明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵權必究!
關注我的訂閱號,每天分享移動開發技術(Android/IOS),項目管理以及博客文章!
關注我的微博,可以獲得更多精彩內容