本项目来自菜鸟窝,有兴趣者点击http://www.cniao5.com/course/
项目已经做完,
https://github.com/15829238397/CN5E-shop
仿京东商城系列0------项目简介
仿京东商城系列1------fragmentTabHost实现底部导航栏
仿京东商城系列2------自定义toolbar
仿京东商城系列3------封装Okhttp
仿京东商城系列4------轮播广告条
仿京东商城系列5------商品推荐栏
仿京东商城系列6------下拉刷新上拉加载的商品列表
仿京东商城系列7------商品分类页面
仿京东商城系列8------自定义的数量控制器
仿京东商城系列9------购物车数据存储器实现
仿京东商城系列10------添加购物车,管理购物车功能实现
仿京东商城系列11------商品排序功能以及布局切换实现(Tablayout)
仿京东商城系列12------商品详细信息展示(nativie与html交互)
仿京东商城系列13------商品分享(shareSDK)
仿京东商城系列14------用户登录以及app登录拦截
仿京东长城系列15------用户注册,SMSSDK集成
仿京东商城系列16------支付SDK集成
仿京东商城系列17------支付功能实现
仿京东商城系列18------xml文件读取(地址选择器)
仿京东商城系列19------九宫格订单展示
仿京东商城系列20------终章
前言
上一篇,我们实现了商品广告的轮播效果,今天我们一起来看看,如何实现商品推荐列表。上效果图:
商品推荐列表内容
大纲
- RecyclerView控件介绍。
- CardView控件介绍。
- adapter封装使用。
内容
- RecyclerView简介
recylerView详解
通过使用RecyclerView控件,我们可以在APP中创建带有Material Design风格的复杂列表。RecyclerView控件和ListView的原理有很多相似的地方,都是维护少量的View来进行显示大量的数据,不过RecyclerView控件比ListView更加高级并且更加灵活。当我们的数据因为用户事件或者网络事件发生改变的时候也能很好的进行显示。
和ListView不同的是,RecyclerView不用在负责Item的显示相关的功能,在这边所有有关布局,绘制,数据绑定等都被分拆成不同的类进行管理,下面我这边会一个个的进行讲解。同时RecyclerView控件提供了以下两种方法来进行简化和处理大数量集合:
1. 采用LayoutManager来处理Item的布局
2. 提供Item操作的默认动画,例如在增加或者删除item的时候
你也可以自定义LayoutManager或者设置添加/删除的动画,整体的RecyclerView结构图如下:
为了使用RecyclerView控件,我们需要创建一个Adapter和一个LayoutManager:
Adapter:继承自RecyclerView.Adapetr类,主要用来将数据和布局item进行绑定。LayoutManager:布局管理器,设置每一项view在RecyclerView中的位置布局以及控件item view的显示或者隐藏。当View重用或者回收的时候,LayoutManger都会向Adapter来请求新的数据来进行替换原来数据的内容。这种回收重用的机制可以提供性能,避免创建很多的view或者是频繁的调用findViewById方法。这种机制和ListView还是很相似的。
RecyclerView提供了三种内置的LayoutManager:
1. LinearLayoutManager:线性布局,横向或者纵向滑动列表2. GridLayoutManager:表格布局3. StaggeredGridLayoutManager:流式布局,例如瀑布流效果
当然除了上面的三种内部布局之外,我们还可以继承RecyclerView.LayoutManager来实现一个自定义的LayoutManager。
Animations(动画)效果:
RecyclerView对于Item的添加和删除是默认开启动画的。我们当然也可以通过RecyclerView.ItemAnimator类定制动画,然后通过RecyclerView.setItemAnimator()方法来进行使用。
RecyclerView相关类:
RecyclerView.Adapter
可以托管数据集合,为每一项Item创建视图并且绑定数据
RecyclerView.ViewHolder
承载Item视图的子布局
RecyclerView.LayoutManager
负责Item视图的布局的显示管理
RecyclerView.ItemDecoration
给每一项Item视图添加子View,例如可以进行画分隔线之类的
RecyclerView.ItemAnimator
负责处理数据添加或者删除时候的动画效果
- RecyclerView的简单使用
1、添加库依赖:
dependencies {
…….
compile'com.android.support:recyclerview-v7:23.1.1'
}
2、新建布局,引入RecyclerView控件:
<?xmlversionxmlversion="1.0" encoding="utf-8"?>
<LinearLayout xmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent">
<includelayoutincludelayout="@layout/common_top_bar_layout"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView_one"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
></android.support.v7.widget.RecyclerView>
</LinearLayout>
3、在Activity中获取RecyclerView控件然后进行设置LayoutManger以及Adapter即可,和ListView的写法有点类似:
public class RecyclerViewTestActivity extends AppCompatActivity {
private RecyclerView recyclerView_one;
private RecyclerView.Adapter mAdapter;
private LinearLayoutManager mLayoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//开始设置RecyclerView
recyclerView_one=(RecyclerView)this.findViewById(R.id.recyclerView);
//设置固定大小
recyclerView_one.setHasFixedSize(true);
//创建线性布局
mLayoutManager = new LinearLayoutManager(this);
//垂直方向
mLayoutManager.setOrientation(OrientationHelper.VERTICAL);
//给RecyclerView设置布局管理器
recyclerView_one.setLayoutManager(mLayoutManager);
//创建适配器,并且设置
mAdapter = new TestRecyclerAdapter(this);
recyclerView_one.setAdapter(mAdapter);
}
}
4、自定义一个适配器来进行创建item view以及绑定数据
public class TestRecyclerAdapter extends RecyclerView.Adapter<TestRecyclerAdapter.ViewHolder>{
private LayoutInflater mInflater;
private String[] mTitles=null;
public TestRecyclerAdapter(Context context){
this.mInflater=LayoutInflater.from(context);
this.mTitles=new String[20];
for (int i=0;i<20;i++){
int index=i+1;
mTitles[i]="item"+index;
}
}
/**
* item显示类型
* @param parent
* @param viewType
* @return
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view=mInflater.inflate(R.layout.item_recycler_layout,parent,false);
//view.setBackgroundColor(Color.RED);
ViewHolder viewHolder=new ViewHolder(view);
return viewHolder;
}
/**
* 数据的绑定显示
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.item_tv.setText(mTitles[position]);
}
@Override
public int getItemCount() {
return mTitles.length;
}
//自定义的ViewHolder,持有每个Item的的所有界面元素
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView item_tv;
public ViewHolder(View view){
super(view);
item_tv = (TextView)view.findViewById(R.id.item_tv);
}
}
}
这个自定义Adapter和我们在使用Listview时候的Adapter相比还是有点不太一样的,首先这边我们需要继承RecyclerView.Adaper类,然后实现两个重要的方法onBindViewHodler()以及onCreateViewHolder(),这边我们看出来区别,使用RecyclerView控件我们就可以把Item View视图创建和数据绑定这两步进行分来进行管理,用法就更加方便而且灵活了,并且我们可以定制打造千变万化的布局。同时这边我们还需要创建一个ViewHolder类,该类必须继承自RecyclerView.ViewHolder类,现在Google也要求我们必须要实现ViewHolder来承载Item的视图。
- adapter的封装
上面我们已经了解到,adapter的定义和使用方式,那么,我们开始封装,上代码:
package com.example.cne_shop.base;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 博 on 2017/7/12.
*/
package com.example.cne_shop.base;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 博 on 2017/7/12.
*/
public abstract class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
protected List<T> mDatas ;
protected Context context ;
protected LayoutInflater inflater ;
protected int resId ;
protected View mView ;
protected onItemClickListener listener ;
private List<Boolean> isSelecterd ;
//新建一个接口,用于响应点击事件
public interface onItemClickListener{
void onClick(View view, int position) throws Exception;
} ;
//设置点击事件
public void setOnItemClickListener(onItemClickListener listener){
this.listener = listener ;
}
public BaseAdapter(Context context , List<T> mDatas , int resId) {
this.context = context ;
this.mDatas = mDatas ;
this.inflater = LayoutInflater.from(context) ;
this.resId = resId ;
this.isSelecterd = new ArrayList<Boolean>() ;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mView = inflater.inflate(resId , null , false) ;
return new BaseViewHolder(mView , listener);
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
T t = getData( position) ;
bindData(holder , t , position);
}
@Override
public int getItemCount() {
for (int i = 0 ; i < mDatas.size() ; i++){
isSelecterd.add(false) ;
}
return mDatas.size();
}
public abstract void bindData(BaseViewHolder holder , T t , int position);
public T getData ( int positon){
return mDatas.get(positon) ;
}
public void cleanData(){
this.notifyItemRangeRemoved(0 , mDatas.size());
mDatas.clear();
}
public void addData(List<T> data){
mDatas.addAll(mDatas.size() , data ) ;
this.notifyItemRangeChanged(0 , mDatas.size());
}
public int getMdataSize(){
return mDatas.size() ;
}
}
package com.example.cne_shop.base;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.TextView;
/**
* Created by 博 on 2017/7/12.
*/
public class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
//定义一个SparseArray<View>来存储所有的view
private SparseArray<View> views ;
private View itemView ;
private BaseAdapter.onItemClickListener listener ;
public BaseViewHolder(View itemView , BaseAdapter.onItemClickListener listener ) {
super(itemView);
this.itemView = itemView ;
this.views = new SparseArray<View>() ;
this.listener = listener ;
}
/**
* 暴露给外界的方法用来获得view实例
*/
// public SimpleDraweeView findSimpleDraweeView(int resId){
// return findView(resId) ;
// }
public ImageView findImageView(int resId){
return findView(resId) ;
}
public Button findButton (int resId){
return findView(resId) ;
}
public TextView findTextView(int resId){
return findView(resId) ;
}
// public NineGridView findNineGridView(int resId){
// return findView(resId) ;
// }
public RadioButton findRadioButton(int resId){
return findView(resId) ;
}
public CheckBox findCheckBox(int resId) {return findView(resId) ; }
// public NumControlerView findNumControlerView(int resId) {return findView(resId) ;}
//获得view实例对象。在views里查找,如果存在,直接返回,如果不存在实例化一个,并将实例化结果返回。
private < T extends View> T findView (int resId){
if ( views.get(resId) == null ){
View view = itemView.findViewById(resId) ;
views.put(resId , view);
//给新得到的view增加点击事件
view.setOnClickListener(this);
}
return (T) views.get(resId) ;
}
@Override
public void onClick(View v) {
if(listener != null)
try {
listener.onClick(v , getLayoutPosition() );
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 我们定义了两个类,分别继承了extends RecyclerView.Adapter<BaseViewHolder>和RecyclerView.ViewHolder。
- 先说BaseViewHolder 类,这各类必须继承RecyclerView.ViewHolder,并实现onBindViewHodler()以及onCreateViewHolder()方法,承载item的视图。实现item的视图的数据填充,并与对应的item绑定。
- 其次是BaseAdapter类,此类继承了RecyclerView.Adapter<BaseViewHolder> 类,并实现了onCreateViewHolder(ViewGroup parent, int viewType) , onBindViewHolder(BaseViewHolder holder, int position) getItemCount(),等方法。注意
public abstract void bindData(BaseViewHolder holder , T t , int position);
这个抽象方法,留给使用者来实现,填充具体的控件数据。
最后我们暴露一些方法供使用者操作adapter数据。
public T getData ( int positon){
return mDatas.get(positon) ;
}
public void cleanData(){
this.notifyItemRangeRemoved(0 , mDatas.size());
mDatas.clear();
}
public void addData(List<T> data){
mDatas.addAll(mDatas.size() , data ) ;
this.notifyItemRangeChanged(0 , mDatas.size());
}
public int getMdataSize(){
return mDatas.size() ;
}
- 使用封装
使用方式非常简单,只需要继承BaseAdapter并实现BindData()方法即可。
package com.example.cne_shop.adapter;
import android.content.Context;
import com.example.cne_shop.base.BaseAdapter;
import com.example.cne_shop.base.BaseViewHolder;
import com.example.cne_shop.okhttp.BaseCallback;
import java.util.List;
/**
* Created by 博 on 2017/7/12.
*/
package com.example.cne_shop.adapter;
import android.content.Context;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.example.cne_shop.R;
import com.example.cne_shop.base.BaseViewHolder;
import com.example.cne_shop.base.ResyslerViewIndicator;
import java.util.List;
/**
* Created by 博 on 2017/7/12.
*/
public class HomeAdapter extends BaseAdapter<ResyslerViewIndicator> {
private static int viewTypeNum = 0 ;
private ImageView imageViewBig ;
private ImageView imageViewSmallBottom ;
private ImageView imageViewSmallTop ;
private TextView title ;
public HomeAdapter(Context context, List<ResyslerViewIndicator> mDatas) {
super(context, mDatas, (viewTypeNum ++ % 2 == 0) ? R.layout.home_cardview_left : R.layout.home_cardview_right );
}
@Override
public void bindData(BaseViewHolder holder, ResyslerViewIndicator resyslerViewIndicator , int position) {
imageViewBig = holder.findImageView(R.id.imageview_big) ;
imageViewSmallTop = holder.findImageView(R.id.imageview_small_top);
imageViewSmallBottom = holder.findImageView(R.id.imageview_small_bottom) ;
title = holder.findTextView(R.id.cardView_title) ;
this.title.setText(resyslerViewIndicator.getTitle());
//
//将图片加载到指定view
Glide.with(context).load(resyslerViewIndicator.getCpTwo().getImgUrl()).asBitmap().into(this.imageViewSmallBottom);
Glide.with(context).load(resyslerViewIndicator.getCpOne().getImgUrl()).asBitmap().into(this.imageViewBig);
Glide.with(context).load(resyslerViewIndicator.getCpThree().getImgUrl()).asBitmap().into(this.imageViewSmallTop);
}
}
网友评论