仿京东商城系列5------商品推荐栏

本项目来自菜鸟窝,有兴趣者点击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);

    }



}



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容