本文為菜鳥窩作者劉婷的連載∏渖樱”商城項目實戰(zhàn)”系列來聊聊仿”京東淘寶的購物商城”如何實現(xiàn)莱衩。
每個程序猿必備的110本經(jīng)典編程書,免費領(lǐng)取地址:http://mp.weixin.qq.com/s/cx433vAj_CDLzmhOoUS6zA
在文章《商城項目實戰(zhàn) | 11.1 實現(xiàn)商城分類模塊的一級目錄和二級目錄效果(一)》中已經(jīng)詳細(xì)介紹了一級目錄的實現(xiàn)娇澎,下面就繼續(xù)往下實現(xiàn)輪播廣告以及二級目錄的效果,效果圖還是要先來貼一貼的睹晒。

實現(xiàn)輪播廣告
分類模塊中的輪播廣告效果和主頁的是一樣的趟庄,就使用一樣的方法實現(xiàn)就可以了。
1. 修改主布局
之前的主布局中只有右側(cè)的一級目錄 RecyclerView伪很,現(xiàn)在就要添加輪播廣告的控件 SliderLayout戚啥。
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/tools"
android:orientation="vertical">
<com.liuting.cniao_shop.widget.CNiaoToolbar
android:id="@id/toolbar"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="分類"
android:layout_alignParentTop="true"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/basicPaddingTop">
<android.support.v7.widget.RecyclerView
android:id="@+id/category_recycler_view_top"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@color/white"
>
</android.support.v7.widget.RecyclerView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginLeft="2dp"
>
<com.daimajia.slider.library.SliderLayout
android:id="@+id/category_slider_ads"
android:layout_width="match_parent"
android:layout_height="180dp"
custom:pager_animation="Accordion"
custom:auto_cycle="true"
custom:indicator_visibility="visible"
custom:pager_animation_span="1100"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
2. 請求網(wǎng)絡(luò)數(shù)據(jù)
這里的輪播廣告欄所要請求的 Json 數(shù)據(jù)的格式和主頁的一樣的,所要使用的實體類也是之前已經(jīng)定義過了的 BannerInfo 對象锉试,里面包括了 name猫十、imgUrl 和 ID 三個屬性,實體類的具體定義就不展示了,直接請求網(wǎng)絡(luò)數(shù)據(jù)了拖云。
private void requestBannerData(int type) {
String url = Constants.API.BANNER+"?type="+type;
mHttpHelper.get(url, new SpotsCallBack<List<BannerInfo>>(getContext()){
@Override
public void onSuccess(Response response, List<BannerInfo> banners) {
...
}
@Override
public void onError(Response response, int code, Exception e) {
}
@Override
public void onFailure(Request request, Exception e) {
super.onFailure(request, e);
}
});
}
輪播廣告的網(wǎng)絡(luò)數(shù)據(jù)的請求寫在了 requestBannerData() 方法中贷笛,相應(yīng)地在請求成功 onSuccess() 方法、請求失敗 onFailure() 以及請求出錯的 onError() 方法中做處理宙项。
3. 顯示輪播圖片
獲取到了數(shù)據(jù)乏苦,下面就是要把廣告圖片加載顯示出來。
private void showSliderViews(List<BannerInfo> banners){
if(banners !=null){
for (BannerInfo banner : banners){
DefaultSliderView sliderView = new DefaultSliderView(this.getActivity());
sliderView.image(banner.getImgUrl());
sliderView.description(banner.getName());
sliderView.setScaleType(BaseSliderView.ScaleType.Fit);
sliderAds.addSlider(sliderView);
}
}
sliderAds.setPresetIndicator(SliderLayout.PresetIndicators.Center_Bottom);
sliderAds.setCustomAnimation(new DescriptionAnimation());
sliderAds.setPresetTransformer(SliderLayout.Transformer.Default);
sliderAds.setDuration(3000);
}
廣告圖片的顯示加載則寫在了 showSliderViews() 方法中尤筐,最后只要把這個方法寫入網(wǎng)絡(luò)請求成功的 onSuccess() 中就大功告成了汇荐。
@Override
public void onSuccess(Response response, List<BannerInfo> banners) {
showSliderViews(banners);
}
4. 效果圖
運行代碼看下輪播廣告的效果圖。

輪播廣告實現(xiàn)完成了盆繁,接下來就是最后一步掀淘,二級目錄的實現(xiàn)。
實現(xiàn)二級目錄
二級目錄中是商品的網(wǎng)格列表油昂,還是使用 RecyclerView革娄,不過這次是網(wǎng)格形式的。
1. 修改主布局
在主布局中添加二級目錄秕狰,這里還是使用 RecyclerView稠腊。
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/tools"
android:orientation="vertical">
<com.liuting.cniao_shop.widget.CNiaoToolbar
android:id="@id/toolbar"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="分類"
android:layout_alignParentTop="true"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/basicPaddingTop">
<android.support.v7.widget.RecyclerView
android:id="@+id/category_recycler_view_top"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@color/white"
>
</android.support.v7.widget.RecyclerView>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginLeft="2dp"
>
<com.daimajia.slider.library.SliderLayout
android:id="@+id/category_slider_ads"
android:layout_width="match_parent"
android:layout_height="180dp"
custom:pager_animation="Accordion"
custom:auto_cycle="true"
custom:indicator_visibility="visible"
custom:pager_animation_span="1100"
/>
<com.cjj.MaterialRefreshLayout
android:id="@+id/category_layout_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
app:overlay="false"
app:wave_show="false"
app:progress_colors="@array/material_colors"
app:wave_height_type="higher"
app:progress_show_circle_backgroud="false"
>
<com.liuting.cniao_shop.widget.NoScrollRecyclerView
android:id="@+id/category_recycler_view_secondary"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.liuting.cniao_shop.widget.NoScrollRecyclerView>
</com.cjj.MaterialRefreshLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</LinearLayout>
2. 定義商品實體類
public class WaresInfo implements Serializable{
private Long id;
private String name;
private String imgUrl;
private String description;
private Float price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
}
id 就是商品 ID,name 是商品名稱鸣哀,imgUrl 是商品圖片的路徑架忌,description 是商品描述,至于 price 就是價格了我衬。
3. 實現(xiàn)商品列表 item 布局以及 Adapter
商品列表的布局中需要有圖片實現(xiàn)叹放、商品名稱以及價格的顯示,所以布局如下挠羔。
<?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="wrap_content"
android:background="@drawable/selector_list_item"
android:orientation="vertical"
android:padding="@dimen/basicPaddingTop">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/wares_drawee_img"
android:layout_width="@dimen/ware_grid_img_width"
android:layout_height="@dimen/ware_grid_img_height"
android:layout_alignParentLeft="true"
android:layout_gravity="center"></com.facebook.drawee.view.SimpleDraweeView>
<TextView
android:id="@+id/wares_tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:maxLines="3"
android:textColor="@color/gray"
android:textSize="16sp" />
<TextView
android:id="@+id/wares_tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:textColor="@color/crimson"
android:textSize="18sp" />
</LinearLayout>
item 布局寫好了井仰,就可以實現(xiàn) Adapter,實現(xiàn)很簡單破加,直接繼承之前已經(jīng)封裝好的 SimpleAdapter俱恶。
public class WaresAdapter extends SimpleAdapter<WaresInfo> {
public WaresAdapter(Context context, List<WaresInfo> datas) {
super(context, R.layout.grid_item_wares_layout, datas);
}
@Override
protected void convert(BaseViewHolder viewHoder, WaresInfo item) {
viewHoder.getTextView(R.id.wares_tv_title).setText(item.getName());
viewHoder.getTextView(R.id.wares_tv_price).setText("¥" + item.getPrice());
SimpleDraweeView draweeView = (SimpleDraweeView) viewHoder.getView(R.id.wares_drawee_img);
draweeView.setImageURI(Uri.parse(item.getImgUrl()));
}
}
這里使用的是 SimpleDraweeView 來顯示圖片,也就是 Fresco 中的圖片組件范舀,具體可以參考文章《商城項目實戰(zhàn) | 7.1 強大的 Fresco 專為 Android 加載圖片》合是。
4. 請求網(wǎng)絡(luò)數(shù)據(jù)
同樣的將二級目錄商品列表的數(shù)據(jù)請求寫在一個單獨的方法中。
private void requestWares(long categoryId){
String url = Constants.API.WARES_LIST+"?categoryId="+categoryId+"&curPage="+currPage+"&pageSize="+pageSize;
mHttpHelper.get(url, new SpotsCallBack<PageInfo<WaresInfo>>(getActivity()) {
@Override
public void onError(Response response, int code, Exception e) {
super.onError(response, code, e);
}
@Override
public void onFailure(Request request, Exception e) {
super.onFailure(request, e);
}
@Override
public void onSuccess(Response response, PageInfo<WaresInfo> waresPage) {
....
}
});
}
5. 實現(xiàn)下拉刷新和加載更多
列表數(shù)據(jù)雖然是拿到了,但是有個問題,那就是要實現(xiàn)下拉刷新和加載更多霉赡,所以我們要先把下拉刷新和加載更多的方法寫好,這里區(qū)分三種狀態(tài)难礼,分為正常狀態(tài) STATE_NORMAL娃圆,刷新狀態(tài) STATE_REFREH 以及加載更多狀態(tài) STATE_MORE。
private void refreshData(){
currPage =1;
state=STATE_REFREH;
requestWares(category_id);
}
刷新數(shù)據(jù)時蛾茉,需要把當(dāng)前頁面 currPage 設(shè)置為1讼呢,同時狀態(tài)變?yōu)樗⑿聽顟B(tài) STATE_REFREH,最后請求最新的網(wǎng)絡(luò)數(shù)據(jù)臀稚。
而加載更多的實現(xiàn)就不一樣了吝岭。
private void loadMoreData(){
currPage = ++currPage;
state = STATE_MORE;
requestWares(category_id);
}
加載更多則是不斷往后獲取更多的數(shù)據(jù),當(dāng)前頁面 currPage 要加1吧寺。
6. 顯示商品列表
根據(jù)不同的狀態(tài)窜管,來顯示商品列表,刷新時刷新數(shù)據(jù)稚机,加載更多時獲取更多的數(shù)據(jù)幕帆,正常狀態(tài)時初始化數(shù)據(jù)。
private void showWaresData(List<WaresInfo> wares){
switch (state){
case STATE_NORMAL:
if(mWaresAdatper ==null) {
mWaresAdatper = new WaresAdapter(getContext(), wares);
recyclerViewSecondary.setAdapter(mWaresAdatper);
recyclerViewSecondary.setLayoutManager(new GridLayoutManager(getContext(), 2));
recyclerViewSecondary.setItemAnimator(new DefaultItemAnimator());
recyclerViewSecondary.addItemDecoration(new DividerGridItemDecoration(getContext()));
}
else{
mWaresAdatper.clearData();
mWaresAdatper.addData(wares);
}
break;
case STATE_REFREH:
mWaresAdatper.clearData();
mWaresAdatper.addData(wares);
recyclerViewSecondary.scrollToPosition(0);
layoutRefresh.finishRefresh();
break;
case STATE_MORE:
mWaresAdatper.addData(mWaresAdatper.getDatas().size(),wares);
recyclerViewSecondary.scrollToPosition(mWaresAdatper.getDatas().size());
layoutRefresh.finishRefreshLoadMore();
break;
}
}
數(shù)據(jù)的增刪直接調(diào)用寫好的 addData() 方法和 clearData() 就可以了赖条。
7. 一級目錄與二級目錄聯(lián)動
二級目錄雖然已經(jīng)可以顯示了失乾,但是還差了一點點,那就是和一級目錄的聯(lián)動效果纬乍,這也很簡單了碱茁,在一級目錄列表中添加 item 的點擊事件,然后傳入對應(yīng)的 category_id仿贬,然后調(diào)用請求方法 requestWares() 就可以動起來了纽竣。
mCategoryAdapter.setOnItemClickListener(new BaseAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
TopCategoryInfo category = mCategoryAdapter.getItem(position);
category_id = category.getId();
currPage=1;
state=STATE_NORMAL;
requestWares(category_id);
}
});
根據(jù) category_id 來聯(lián)動二級目錄。
8. 效果圖
獲取到效果圖如下茧泪。

最終效果圖
分類模塊已經(jīng)基本寫好了蜓氨,最終的效果圖來看下。

每個程序猿必備的110本經(jīng)典編程書队伟,免費領(lǐng)取地址:http://mp.weixin.qq.com/s/cx433vAj_CDLzmhOoUS6zA