文章目录

  • 目标
  • 一、项目分析
    • 目标
    • 项目概述
    • 开发环境
    • 模块说明
  • 二、效果展示
    • 目标
    • 店铺界面
    • 店铺详情界面
    • 店铺详情界面
    • 确认清空购物车的对话框
    • 菜品详情界面
    • 订单界面和支付界面
  • 三、服务器数据准备
    • 目标
    • 注意
  • 四、店铺功能业务实现
    • 目标
    • 4.1 搭建标题栏布局
    • 4.2 搭建广告栏界面布局
    • 4.3 搭建店铺界面布局
    • 4.4 搭建店铺列表条目界面布局
    • 4.5 封装店铺信息与菜品信息的实体类
      • 创建ShopBean类
      • 创建FoodBean类
    • 4.6 编写广告栏的适配器
    • 4.7 编写店铺列表适配器
    • 4.8 实现店铺界面显示功能
  • 五、店铺详情功能业务实现
    • 目标
    • 5.1 搭建店铺详情界面布局
    • 5.2 搭建菜单列表条目界面布局
    • 5.3 搭建购物车列表条目界面布局
    • 5.4 搭建确认清空购物车界面布局
    • 5.5 编写菜单列表适配器
    • 5.6 编写购物车列表适配器
    • 5.7 实现菜单显示与购物车功能
  • 六、菜品详情功能业务实现
    • 目标
    • 6.1 搭建菜品详情界面布局
    • 6.2 实现菜品界面显示功能
  • 七、订单功能业务实现
    • 目标
    • 7.1 搭建订单界面布局
    • 7.2 搭建订单列表条目界面布局
    • 7.3 搭建支付界面布局
    • 7.4 编写订单列表适配器
    • 7.5 实现订单显示与支付功能
  • 总结
  • 总结

目标

  • 了解仿美团外卖项目的分析,能够说出项目的开发环境和模块
  • 掌握服务器的搭建方式,能够独立搭建服务器
  • 掌握店铺界面的开发过程,能够实现店铺界面的显示效果
  • 掌握店铺详情界面与购物车的开发过程,能够独立实现购物车功能
  • 掌握菜品详情界面的开发过程,能够实现菜品详情界面的功能
  • 掌握订单界面的开发过程,能够实现订单界面的效果

​ 为了巩固第1~11章的Android基础知识,本章要开发一款仿美团外卖的项目,该项目与我们平常看到的美团外卖项目界面比较类似,展示的内容包括店铺、菜单、购物车、订单与支付等信息。为了让大家能够熟练掌握仿美团外卖项目中用到的知识点,接下来我们将从项目分析开始,一步一步带领大家开发仿美团外卖项目的各个功能。

一、项目分析

目标

  • 了解仿美团外卖项目的分析,能够说出项目的开发环境和模块

项目概述

​ 仿美团外卖项目是一个网上订餐项目,该项目中包含订餐的店铺、各店铺的菜单、购物车以及订单与付款等模块。在店铺列表中可以看到店铺的名称、月售数量、起送价格与配送费用、配送时间以及店铺特色等信息,点击店铺列表中的任意一个店铺,程序会进入到店铺详情界面,该界面主要用于显示店铺中的菜单信息,同时可以将想要吃的菜添加到购物车中,选完菜之后可以点击该界面中的“去结算”按钮,进入到订单界面,在该界面核对已点的菜单信息,并通过“去支付”按钮进行付款。

开发环境

操作系统:

  • Windows系统 8

开发工具:

  • JDK 8

  • Android Studio 3.2 +模拟器(天天模拟器)

  • Tomcat 8.5.59

API版本:

  • Android API 28

​ 由于本项目使用的是在实际开发中的网络请求代码来访问Tomcat服务器上的数据,所以开发工具中的模拟器必须为第三方模拟器(如,夜神模拟器、天天模拟器),如果用Android原生模拟器,则会访问不到数据。

模块说明

二、效果展示

目标

  • 了解仿美团外卖项目的效果展示,能够说出项目中各个界面的跳转关系

店铺界面

​ 程序启动后,首先会进入店铺界面,该界面展示的是一些由店铺信息组成的列表与一个滑动的广告栏。

店铺详情界面

​ 点击店铺列表中任意一个条目或广告栏中的任意一张图片,程序都会跳转到对应的店铺详情界面,该界面展示的是店铺的公告信息、配送信息、菜单列表信息以及购物车信息。

店铺详情界面

​ 点击菜单列表条目右侧的“加入购物车”按钮可以将菜品添加到购物车中,在界面左下角可以看到购物车中添加的菜品数量。

确认清空购物车的对话框

​ 已选商品列表的右上角有一个“清空”按钮,点击该按钮会弹出一个确认清空购物车的对话框。

菜品详情界面

​ 在店铺详情界面中,点击菜单列表的任意一个条目,程序都会跳转到菜品详情界面,菜品详情界面是一个对话框的样式。

订单界面和支付界面

​ 在店铺详情界面中,点击“去结算”按钮会跳转到订单界面,该界面通过一个列表展示购物车中的菜品信息,点击“去支付”按钮,程序会弹出一个显示支付二维码的对话框。

三、服务器数据准备

目标

  • 掌握服务器的搭建方式,能够独立搭建服务器

​ 上述图中,ROOT文件夹在apache-tomcat-7.0.56/webapps/目录下,表示Tomcat的根目录。order文件夹存放的是订餐项目用到的所有数据,其中,order/img文件夹存放的是图片资源,包含店铺图片和菜单图片。shop_list_data.json文件中存放的是店铺列表与店铺详情界面的数据,数据的具体内容可参考教材。

注意

​ 上述文件中的IP地址需要修改为自己电脑上的IP地址,否则访问不到Tomcat服务器中的数据。

​ 如果想要启动Tomcat服务器,可以在apache-tomcat-8.5.59\bin包中找到startup.bat文件,双击该文件即可。

四、店铺功能业务实现

目标

  • 掌握店铺界面的开发过程,能够实现店铺界面的显示效果

​ 当打开仿美团外卖项目时,程序会直接进入主界面,也就是店铺列表界面。店铺列表界面从上至下分为标题栏、水平滑动广告栏和店铺列表三部分。其中,广告栏与店铺列表的数据是通过网络请求从服务器上获取的JSON数据,接下来本节将针对店铺功能的相关业务进行开发。

4.1 搭建标题栏布局

​ 在仿美团外卖项目中,大部分界面都有一个返回键和一个标题栏。为了便于代码重复利用,可以将返回键和标题栏抽取出来单独放在一个布局文件(title_bar.xml)中。

放置界面控件 res\layout\title_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/title_bar"android:layout_width="match_parent"android:layout_height="45dp"android:background="@android:color/transparent"><TextViewandroid:id="@+id/tv_back"android:layout_width="50dp"android:layout_height="50dp"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:background="@drawable/go_back_selector" /><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:textColor="@android:color/white"android:textSize="20sp" />
</RelativeLayout>

创建背景选择器 drawable\go_back_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@drawable/iv_back_selected" android:state_pressed="true"/><item android:drawable="@drawable/iv_back"/>
</selector>

4.2 搭建广告栏界面布局

​ 广告栏界面主要用于展示广告图片信息与跟随图片滑动的小圆点,当前显示的广告图片对应的小圆点颜色为白色,其余小圆点的颜色为灰色。

放置界面控件 res\layout\adbanner.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/adbanner_layout"android:layout_width="match_parent"android:layout_height="wrap_content"><android.support.v4.view.ViewPagerandroid:id="@+id/slidingAdvertBanner"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_alignParentLeft="true"android:layout_alignParentTop="true"android:layout_marginBottom="1dp"android:background="@android:color/black"android:gravity="center" /><cn.itcast.order.views.ViewPagerIndicatorandroid:id="@+id/advert_indicator"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:padding="10dp" />
</RelativeLayout>

自定义控件ViewPagerIndicator类 order\views\ViewPagerIndicator.java

package cn.itcast.order.views;import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;import cn.itcast.order.R;public class ViewPagerIndicator extends LinearLayout {private int mCount; //小圆点的个数private int mIndex; //当前小圆点的位置private Context context;public ViewPagerIndicator(Context context) {this(context, null);}public ViewPagerIndicator(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;}/*** 设置滑动到当前小圆点时其他圆点的位置*/public void setCurrentPostion(int currentIndex) {mIndex = currentIndex; //当前小圆点this.removeAllViews(); //移除界面上存在的viewint pex = context.getResources().getDimensionPixelSize(R.dimen.view_indicator_padding);for (int i = 0; i < this.mCount; i++) {//创建一个ImageView控件来放置小圆点ImageView imageView = new ImageView(context);if (mIndex == i) { //滑动到的当前界面//设置小圆点的图片为白色图片imageView.setImageResource(R.drawable.indicator_on);}else {//设置小圆点的图片为灰色图片imageView.setImageResource(R.drawable.indicator_off);}imageView.setPadding(pex, 0, pex, 0);//设置小圆点图片上下左右的paddingthis.addView(imageView);//把小圆点添加到自定义ViewPagerIndicator控件上}}/*** 设置小圆点的数目*/public void setCount(int count) {this.mCount = count;}
}

设置界面上圆点间的距离 res\values\dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="view_indicator_padding">5dp</dimen>
</resources>

白色和灰色的小圆点图片 res\drawable\indicator_on.xml和indicator_off.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval"><size android:height="6dp" android:width="6dp" /><solid android:color="@android:color/white" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="oval"><size android:height="6dp" android:width="6dp" /><solid android:color="#BCBCBC" />
</shape>

4.3 搭建店铺界面布局

​ 店铺界面是由一个标题栏、一个广告栏以及一个店铺列表组成,标题栏主要用于展示该界面的标题,广告栏主要用于展示店铺中的菜品广告图片,店铺列表主要用于展示各店铺的信息。

放置界面控件 res\layout\activity_shop.xml

<?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:background="@android:color/white"android:orientation="vertical"><include layout="@layout/title_bar" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="#f5f5f6"android:scrollbars="none"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><include layout="@layout/adbanner" /><cn.itcast.order.views.ShopListViewandroid:id="@+id/slv_list"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_marginLeft="8dp"android:layout_marginTop="8dp"android:layout_marginRight="8dp"android:dividerHeight="8dp"android:scrollbars="none" /></LinearLayout></ScrollView>
</LinearLayout>

创建自定义控件ShopListView order\views\ShopListView.java

package cn.itcast.order.views;import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;public class ShopListView extends ListView {public ShopListView(Context context) {super(context);}public ShopListView(Context context, AttributeSet attrs) {super(context, attrs);}public ShopListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, expandSpec);}
}

4.4 搭建店铺列表条目界面布局

​ 由于店铺界面使用自定义控件ShopListView展示店铺列表,所以需要创建一个该列表的条目界面。在条目界面中需要展示店铺名称、月销售商品的数量、起送价格、配送费用、店铺特色以及配送时间。

放置界面控件 res\layout\shop_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="8dp"android:layout_marginTop="8dp"android:layout_marginRight="8dp"android:background="@drawable/item_bg_selector"android:padding="10dp"><ImageViewandroid:id="@+id/iv_shop_pic"android:layout_width="100dp"android:layout_height="70dp"android:layout_alignParentLeft="true" /><LinearLayoutandroid:id="@+id/ll_info"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_toRightOf="@id/iv_shop_pic"android:orientation="vertical"><TextViewandroid:id="@+id/tv_shop_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_sale_num"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:textColor="@color/color_gray"android:textSize="12sp" /><TextViewandroid:id="@+id/tv_cost"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:textColor="@color/color_gray"android:textSize="12sp" /></LinearLayout><TextViewandroid:id="@+id/tv_feature"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/ll_info"android:layout_marginLeft="8dp"android:layout_marginTop="10dp"android:layout_toRightOf="@id/iv_shop_pic"android:background="@drawable/feature_bg"android:gravity="center"android:padding="4dp"android:textColor="@color/feature_text_color"android:textSize="12sp" /><TextViewandroid:id="@+id/tv_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_marginTop="30dp"android:textColor="@color/color_gray"android:textSize="12sp" />
</RelativeLayout>

创建店铺特色背景配置文件 res\drawable\feature_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><solid android:color="@color/feature_bg_color" /><corners android:radius="3dp" />
</shape>

创建商铺列表条目界面的背景选择器 res\drawable\item_bg_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_pressed="true" ><shape android:shape="rectangle"><corners android:radius="8dp"/><solid android:color="@color/item_bg_color"/></shape></item><item android:state_pressed="false" ><shape android:shape="rectangle"><corners android:radius="8dp"/><solid android:color="#ffffff" /></shape></item>
</selector>

4.5 封装店铺信息与菜品信息的实体类

创建ShopBean类

​ 由于店铺信息包含很多属性,因此,我们需要创建一个ShopBean类与封装店铺信息的属性。

 选中cn.itcast.order包,在该包下创建bean包,在bean包中创建一个ShopBean类。由于该类的对象中存储的信息需要在Activity之间进行传输,因此<font color='cornflowerblue'>将ShopBean类进行序列化,即实现Serializable接口。该类定义了店铺信息的所有属性。</font>
package cn.itcast.order.bean;import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;public class ShopBean implements Serializable {private static final long serialVersionUID = 1L; //序列化时保持ShopBean类版本的兼容性private int id;                               //店铺Idprivate String shopName;                  //店铺名称private int saleNum;                          //月售数量private BigDecimal offerPrice;            //起送价格private BigDecimal distributionCost;      //配送费用private String feature;                   //店铺特色private String time;                          //配送时间private String banner;                        //广告栏图片private String shopPic;                      //店铺图片private String shopNotice;                //店铺公告private List<FoodBean> foodList;        //菜单列表public int getId() {return id;}public void setId(int id) {this.id = id;}public String getShopName() {return shopName;}public void setShopName(String shopName) {this.shopName = shopName;}public int getSaleNum() {return saleNum;}public void setSaleNum(int saleNum) {this.saleNum = saleNum;}public BigDecimal getOfferPrice() {return offerPrice;}public void setOfferPrice(BigDecimal offerPrice) {this.offerPrice = offerPrice;}public BigDecimal getDistributionCost() {return distributionCost;}public void setDistributionCost(BigDecimal distributionCost) {this.distributionCost = distributionCost;}public String getFeature() {return feature;}public void setFeature(String feature) {this.feature = feature;}public String getTime() {return time;}public void setTime(String time) {this.time = time;}public String getShopPic() {return shopPic;}public String getBanner() {return banner;}public void setBanner(String banner) {this.banner = banner;}public void setShopPic(String shopPic) {this.shopPic = shopPic;}public String getShopNotice() {return shopNotice;}public void setShopNotice(String shopNotice) {this.shopNotice = shopNotice;}public List<FoodBean> getFoodList() {return foodList;}public void setFoodList(List<FoodBean> foodList) {this.foodList = foodList;}
}

创建FoodBean类

​ 由于菜单列表包含很多属性,因此,我们需要创建一个FoodBean类封装菜单信息的属性。

 在cn.itcast.order.bean包中<font color='cornflowerblue'>创建一个FoodBean类并实现Serializable接口,该类中定义了每个菜的所有属性。</font>
package cn.itcast.order.bean;import java.io.Serializable;
import java.math.BigDecimal;public class FoodBean implements Serializable {private static final long serialVersionUID = 1L; //序列化时保持FoodBean类版本的兼容性private int foodId;           //菜品Idprivate String foodName;      //菜品名称private String popularity;    //人气private String saleNum;         //月售量private BigDecimal price;      //价格private int count;              //添加到购物车中的数量private String foodPic;        //菜品图片public int getFoodId() {return foodId;}public void setFoodId(int foodId) {this.foodId = foodId;}public String getFoodName() {return foodName;}public void setFoodName(String foodName) {this.foodName = foodName;}public String getPopularity() {return popularity;}public void setPopularity(String popularity) {this.popularity = popularity;}public String getSaleNum() {return saleNum;}public void setSaleNum(String saleNum) {this.saleNum = saleNum;}public BigDecimal getPrice() {return price;}public void setPrice(BigDecimal price) {this.price = price;}public String getFoodPic() {return foodPic;}public void setFoodPic(String foodPic) {this.foodPic = foodPic;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}
}

4.6 编写广告栏的适配器

​ 店铺界面上的广告栏用到了ViewPager控件,为了给该控件填充数据,我们需要创建一个数据适配器AdBannerAdapter将获取到的数据传递到创建的AdBannerFragment中,AdBannerFragment用于将接收到的数据设置到ViewPager控件上。具体步骤如下:

1. 编写数据适配器AdBannerAdapter

在cn.itcast.order包中创建一个adapter包,并在该包中创建一个数据适配器AdBannerAdapter。

package cn.itcast.order.adapter;import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;import java.util.ArrayList;
import java.util.List;import cn.itcast.order.bean.ShopBean;
import cn.itcast.order.fragment.AdBannerFragment;public class AdBannerAdapter extends FragmentStatePagerAdapter {private List<ShopBean> sbl;public AdBannerAdapter(FragmentManager fm) {super(fm);sbl = new ArrayList<>();}/***  获取数据并更新界面*/public void setData(List<ShopBean> sbl) {this.sbl = sbl;          //接受从ShopActivity中传递过来的店铺数据集合sblnotifyDataSetChanged();  //更新界面数据}@Overridepublic Fragment getItem(int index) {Bundle args = new Bundle();if (sbl.size() > 0)
//            传递店铺数据
//            ad            表示传递数据的key值
//            sbl.get(index % sbl.size())       由于广告栏一直循环滑动,所以采取求余法来获取对应位置的店铺数据args.putSerializable("ad", sbl.get(index % sbl.size()));    return AdBannerFragment.newInstance(args);}@Overridepublic int getCount() {return Integer.MAX_VALUE;}/*** 返回数据集中元素的数量*/public int getSize() {return sbl == null ? 0 : sbl.size();}@Overridepublic int getItemPosition(Object object) {//防止刷新结果显示列表的时候出现缓存数据,重载这个函数,使之默认返回POSITION_NONEreturn POSITION_NONE;}
}

2. 将数据设置到广告栏界面上

(1)添加框架glide-3.7.0.jar

(2)创建AdBannerFragment

package cn.itcast.order.fragment;import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;import com.bumptech.glide.Glide;import cn.itcast.order.R;
import cn.itcast.order.activity.ShopDetailActivity;
import cn.itcast.order.bean.ShopBean;public class AdBannerFragment extends Fragment {private ShopBean sb;   //广告private ImageView iv;  //图片public static AdBannerFragment newInstance(Bundle args) {AdBannerFragment af = new AdBannerFragment();af.setArguments(args);return af;}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Bundle arg = getArguments();sb = (ShopBean) arg.getSerializable("ad"); //获取一个店铺对象,即店铺广告图片对应的店铺数据}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);}@Overridepublic void onResume() {super.onResume();if (sb != null) {//调用Glide框架加载图片Glide.with(getActivity())    //上下文.load(sb.getBanner())   //网络图片数据.error(R.mipmap.ic_launcher)    //当网络图片加载失败时,界面上默认显示的图片.into(iv);              //显示广告图片的控件}}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {iv = new ImageView(getActivity()); //创建一个ImageView控件的对象,用于显示广告图片ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);iv.setLayoutParams(lp);                       //设置ImageView控件的宽高参数iv.setScaleType(ImageView.ScaleType.FIT_XY); //把图片填满整个控件
//      实现广告图片的点击事件iv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//跳转到店铺详情界面,同时将数据也传递到店铺详情界面if (sb == null) return;Intent intent = new Intent(getActivity(), ShopDetailActivity.class);intent.putExtra("shop", sb);getActivity().startActivity(intent);}});return iv;}
}

4.7 编写店铺列表适配器

​ 由于店铺界面的列表是用ShopListView控件展示的,所以需要创建一个数据适配器ShopAdapter对ShopListView控件进行数据适配。

​ 在cn.itcast.order.adapter包中创建一个店铺列表的适配器ShopAdapter,在该适配器中重写getCount()方法、getItem()方法、getItemId()方法和getView()方法,这些方法分别用于获取列表中条目的总数、对应的条目对象、条目对象的Id、对应的条目视图。为了减少程序的缓存,需要在getView()方法中复用convertView对象。

package cn.itcast.order.adapter;import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;import com.bumptech.glide.Glide;import java.util.List;import cn.itcast.order.R;
import cn.itcast.order.activity.ShopDetailActivity;
import cn.itcast.order.bean.ShopBean;public class ShopAdapter extends BaseAdapter {private Context mContext;private List<ShopBean> sbl;public ShopAdapter(Context context) {this.mContext = context;}/*** 获取数据并更新界面*/public void setData(List<ShopBean> sbl) {this.sbl = sbl;notifyDataSetChanged();}/*** 获取条目的总数*/@Overridepublic int getCount() {return sbl == null ? 0 : sbl.size();}/*** 根据position得到对应条目的对象*/@Overridepublic ShopBean getItem(int position) {return sbl == null ? null : sbl.get(position);}/*** 根据position得到对应条目的Id*/@Overridepublic long getItemId(int position) {return position;}/*** 得到相应position对应的条目视图,position是当前条目的位置,* convertView参数是滚出屏幕的条目视图*/@Overridepublic View getView(int position, View convertView, ViewGroup parent) {final ViewHolder vh;//复用convertViewif (convertView == null) {vh = new ViewHolder();
//            inflate()加载条目布局文件shop_item.xmlconvertView=LayoutInflater.from(mContext).inflate(R.layout.shop_item,null);vh.tv_shop_name = convertView.findViewById(R.id.tv_shop_name);vh.tv_sale_num = convertView.findViewById(R.id.tv_sale_num);vh.tv_cost =  convertView.findViewById(R.id.tv_cost);vh.tv_feature = convertView.findViewById(R.id.tv_feature);vh.tv_time = convertView.findViewById(R.id.tv_time);vh.iv_shop_pic = convertView.findViewById(R.id.iv_shop_pic);convertView.setTag(vh);} else {vh = (ViewHolder) convertView.getTag();}//获取position对应条目的数据对象final ShopBean bean = getItem(position);if (bean != null) {//            将文本信息设置到界面控件上vh.tv_shop_name.setText(bean.getShopName());vh.tv_sale_num.setText("月售" + bean.getSaleNum());vh.tv_cost.setText("起送¥" + bean.getOfferPrice() + " | 配送¥" +bean.getDistributionCost());vh.tv_time.setText(bean.getTime());vh.tv_feature.setText(bean.getFeature());
//            将店铺图片设置到iv_shop_pic控件上Glide.with(mContext).load(bean.getShopPic()).error(R.mipmap.ic_launcher).into(vh.iv_shop_pic);}//每个条目的点击事件convertView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//跳转到店铺详情界面if (bean == null) return;Intent intent = new Intent(mContext,ShopDetailActivity.class);//把店铺的详细信息传递到店铺详情界面intent.putExtra("shop", bean);mContext.startActivity(intent);}});return convertView;}class ViewHolder {public TextView tv_shop_name, tv_sale_num, tv_cost, tv_feature, tv_time;public ImageView iv_shop_pic;}
}

4.8 实现店铺界面显示功能

实现店铺界面显示功能的具体步骤如下:

  • 添加okhttp库

​ 由于仿美团外卖项目中需要使用OkHttpClient类向服务器请求数据,所以需要将okhttp库添加到项目中。

  • 添加gson库

    由于仿美团外卖项目中需要用gson库解析获取到的JSON数据,所以需要将gson库添加到项目中。

  • 创建Constant类

    由于仿美团外卖项目中的数据需要通过请求网络从Tomcat(一个小型服务器)上获取,所以需要创建一个Constant类存放各界面从服务器上请求数据时使用的接口地址。

package cn.itcast.order.utils;
public class Constant {public static final String WEB_SITE = "http://192.168.0.193:8080/order";//内网接口,改为自己电脑的实际ippublic static final String REQUEST_SHOP_URL = "/shop_list_data.json";  //店铺列表接口
}
  • 创建JsonParse类

由于从Tomcat服务器上获取的店铺数据是JSON类型的数据,JSON数据不能直接显示到界面上,所以需要在cn.itcast.order.utils包中创建一个JsonParse类用于解析获取到的JSON数据。

package cn.itcast.order.utils;import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;import java.lang.reflect.Type;
import java.util.List;import cn.itcast.order.bean.ShopBean;public class JsonParse {private static JsonParse instance;private JsonParse() {}public static JsonParse getInstance() {if (instance == null) {instance = new JsonParse();}return instance;}public List<ShopBean> getShopList(String json) {Gson gson = new Gson(); // 使用gson库解析JSON数据// 创建一个TypeToken的匿名子类对象,并调用对象的getType()方法Type listType = new TypeToken<List<ShopBean>>() {}.getType();// 把获取到的信息集合存到shopList中List<ShopBean> shopList = gson.fromJson(json, listType);return shopList;}
}
  • 将数据显示到店铺界面上

    (1)初始化界面控件

    (2)获取界面数据

    (3)显示广告栏数据

    (4)退出当前应用程序

package cn.itcast.order.activity;import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;import java.io.IOException;
import java.util.List;import cn.itcast.order.R;
import cn.itcast.order.adapter.AdBannerAdapter;
import cn.itcast.order.adapter.ShopAdapter;
import cn.itcast.order.bean.ShopBean;
import cn.itcast.order.utils.Constant;
import cn.itcast.order.utils.JsonParse;
import cn.itcast.order.views.ShopListView;
import cn.itcast.order.views.ViewPagerIndicator;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;public class ShopActivity extends AppCompatActivity {private TextView tv_back, tv_title;        //返回键与标题控件private ShopListView slv_list;             //列表控件private ShopAdapter adapter;               //列表的适配器private RelativeLayout rl_title_bar;private ViewPager adPager;         //广告private ViewPagerIndicator vpi;   //小圆点private View adBannerLay;          //广告条容器private AdBannerAdapter ada;      //适配器public static final int MSG_AD_SLID = 1;  //广告自动滑动public static final int MSG_SHOP_OK = 2;  //获取数据private MHandler mHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shop);mHandler = new MHandler();initData();init();}/*** 初始化界面控件*/private void init() {tv_back = findViewById(R.id.tv_back);tv_title = findViewById(R.id.tv_title);tv_title.setText("店铺");rl_title_bar = findViewById(R.id.title_bar);rl_title_bar.setBackgroundColor(getResources().getColor(R.color.blue_color));tv_back.setVisibility(View.GONE);slv_list = findViewById(R.id.slv_list);adapter = new ShopAdapter(this);slv_list.setAdapter(adapter);adBannerLay = findViewById(R.id.adbanner_layout);adPager = findViewById(R.id.slidingAdvertBanner);vpi = findViewById(R.id.advert_indicator);adPager.setLongClickable(false);ada = new AdBannerAdapter(getSupportFragmentManager());adPager.setAdapter(ada);
//        实现小圆点的颜色效果adPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {@Overridepublic void onPageSelected(int index) {if (ada.getSize() > 0) {vpi.setCurrentPostion(index % ada.getSize()); //设置当前小圆点}}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic void onPageScrollStateChanged(int arg0) {}});resetSize();new AdAutoSlidThread().start();}//    实现广告的自动切换效果class AdAutoSlidThread extends Thread {@Overridepublic void run() {super.run();while (true) {try {sleep(5000); //睡眠5秒} catch (InterruptedException e) {e.printStackTrace();}if (mHandler != null)mHandler.sendEmptyMessage(MSG_AD_SLID);}}}private void initData() {OkHttpClient okHttpClient = new OkHttpClient();Request request = new Request.Builder().url(Constant.WEB_SITE +Constant.REQUEST_SHOP_URL).build();Call call = okHttpClient.newCall(request);// 开启异步线程访问网络call.enqueue(new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {String res = response.body().string(); //获取店铺数据Message msg = new Message();msg.what = MSG_SHOP_OK;msg.obj = res;mHandler.sendMessage(msg);}@Overridepublic void onFailure(Call call, IOException e) {}});}/*** 事件捕获*/class MHandler extends Handler {@Overridepublic void dispatchMessage(Message msg) {super.dispatchMessage(msg);switch (msg.what) {case MSG_SHOP_OK:if (msg.obj != null) {String vlResult = (String) msg.obj;//getShopList()解析获取的JSON数据,传递到sbl中List<ShopBean> sbl = JsonParse.getInstance().getShopList(vlResult);
//                        将集合数据sbl传递到店铺列表的数据适配器ShopAdapter中adapter.setData(sbl);if (sbl != null) {if (sbl.size() > 0) {ada.setData(sbl); //设置广告栏数据到界面上vpi.setCount(sbl.size()); //设置小圆点数目vpi.setCurrentPostion(0); //设置当前小圆点的位置为0}}}break;case MSG_AD_SLID:if (ada.getCount() > 0) {//设置滑动到下一张广告图片adPager.setCurrentItem(adPager.getCurrentItem() + 1);}break;}}}/*** 计算控件大小*/private void resetSize() {int sw = getScreenWidth();//获取屏幕宽度int adLheight = sw / 3; //广告条高度ViewGroup.LayoutParams adlp = adBannerLay.getLayoutParams();adlp.width = sw;adlp.height = adLheight;adBannerLay.setLayoutParams(adlp);}/*** 获取屏幕宽度*/public int getScreenWidth() {WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);return outMetrics.widthPixels;}protected long exitTime;//记录第一次点击时的时间//    实现点击两次返回键的时间间隔小于或等于2秒时,退出仿美团外卖应用程序的功能@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {//        keyCode               判断值是否为用户点击了返回键
//        event.getAction()     判断返回键是否处于被按下的状态if (keyCode == KeyEvent.KEYCODE_BACK&& event.getAction() == KeyEvent.ACTION_DOWN) {//            判断两次按下返回键的时间是否大于2秒
//            若大于,则提示用户"再按一次退出仿美团外卖应用",并保存当前时间
//            否则,调用finish()方法关闭当前页面,同时调用exit(0)方法退出当前应用程序if ((System.currentTimeMillis() - exitTime) > 2000) {Toast.makeText(ShopActivity.this, "再按一次退出仿美团外卖应用",Toast.LENGTH_SHORT).show();exitTime = System.currentTimeMillis();} else {ShopActivity.this.finish();System.exit(0);}return true;}return super.onKeyDown(keyCode, event);}
}
  • 修改colors.xml文件

    由于店铺界面的标题栏背景颜色为蓝色的,为了便于颜色的管理,所以需要在res/values文件夹中的colors.xml文件中添加一个蓝色的颜色值。

五、店铺详情功能业务实现

目标

  • 掌握店铺详情界面与购物车的开发过程,能够独立实现购物车功能

​ 当店铺列表界面的条目被点击后,程序会跳转到店铺详情界面,该界面主要分为三个部分,其中第一部分用于展示店铺的信息,如店铺名称、店铺图片、店铺公告以及配送时间,第二部分用于展示该店铺中的菜单列表,第三部分用于展示购物车。当点击菜单列表中的“加入购物车”按钮时,程序会将菜品添加到购物车中,此时点击购物车会弹出一个购物车列表,在该列表中可以添加和删除购物车中的菜品。本节将针对店铺详情功能业务的实现进行详细讲解。

5.1 搭建店铺详情界面布局

店铺详情界面

​ 在仿美团外卖的项目中,点击店铺列表条目时,程序会跳转到店铺详情界面,该界面主要用于展示店铺名称、店铺图片、配送时间、店铺公告、店铺的菜单列表、购物车以及购物车列表等信息。

搭建店铺详情界面布局的具体步骤如下:

放置界面控件 res\layout\activity_shop_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:id="@+id/ll_intro"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/shop_bg"android:orientation="vertical"><include layout="@layout/title_bar" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="65dp"android:background="@android:color/white"android:orientation="vertical"android:paddingTop="60dp"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:padding="10dp"android:text="菜单"android:textColor="@android:color/black"android:textSize="16sp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:background="@color/light_gray" /></LinearLayout></LinearLayout><include layout="@layout/shop_detail_head" /><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="220dp"><ListViewandroid:id="@+id/lv_list"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_alignParentBottom="true"android:layout_marginBottom="50dp" /><include layout="@layout/car_list" /><include layout="@layout/shop_car" /></RelativeLayout>
</FrameLayout>

放置店铺名称、图片、公告与配送信息 res\layout\shop_detail_head.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingTop="20dp"android:background="@android:color/transparent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_marginTop="50dp"android:background="@drawable/corner_bg"android:orientation="vertical"android:padding="10dp"><TextViewandroid:id="@+id/tv_shop_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:textColor="@android:color/black"android:textSize="18sp" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:orientation="horizontal"><ImageViewandroid:layout_width="15dp"android:layout_height="15dp"android:layout_alignParentLeft="true"android:scaleType="fitXY"android:src="@drawable/time_icon" /><TextViewandroid:id="@+id/tv_time"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="4dp"android:textColor="@color/color_gray"android:textSize="12sp" /></LinearLayout><TextViewandroid:id="@+id/tv_notice"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:textColor="@color/color_gray"android:textSize="12sp" /></LinearLayout><ImageViewandroid:id="@+id/iv_shop_pic"android:layout_width="85dp"android:layout_height="60dp"android:layout_alignParentRight="true"android:layout_margin="20dp"android:scaleType="fitXY" />
</RelativeLayout>

显示“未选购商品”,“去结算”,“不够起送价格”的文本信息与配送费信息 res\layout\shop_car.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="65dp"android:layout_alignParentBottom="true"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:layout_marginTop="15dp"android:background="@color/car_gray_color"android:paddingLeft="60dp"><TextViewandroid:id="@+id/tv_money"android:layout_width="wrap_content"android:layout_height="25dp"android:layout_marginLeft="4dp"android:layout_marginTop="4dp"android:gravity="center"android:text="未选购商品"android:textColor="@color/light_gray"android:textSize="16sp" /><TextViewandroid:id="@+id/tv_distribution_cost"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/tv_money"android:layout_marginLeft="4dp"android:textColor="@android:color/white"android:textSize="12sp"android:textStyle="bold"android:visibility="gone" /><TextViewandroid:id="@+id/tv_settle_accounts"android:layout_width="100dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:background="@color/account_color"android:gravity="center"android:text="去结算"android:textColor="@android:color/white"android:textSize="16sp"android:visibility="gone" /><TextViewandroid:id="@+id/tv_not_enough"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_alignParentRight="true"android:background="@color/account_gray_color"android:gravity="center"android:padding="8dp"android:textColor="@color/light_gray"android:textSize="16sp" /></RelativeLayout><include layout="@layout/car" /></FrameLayout>
</RelativeLayout>

显示购物车图片与购物车中商品的数量 res\layout\car.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:id="@+id/iv_shop_car"android:layout_width="55dp"android:layout_height="55dp"android:layout_alignParentStart="true"android:layout_marginLeft="8dp"android:src="@drawable/shop_car_empty" /><LinearLayoutandroid:layout_width="55dp"android:layout_height="55dp"android:gravity="right|top"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_count"style="@style/badge_style"android:layout_marginTop="4dp" /></LinearLayout>
</RelativeLayout>

显示“已选商品”,与“清空”的文本消息,以及购物车列表 res\layout\car_list.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/rl_car_list"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginBottom="65dp"android:background="@android:color/transparent"android:gravity="bottom"android:visibility="gone"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><LinearLayoutandroid:id="@+id/ll_intro"android:layout_width="match_parent"android:layout_height="32dp"android:background="@color/light_gray"android:gravity="center_vertical"android:orientation="horizontal"android:paddingLeft="8dp"android:paddingRight="8dp"><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginLeft="5dp"android:layout_weight="1"android:text="已选商品"android:textColor="@color/color_gray"android:textSize="12sp"android:textStyle="bold" /><TextViewandroid:id="@+id/tv_clear"android:layout_width="wrap_content"android:layout_height="match_parent"android:drawableLeft="@drawable/icon_clear"android:drawablePadding="2dp"android:gravity="center"android:text="清空"android:textColor="@color/color_gray"android:textSize="12sp" /></LinearLayout><ListViewandroid:id="@+id/lv_car"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/white"/></LinearLayout>
</RelativeLayout>

设置一个边角为圆角的矩形 res\drawable\corner_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><solid android:color="#FFFFFF" /><corners android:radius="3dp" /><!--定义矩形的四条边的宽度和颜色--><strokeandroid:width="1dp"android:color="@color/light_gray" />
</shape>

购物车右上角有一个显示商品数量的控件,

设置该控件的背景为一个红色的圆形 res\drawable\badge_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><!--gradient      用于定义矩形中的渐变色type="linear" 线性渐变    --><gradientandroid:endColor="#fe451d"android:startColor="#fe957f"android:type="linear" /><corners android:radius="180dp" />
</shape>

购物车右上角有一个显示商品数量的控件,抽取该控件的属性样式 res\values\styles.xml

<style name="badge_style"><item name="android:layout_width">wrap_content</item><item name="android:layout_height">wrap_content</item><item name="android:minHeight">14dp</item><item name="android:minWidth">14dp</item><item name="android:paddingLeft">2dp</item><item name="android:paddingRight">2dp</item><item name="android:textColor">@android:color/white</item><item name="android:visibility">gone</item><item name="android:gravity">center</item><item name="android:background">@drawable/badge_bg</item><item name="android:textStyle">bold</item><item name="android:textSize">10sp</item>
</style>

5.2 搭建菜单列表条目界面布局

​ 在店铺详情界面中有一个菜单列表,该列表是用ListView控件来展示菜单信息的,所以需要创建一个该列表的条目界面,在条目界面中需要展示菜品的名称、人气、月售数量、价格以及“加入购物车”按钮。

创建菜单列表条目界面布局文件 res\layout\menu_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="8dp"android:layout_marginTop="8dp"android:layout_marginRight="8dp"android:background="@drawable/menu_item_bg_selector"android:padding="10dp"><ImageViewandroid:id="@+id/iv_food_pic"android:layout_width="80dp"android:layout_height="60dp"android:layout_alignParentLeft="true" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_toRightOf="@id/iv_food_pic"android:orientation="vertical"><TextViewandroid:id="@+id/tv_food_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_popularity"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:background="@drawable/feature_bg"android:padding="4dp"android:textColor="@color/feature_text_color"android:textSize="12sp" /><TextViewandroid:id="@+id/tv_sale_num"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:textColor="@color/color_gray"android:textSize="12sp" /><TextViewandroid:id="@+id/tv_price"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"android:textColor="@color/price_red"android:textSize="16sp" /></LinearLayout><Buttonandroid:id="@+id/btn_add_car"android:layout_width="65dp"android:layout_height="25dp"android:layout_alignParentRight="true"android:layout_marginTop="30dp"android:background="@drawable/add_car_selector"android:gravity="center"android:text="加入购物车"android:textColor="@android:color/white"android:textSize="10sp" />
</RelativeLayout>

创建菜单列表条目界面的背景选择器 res\drawable\menu_item_bg_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@color/item_bg_color" android:state_pressed="true"/><item android:drawable="@android:color/white"/>
</selector>

加入购物车的背景选择器 res\drawable\add_car_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@drawable/add_car_selected" android:state_pressed="true" /><item android:drawable="@drawable/add_car_normal" />
</selector>

5.3 搭建购物车列表条目界面布局

​ 购物车列表条目界面中需要展示菜品的名称、价格、数量、添加菜品的按钮以及删除菜品的按钮。

放置界面控件来显示“菜品名称”,“价格”,“数量”,“+”,“-”按钮 res\layout\car_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="60dp"android:gravity="center_vertical"android:orientation="vertical"android:padding="8dp"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_vertical"><TextViewandroid:id="@+id/tv_food_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="8dp"android:maxLines="1"android:textColor="#757575"android:textSize="16sp" /><ImageViewandroid:id="@+id/iv_add"android:layout_width="25dp"android:layout_height="25dp"android:layout_alignParentRight="true"android:src="@drawable/car_add" /><TextViewandroid:id="@+id/tv_food_count"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="10dp"android:layout_marginTop="4dp"android:layout_toLeftOf="@id/iv_add"android:textColor="@android:color/black" /><ImageViewandroid:id="@+id/iv_minus"android:layout_width="25dp"android:layout_height="25dp"android:layout_marginRight="10dp"android:layout_toLeftOf="@id/tv_food_count"android:src="@drawable/car_minus" /><TextViewandroid:id="@+id/tv_food_price"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="10dp"android:layout_marginTop="4dp"android:layout_toLeftOf="@id/iv_minus"android:textColor="@color/price_red"android:textSize="14sp"android:textStyle="bold" /></RelativeLayout>
</LinearLayout>

实现点击购物车时的弹出动画效果 res\anim\slide_bottom_to_top.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_interpolator">
<!--translate        实现界面的平移的动画效果fromYDelta      指定动画开始时界面在y轴上的坐标-->    <translateandroid:duration="500"android:fromYDelta="100.0%"android:toYDelta="10.000002%" /><!--alpha            实现界面透明度的渐变动画fromAlpha       指定动画的起始透明度-->  <alphaandroid:duration="500"android:fromAlpha="0.0"android:toAlpha="1.0" />
</set>

5.4 搭建确认清空购物车界面布局

​ 在购物车列表界面的右上角有一个清空购物车的图标,点击该图标会弹出一个确认清空购物车的对话框界面,该界面主要用于展示“确认清空购物车?”的文本、取消按钮和清空按钮。

放置界面控件 res\layout\dialog_clear.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="250dp"android:layout_height="100dp"android:background="@android:color/white"android:gravity="center"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_horizontal"android:text="确认清空购物车?"android:textColor="@color/color_gray"android:textSize="16sp" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:gravity="center_horizontal"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_cancel"android:layout_width="80dp"android:layout_height="30dp"android:background="@drawable/item_bg_selector"android:gravity="center"android:text="取消"android:textColor="@color/blue_color"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_clear"android:layout_width="80dp"android:layout_height="30dp"android:layout_marginLeft="20dp"android:background="@drawable/item_bg_selector"android:gravity="center"android:text="清空"android:textColor="@color/blue_color"android:textSize="14sp" /></LinearLayout>
</LinearLayout>

创建清空对话框的样式 res\values\styles.xml

<style name="Dialog_Style" parent="@android:style/Theme.Dialog"><!--设置界面无标题栏--><item name="android:windowIsFloating">true</item>     <!--对话框浮在Activity之上--><item name="android:windowIsTranslucent">true</item> <!--设置对话框背景为透明--><item name="android:windowNoTitle">true</item>         <!--设置界面无标题--><!--设置窗体背景透明--><item name="android:windowBackground">@android:color/transparent</item><!--设置对话框内容背景透明--><item name="android:background">@android:color/transparent</item><!--设置对话框背景有半透明遮障层--><item name="android:backgroundDimEnabled">true</item></style>

5.5 编写菜单列表适配器

 由于店铺详情界面中的菜单列表是用ListView控件展示的,所以需要创建一个数据适配器MenuAdapter对ListView控件进行数据适配。编写菜单列表适配器的具体步骤如下:

1. 创建菜单列表适配器MenuAdapter

选中cn.itcast.order.adapter包,在该包中<font color='cornflowerblue'>创建一个菜单列表适配器MenuAdapter</font>,并在该适配器中重写getCount()方法、getItem()方法、getItemId()方法和getView()方法。

2. 创建ViewHolder类

<font color='cornflowerblue'>在MenuAdapter中创建ViewHolder类,该类主要用于定义菜单列表条目上的控件对象</font>,当菜单列表快速滑动时,该类可以快速为界面控件设置值,而不必每次重新创建很多控件对象,从而有效提高程序的性能。

3. 创建OnSelectListener接口

​ 当点击菜单列表上的“加入购物车”按钮时,会增加购物车中菜品的数量,该数量的增加需要在ShopDetailActivity中进行,所以需要在MenuAdapter中创建一个OnSelectListener接口,在该接口中创建一个onSelectAddCar()方法用于处理“加入购物车”按钮的点击事件。

package cn.itcast.order.adapter;import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;import com.bumptech.glide.Glide;import java.util.List;import cn.itcast.order.R;
import cn.itcast.order.activity.FoodActivity;
import cn.itcast.order.bean.FoodBean;public class MenuAdapter extends BaseAdapter {private Context mContext;private List<FoodBean> fbl;                   //菜单列表数据private OnSelectListener onSelectListener; //加入购物车按钮的监听事件public MenuAdapter(Context context, OnSelectListener onSelectListener) {this.mContext = context;this.onSelectListener=onSelectListener;}/*** 设置数据更新界面*/public void setData(List<FoodBean> fbl) {this.fbl = fbl;notifyDataSetChanged();}/*** 获取条目的总数*/@Overridepublic int getCount() {return fbl == null ? 0 : fbl.size();}/*** 根据position得到对应条目的对象*/@Overridepublic FoodBean getItem(int position) {return fbl == null ? null : fbl.get(position);}/*** 根据position得到对应条目的Id*/@Overridepublic long getItemId(int position) {return position;}/*** 得到相应position对应的条目视图,position是当前条目的位置,* convertView参数是滚出屏幕的条目视图*/@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {final ViewHolder vh;//复用convertViewif (convertView == null) {vh = new ViewHolder();convertView = LayoutInflater.from(mContext).inflate(R.layout.menu_item,null);vh.tv_food_name = convertView.findViewById(R.id.tv_food_name);vh.tv_popularity =  convertView.findViewById(R.id.tv_popularity);vh.tv_sale_num = convertView.findViewById(R.id.tv_sale_num);vh.tv_price =  convertView.findViewById(R.id.tv_price);vh.btn_add_car = convertView.findViewById(R.id.btn_add_car);vh.iv_food_pic =  convertView.findViewById(R.id.iv_food_pic);convertView.setTag(vh);} else {vh = (ViewHolder) convertView.getTag();}//获取position对应条目的数据对象final FoodBean bean = getItem(position);if (bean != null) {vh.tv_food_name.setText(bean.getFoodName());vh.tv_popularity.setText(bean.getPopularity ());vh.tv_sale_num.setText("月售" + bean.getSaleNum());vh.tv_price.setText("¥"+bean.getPrice());Glide.with(mContext).load(bean.getFoodPic()).error(R.mipmap.ic_launcher).into(vh.iv_food_pic);}//每个条目的点击事件convertView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//跳转到菜品详情界面if (bean == null)return;Intent intent = new Intent(mContext,FoodActivity.class);//把菜品的详细信息传递到菜品详情界面intent.putExtra("food", bean);mContext.startActivity(intent);}});vh.btn_add_car.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) { //加入购物车按钮的点击事件onSelectListener.onSelectAddCar(position);}});return convertView;}class ViewHolder {public TextView tv_food_name, tv_popularity, tv_sale_num, tv_price;public Button btn_add_car;public ImageView iv_food_pic;}public interface OnSelectListener {void onSelectAddCar (int position); //处理加入购物车按钮的方法}
}

5.6 编写购物车列表适配器

 由于店铺详情界面中的购物车列表是用ListView控件展示的,所以需要创建一个数据适配器CarAdapter对ListView控件进行数据适配。编写购物车列表适配器的具体步骤如下:

1. 创建购物车列表适配器CarAdapter

  选中cn.itcast.order.adapter包,在该包中<font color='cornflowerblue'>创建一个适配器CarAdapter</font>,在该适配器中重写getCount()方法、getItem()方法、getItemId()方法和getView()方法。

2. 创建ViewHolder类

  <font color='cornflowerblue'> 在CarAdapter中创建一个ViewHolder类</font>,该类主要用于创建购物车列表条目界面上的控件对象,当购物车列表快速滑动时,该类可以快速为界面控件设置值,而不必每次都重新创建很多控件对象,这样可以提高程序的性能。

3. 创建OnSelectListener接口

  当点击购物车列表界面的添加或减少菜品数量的按钮时,购物车中菜品的数量会随之变化,该数量的变化需要在ShopDetailActivity中进行,因此需要<font color='cornflowerblue'>在CarAdapter中创建一个OnSelectListener接口,在该接口中创建onSelectAdd()方法与onSelectMis()方法</font>,分别用于处理增加或减少菜品数量按钮的点击事件。
package cn.itcast.order.adapter;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;import java.math.BigDecimal;
import java.util.List;import cn.itcast.order.R;
import cn.itcast.order.bean.FoodBean;public class CarAdapter extends BaseAdapter {private Context mContext;private List<FoodBean> fbl;private OnSelectListener onSelectListener;public CarAdapter(Context context, OnSelectListener onSelectListener) {this.mContext = context;this.onSelectListener=onSelectListener;}/*** 设置数据更新界面*/public void setData(List<FoodBean> fbl) {this.fbl = fbl;notifyDataSetChanged();}/*** 获取条目的总数*/@Overridepublic int getCount() {return fbl == null ? 0 : fbl.size();}/*** 根据position得到对应条目的对象*/@Overridepublic FoodBean getItem(int position) {return fbl == null ? null : fbl.get(position);}/*** 根据position得到对应条目的Id*/@Overridepublic long getItemId(int position) {return position;}/*** 得到相应position对应的条目视图,position是当前条目的位置,* convertView参数是滚出屏幕的条目视图*/@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {final ViewHolder vh;//复用convertViewif (convertView == null) {vh = new ViewHolder();convertView = LayoutInflater.from(mContext).inflate(R.layout.car_item, null);vh.tv_food_name =  convertView.findViewById(R.id.tv_food_name);vh.tv_food_count = convertView.findViewById(R.id.tv_food_count);vh.tv_food_price =  convertView.findViewById(R.id.tv_food_price);vh.iv_add =  convertView.findViewById(R.id.iv_add);vh.iv_minus = convertView.findViewById(R.id.iv_minus);convertView.setTag(vh);} else {vh = (ViewHolder) convertView.getTag();}//获取position对应的条目数据对象final FoodBean bean = getItem(position);if (bean != null) {vh.tv_food_name.setText(bean.getFoodName());vh.tv_food_count.setText(bean.getCount()+"");BigDecimal count=BigDecimal.valueOf(bean.getCount());vh.tv_food_price.setText("¥" + bean.getPrice().multiply(count));}vh.iv_add.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {onSelectListener.onSelectAdd(position,vh.tv_food_count,vh.tv_food_price);}});vh.iv_minus.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {onSelectListener.onSelectMis(position,vh.tv_food_count,vh.tv_food_price);}});return convertView;}class ViewHolder {public TextView tv_food_name, tv_food_count,tv_food_price;public ImageView iv_add,iv_minus;}public interface OnSelectListener {void onSelectAdd(int position,TextView tv_food_price,TextView tv_food_count);void onSelectMis(int position,TextView tv_food_price,TextView tv_food_count);}
}

5.7 实现菜单显示与购物车功能

​ 店铺详情界面主要是展示店铺信息、菜单列表信息以及购物车信息,其中在菜单列表中可以点击“加入购物车”按钮,将菜品添加到购物车中。此时点击购物车图片会从界面底部弹出一个购物车列表,该列表显示的是购物车中添加的菜品信息,这些菜品信息在列表中可以进行增加和删除。点击购物车列表右上角的“清空”按钮,程序会弹出一个确认清空购物车的对话框,点击对话框中的“清空”按钮会清空购物车中的数据。

​ 实现菜单显示与购物车功能的具体步骤如下:

1. 获取界面控件

​ 在ShopDetailActivity中创建initView()方法,该方法用于初始化界面控件。

2. 实现添加与删除购物车中的菜品

​ 在ShopDetailActivity中创建initAdapter()方法,该方法用于添加与删除购物中的菜品。

3. 设置界面数据

​ 在ShopDetailActivity中创建一个setData ()方法,用于设置界面数据,具体代码如文件12-36所示。

package cn.itcast.order.activity;
import android.app.Dialog;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import cn.itcast.order.R;
import cn.itcast.order.adapter.CarAdapter;
import cn.itcast.order.adapter.MenuAdapter;
import cn.itcast.order.bean.FoodBean;
import cn.itcast.order.bean.ShopBean;
public class ShopDetailActivity extends AppCompatActivity implements View.OnClickListener{private ShopBean bean;private TextView tv_shop_name, tv_time, tv_notice, tv_title, tv_back,tv_settle_accounts, tv_count, tv_money, tv_distribution_cost,tv_not_enough, tv_clear;private ImageView iv_shop_pic, iv_shop_car;private ListView lv_list, lv_car;public static final int MSG_COUNT_OK = 1;// 获取购物车中商品的数量private MHandler mHandler;private int totalCount = 0;private BigDecimal totalMoney;            //购物车中菜品的总价格private List<FoodBean> carFoodList;      //购物车中的菜品数据private MenuAdapter adapter;private CarAdapter carAdapter;private RelativeLayout rl_car_list;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_shop_detail);//获取店铺详情数据bean = (ShopBean) getIntent().getSerializableExtra("shop");if (bean == null) return;mHandler = new MHandler();totalMoney = new BigDecimal(0.0);//初始化变量totalMoneycarFoodList = new ArrayList<>(); //初始化集合carFoodListinitView();     //初始化界面控件initAdapter(); //初始化adaptersetData();      //设置界面数据}/*** 初始化界面控件*/private void initView() {tv_back =  findViewById(R.id.tv_back);tv_title =  findViewById(R.id.tv_title);tv_title.setText("店铺详情");tv_shop_name = findViewById(R.id.tv_shop_name);tv_time = findViewById(R.id.tv_time);tv_notice = findViewById(R.id.tv_notice);iv_shop_pic = findViewById(R.id.iv_shop_pic);lv_list = findViewById(R.id.lv_list);tv_settle_accounts = findViewById(R.id.tv_settle_accounts);tv_distribution_cost = findViewById(R.id.tv_distribution_cost);tv_count = findViewById(R.id.tv_count);iv_shop_car = findViewById(R.id.iv_shop_car);tv_money = findViewById(R.id.tv_money);tv_not_enough = findViewById(R.id.tv_not_enough);tv_clear = findViewById(R.id.tv_clear);lv_car =  findViewById(R.id.lv_car);rl_car_list = findViewById(R.id.rl_car_list);//点击购物车列表界面外的其他部分会隐藏购物车列表界面rl_car_list.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {if (rl_car_list.getVisibility() == View.VISIBLE) {rl_car_list.setVisibility(View.GONE);}return false;}});//设置返回键、去结算按钮、购物车图片、清空购物车按钮的点击监听事件tv_back.setOnClickListener(this);tv_settle_accounts.setOnClickListener(this);iv_shop_car.setOnClickListener(this);tv_clear.setOnClickListener(this);}/*** 初始化adapter*/private void initAdapter(){carAdapter = new CarAdapter(this, new CarAdapter.OnSelectListener() {@Overridepublic void onSelectAdd(int position, TextView tv_food_count,TextView tv_food_price) {//添加菜品到购物车中FoodBean bean = carFoodList.get(position);        //获取当前菜品对象//设置该菜品在购物车中的数量tv_food_count.setText(bean.getCount() + 1 + "");BigDecimal count = BigDecimal.valueOf(bean.getCount() + 1);//菜品总价格tv_food_price.setText("¥" + bean.getPrice().multiply(count));//将当前菜品在购物车中的数量设置给菜品对象bean.setCount(bean.getCount() + 1);Iterator<FoodBean> iterator = carFoodList.iterator();while (iterator.hasNext()) {//遍历购物车中的数据FoodBean food = iterator.next();if (food.getFoodId() == bean.getFoodId()) {//找到当前菜品iterator.remove();   //删除购物车中当前菜品的旧数据}}//将当前菜品的最新数据添加到购物车数据集合中carFoodList.add(position, bean);totalCount = totalCount + 1;      //购物车中菜品的总数量+1//购物车中菜品的总价格+当前菜品价格totalMoney = totalMoney.add(bean.getPrice());//将购物车中菜品的总数量和总价格通过Handler传递到主线程中carDataMsg();}@Overridepublic void onSelectMis(int position, TextView tv_food_count, TextViewtv_food_price) {FoodBean bean = carFoodList.get(position);       //获取当前菜品对象//设置当前菜品的数量tv_food_count.setText(bean.getCount() - 1 + "");BigDecimal count = BigDecimal.valueOf(bean.getCount() - 1);//设置当前菜品总价格,菜品价格=菜品单价*菜品数量tv_food_price.setText("¥" + bean.getPrice().multiply(count));minusCarData(bean, position);//删除购物车中的菜品}});adapter = new MenuAdapter(this, new MenuAdapter.OnSelectListener() {@Overridepublic void onSelectAddCar(int position) {//点击加入购物车按钮将菜添加到购物车中FoodBean fb = bean.getFoodList().get(position);fb.setCount(fb.getCount() + 1);Iterator<FoodBean> iterator = carFoodList.iterator();while (iterator.hasNext()) {FoodBean food = iterator.next();if (food.getFoodId() == fb.getFoodId()) {iterator.remove();}}carFoodList.add(fb);totalCount = totalCount + 1;totalMoney = totalMoney.add(fb.getPrice());carDataMsg();}});lv_list.setAdapter(adapter);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.tv_back:               //返回按钮的点击事件finish();break;case R.id.tv_settle_accounts: //去结算按钮的点击事件//跳转到订单界面if (totalCount > 0) {Intent intent = new Intent(ShopDetailActivity.this, OrderActivity.class);intent.putExtra("carFoodList", (Serializable) carFoodList);intent.putExtra("totalMoney", totalMoney + "");intent.putExtra("distributionCost", bean.getDistributionCost() + "");startActivity(intent);}break;case R.id.iv_shop_car:          //购物车的点击事件if (totalCount <= 0) return;if (rl_car_list != null) {if (rl_car_list.getVisibility() == View.VISIBLE) {rl_car_list.setVisibility(View.GONE);} else {rl_car_list.setVisibility(View.VISIBLE);//创建一个从底部滑出的动画Animation animation = AnimationUtils.loadAnimation(ShopDetailActivity.this, R.anim.slide_bottom_to_top);//将动画加载到购物车列表界面rl_car_list.startAnimation(animation);}}carAdapter.setData(carFoodList);lv_car.setAdapter(carAdapter);break;case R.id.tv_clear://清空按钮的点击事件dialogClear(); //弹出确认清空购物车的对话框break;}}/*** 弹出清空购物车的对话框*/private void dialogClear() {//创建一个对话框Dialogfinal Dialog dialog = new Dialog(ShopDetailActivity.this, R.style.Dialog_Style);dialog.setContentView(R.layout.dialog_clear); //将布局文件加载到对话框中dialog.show();                                     //显示对话框//获取对话框清除按钮TextView tv_clear = dialog.findViewById(R.id.tv_clear);//获取对话框取消按钮TextView tv_cancel = dialog.findViewById(R.id.tv_cancel);tv_cancel.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {dialog.dismiss();//关闭对话框}});tv_clear.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (carFoodList == null) return;for (FoodBean bean : carFoodList) {bean.setCount(0);//设置购物车中所有菜品的数量为0}carFoodList.clear();//清空购物车中的数据carAdapter.notifyDataSetChanged();    //更新界面totalCount = 0;      //购物车中菜品的数量设置为0totalMoney = BigDecimal.valueOf(0.0);//总价格设置为0carDataMsg();        //通过Handler更新购物车中菜品的数量和总价格dialog.dismiss();   //关闭对话框}});}/*** 将购物车中菜品的总数量和总价格通过Handler传递到主线程中*/private void carDataMsg() {Message msg = Message.obtain();msg.what = MSG_COUNT_OK;Bundle bundle = new Bundle();//创建一个Bundler对象//将购物车中的菜品数量和价格放入Bundler对象中bundle.putString("totalCount", totalCount + "");bundle.putString("totalMoney", totalMoney + "");msg.setData(bundle);        //将Bundler对象放入Message对象mHandler.sendMessage(msg); //将Message对象传递到MHandler类}/*** 删除购物车中的数据*/private void minusCarData(FoodBean bean, int position) {int count = bean.getCount() - 1; //将该菜品的数量减1bean.setCount(count);              //将减后的数量设置到菜品对象中Iterator<FoodBean> iterator = carFoodList.iterator();while (iterator.hasNext()) {     //遍历购物车中的菜FoodBean food = iterator.next();if (food.getFoodId() == bean.getFoodId()) {//找到购物车中当前菜的Iditerator.remove();         //删除存放的菜}}//如果当前菜品的数量减1之后大于0,则将当前菜品添加到购物车集合中if (count > 0) carFoodList.add(position, bean);else carAdapter.notifyDataSetChanged();totalCount = totalCount - 1; //购物车中菜品的数量减1//购物车中的总价钱=总价钱-当前菜品的价格totalMoney = totalMoney.subtract(bean.getPrice());carDataMsg();                  //调用该方法更新购物车中的数据}/*** 事件捕获*/class MHandler extends Handler {@Overridepublic void dispatchMessage(Message msg) {super.dispatchMessage(msg);switch (msg.what) {case MSG_COUNT_OK:Bundle bundle = msg.getData();String count = bundle.getString("totalCount", "");String money = bundle.getString("totalMoney", "");if (bundle != null) {if (Integer.parseInt(count) > 0) {//如果购物车中有菜品iv_shop_car.setImageResource(R.drawable.shop_car);tv_count.setVisibility(View.VISIBLE);tv_distribution_cost.setVisibility(View.VISIBLE);tv_money.setTextColor(Color.parseColor("#ffffff"));//加粗字体tv_money.getPaint().setFakeBoldText(true);//设置购物车中菜品总价格tv_money.setText("¥" + money);tv_count.setText(count);        //设置购物车中菜品总数量tv_distribution_cost.setText("另需配送费¥" +bean.getDistributionCost());//将变量money的类型转换为BigDecimal类型BigDecimal bdm = new BigDecimal(money);//总价格money与起送价格对比int result = bdm.compareTo(bean.getOfferPrice());if (-1 == result) { //总价格<起送价格//隐藏去结算按钮tv_settle_accounts.setVisibility(View.GONE);//显示差价文本tv_not_enough.setVisibility(View.VISIBLE);//差价=起送价格-总价格BigDecimal m = bean.getOfferPrice().subtract(bdm);tv_not_enough.setText("还差¥" + m + "起送");} else { //总价格>=起送价格//显示去结算按钮tv_settle_accounts.setVisibility(View.VISIBLE);//隐藏差价文本tv_not_enough.setVisibility(View.GONE);}} else { //如果购物车中没有菜品if (rl_car_list.getVisibility() == View.VISIBLE) {//隐藏购物车列表rl_car_list.setVisibility(View.GONE);} else {//显示购物车列表rl_car_list.setVisibility(View.VISIBLE);}iv_shop_car.setImageResource(R.drawable.shop_car_empty);//隐藏去结算按钮tv_settle_accounts.setVisibility(View.GONE);//显示差价文本tv_not_enough.setVisibility(View.VISIBLE);tv_not_enough.setText("¥" + bean.getOfferPrice() + "起送");//隐藏购物车中的菜品数量控件tv_count.setVisibility(View.GONE);//隐藏配送费用tv_distribution_cost.setVisibility(View.GONE);tv_money.setTextColor(getResources().getColor(R.color.light_gray));tv_money.setText("未选购商品");}}break;}}}/*** 设置界面数据*/private void setData() {if (bean == null) return;tv_shop_name.setText(bean.getShopName()); //设置店铺名称tv_time.setText(bean.getTime());            //设置配送时间tv_notice.setText(bean.getShopNotice());  //设置店铺公告//设置起送价格tv_not_enough.setText("¥" + bean.getOfferPrice() + "起送");Glide.with(this).load(bean.getShopPic()).error(R.mipmap.ic_launcher).into(iv_shop_pic);             //设置店铺图片adapter.setData(bean.getFoodList());//将菜单列表数据传递到adapter中}
}

六、菜品详情功能业务实现

目标

  • 掌握菜品详情界面的开发过程,能够实现菜品详情界面的功能

​ 点击菜单列表的条目,程序会跳转到菜品详情界面,该界面主要用于展示菜品的名称、月售数量和价格等信息。菜品详情界面中的数据是从店铺详情界面传递过来的。接下来本节将针对菜品详情功能业务的实现进行详细讲解。

6.1 搭建菜品详情界面布局

​ 菜品详情界面主要用于展示菜品的名称、月售数量以及菜品的价格。

放置界面控件 src\main\res\layout\activity_food.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="200dp"android:layout_height="wrap_content"android:background="@android:color/white"android:orientation="vertical"android:padding="8dp"><ImageViewandroid:id="@+id/iv_food_pic"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center_horizontal" /><TextViewandroid:id="@+id/tv_food_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="8dp"android:layout_marginTop="8dp"android:textColor="@android:color/black"android:textSize="16sp" /><TextViewandroid:id="@+id/tv_sale_num"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="8dp"android:layout_marginTop="4dp"android:textColor="@color/color_gray"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_price"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="8dp"android:layout_marginTop="4dp"android:textColor="@color/price_red"android:textSize="14sp" />
</LinearLayout>

菜品详情界面的对话框样式定义 values/styles.xml

</style><style name="Theme.ActivityDialogStyle" parent="Theme.AppCompat.Light.NoActionBar"><item name="android:windowIsTranslucent">true</item>   <!--设置对话框背景为透明--><!--设置对话框背景有半透明遮障层--><item name="android:backgroundDimEnabled">true</item><item name="android:windowContentOverlay">@null</item> <!--设置窗体内容背景--><!--点击对话框外的部分关闭该界面--><item name="android:windowCloseOnTouchOutside">true</item><item name="android:windowIsFloating">true</item> <!--浮在Activity之上--></style>

在清单文件中引入对话框样式 app/src/main/AndroidManifest.xml

<activityandroid:name=".activity.FoodActivity"android:theme="@style/Theme.ActivityDialogStyle" />

6.2 实现菜品界面显示功能

菜品详情界面的数据是从店铺详情界面传递过来的,该界面的逻辑代码相对比较简单,主要是获取传递过来的菜品数据,并将数据显示到界面上。实现菜品界面显示功能的具体步骤如下:

1. 获取界面控件

​ 在FoodActivity中创建初始化界面控件的方法initView()。

2. 设置界面数据

在FoodActivity中创建一个setData()方法,该方法用于将数据设置到菜品详情界面的控件上。

3. 修改MenuAdapter.java文件

在MenuAdapter中的getView()方法中的注释“//跳转到菜品详情界面”下方添加跳转到菜品详情界面的逻辑代码。

package cn.itcast.order.activity;import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.TextView;import com.bumptech.glide.Glide;import cn.itcast.order.R;
import cn.itcast.order.bean.FoodBean;public class FoodActivity extends AppCompatActivity {private FoodBean bean;private TextView tv_food_name, tv_sale_num, tv_price;private ImageView iv_food_pic;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_food);//从店铺详情界面传递过来的菜的数据bean = (FoodBean) getIntent().getSerializableExtra("food");initView();setData();}/*** 初始化界面控件*/private void initView() {tv_food_name = findViewById(R.id.tv_food_name);tv_sale_num = findViewById(R.id.tv_sale_num);tv_price = findViewById(R.id.tv_price);iv_food_pic = findViewById(R.id.iv_food_pic);}/*** 设置界面数据*/private void setData() {if (bean == null) return;tv_food_name.setText(bean.getFoodName());tv_sale_num.setText("月售" + bean.getSaleNum());tv_price.setText("¥" + bean.getPrice());
//        将菜品图片显示到iv_food_pic控件上Glide.with(this).load(bean.getFoodPic()).error(R.mipmap.ic_launcher).into(iv_food_pic);}
}

七、订单功能业务实现

目标

  • 掌握订单界面的开发过程,能够实现订单界面的效果

​ 在店铺详情界面,点击“去结算”按钮,程序会跳转到订单界面,订单界面主要展示的是收货地址、订单列表、小计、配送费以及订单总价与“去支付”按钮,该界面的数据是从店铺详情界面传递过来的。点击“去支付”按钮,程序会弹出一个二维码支付界面供用户付款。接下来本节将针对订单功能业务的实现进行详细讲解。

7.1 搭建订单界面布局

​ 订单界面主要用于展示收货地址、订单列表、小计、配送费、订单总价以及“去支付”按钮。

创建订单界面布局 layout/activity_order.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"android:orientation="vertical"><include layout="@layout/order_head"/><include layout="@layout/payment" />
</FrameLayout>

展示收货地址、订单列表、小计、配送费 layout/order_head.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/type_gray"android:orientation="vertical"><include layout="@layout/title_bar" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:layout_marginTop="15dp"android:background="@android:color/white"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:text="收货地址:"android:textColor="@color/color_gray"android:textSize="16sp" /><EditTextandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"android:textColor="@android:color/black"android:textSize="16sp" /></LinearLayout><ListViewandroid:id="@+id/lv_order"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:background="@android:color/white" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="@color/type_gray" /><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@android:color/white"><TextViewandroid:id="@+id/tv_cost"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:padding="10dp"android:textColor="@color/price_red"android:textSize="16sp"android:textStyle="bold" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="2dp"android:layout_toLeftOf="@id/tv_cost"android:padding="10dp"android:text="小计"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_distribution_cost"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_below="@id/tv_cost"android:padding="10dp"android:textColor="@android:color/black"android:textSize="14sp"android:textStyle="bold" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/tv_cost"android:layout_toLeftOf="@id/tv_distribution_cost"android:padding="10dp"android:text="配送费"android:textColor="@android:color/black"android:textSize="14sp" /></RelativeLayout>
</LinearLayout>

展示订单总价以及“去支付”按钮 layout/payment.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/transparent"android:gravity="bottom"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="@android:color/white"><TextViewandroid:id="@+id/tv_total_cost"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:padding="10dp"android:textColor="@color/price_red"android:textSize="18sp"android:textStyle="bold" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="2dp"android:layout_toLeftOf="@id/tv_total_cost"android:padding="10dp"android:text="订单总价"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_payment"android:layout_width="match_parent"android:layout_height="40dp"android:layout_below="@id/tv_total_cost"android:layout_margin="8dp"android:background="@drawable/payment_bg_selector"android:gravity="center"android:text="去支付"android:textColor="@android:color/white"android:textStyle="bold" /></RelativeLayout>
</RelativeLayout>

创建“去支付”按钮的背景选择器 drawable/payment_bg_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@color/account_selected_color" android:state_pressed="true"/><item android:drawable="@color/account_color"/>
</selector>

7.2 搭建订单列表条目界面布局

​ 由于订单界面中使用ListView控件展示订单列表信息,所以需要创建一个该列表的条目界面。在条目界面中需要展示菜品的名称、数量以及总价信息。

放置界面控件:展示菜品的名称、数量以及总价信息 layout/order_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="8dp"android:layout_marginTop="8dp"android:layout_marginRight="8dp"android:background="@drawable/menu_item_bg_selector"android:padding="10dp"><ImageViewandroid:id="@+id/iv_food_pic"android:layout_width="80dp"android:layout_height="60dp"android:layout_alignParentLeft="true" /><LinearLayoutandroid:id="@+id/ll_info"android:layout_width="wrap_content"android:layout_height="60dp"android:layout_marginLeft="10dp"android:layout_toRightOf="@id/iv_food_pic"android:gravity="center"android:orientation="vertical"><TextViewandroid:id="@+id/tv_food_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:textColor="@android:color/black"android:textSize="14sp" /><TextViewandroid:id="@+id/tv_count"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:textColor="@android:color/black"android:textSize="12sp" /></LinearLayout><TextViewandroid:id="@+id/tv_money"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="10dp"android:textColor="@android:color/black"android:textSize="12sp" />
</RelativeLayout>

7.3 搭建支付界面布局

​ 当点击订单界面的“去支付”按钮时,程序会弹出支付界面,该界面是一个对话框的样式,该界面上显示一个文本信息和一个二维码图片。

放置界面控件 layout/qr_code.xml

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:layout_marginLeft="6dp"android:text="请扫描下方二维码进行支付"android:textColor="@android:color/white"android:textSize="16sp" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:scaleType="fitXY"android:src="@drawable/qr_code" />
</LinearLayout>

7.4 编写订单列表适配器

 订单界面的订单列表信息是用ListView控件展示的,所以需要创建一个数据适配器OrderAdapter对ListView控件进行数据适配。编写订单列表适配器的具体步骤如下:

1. 创建订单列表适配器OrderAdapter

  在cn.itcast.order.adapter包中,创建一个适配器OrderAdapter,并在该适配器中重写getCount()方法、getItem()方法、getItemId()方法和getView()方法。

2. 创建ViewHolder类

  <font color='cornflowerblue'>在OrderAdapter中创建一个ViewHolder类</font>,该类主要用于创建订单列表条目上的控件对象,当订单列表快速滑动时,<font color='limeGreen'>该类可以快速为界面控件设置值,而不必每次都重新创建很多控件对象,这样可以提高程序的性能。</font>
package cn.itcast.order.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.math.BigDecimal;
import java.util.List;
import cn.itcast.order.R;
import cn.itcast.order.bean.FoodBean;public class OrderAdapter extends BaseAdapter {private Context mContext;private List<FoodBean> fbl;public OrderAdapter(Context context) {this.mContext = context;}/*** 设置数据更新界面*/public void setData(List<FoodBean> fbl) {this.fbl = fbl;notifyDataSetChanged();}/*** 获取条目的总数*/@Overridepublic int getCount() {return fbl == null ? 0 : fbl.size();}/*** 根据position得到对应条目的对象*/@Overridepublic FoodBean getItem(int position) {return fbl == null ? null : fbl.get(position);}/*** 根据position得到对应条目的Id*/@Overridepublic long getItemId(int position) {return position;}/*** 得到相应position对应的条目视图,position是当前条目的位置,* convertView参数是滚出屏幕的条目的视图*/@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {final ViewHolder vh;//复用convertViewif (convertView == null) {vh = new ViewHolder();convertView = LayoutInflater.from(mContext).inflate(R.layout.order_item,null);vh.tv_food_name = convertView.findViewById(R.id.tv_food_name);vh.tv_count = convertView.findViewById(R.id.tv_count);vh.tv_money = convertView.findViewById(R.id.tv_money);vh.iv_food_pic = convertView.findViewById(R.id.iv_food_pic);convertView.setTag(vh);} else {vh = (ViewHolder) convertView.getTag();}//获取position对应的条目数据对象final FoodBean bean = getItem(position);if (bean != null) {vh.tv_food_name.setText(bean.getFoodName());vh.tv_count.setText("x"+bean.getCount());vh.tv_money.setText("¥"+bean.getPrice().multiply(BigDecimal.valueOf(bean.getCount())));Glide.with(mContext).load(bean.getFoodPic()).error(R.mipmap.ic_launcher).into(vh.iv_food_pic);}return convertView;}class ViewHolder {public TextView tv_food_name, tv_count, tv_money;public ImageView iv_food_pic;}
}

7.5 实现订单显示与支付功能

​ 订单界面的数据是从店铺详情界面传递过来的,该界面的逻辑代码相对比较简单,主要是获取传递过来的数据,并将数据显示到界面上。实现订单显示与支付功能的具体步骤如下:

1. 获取界面控件

​ 在OrderActivity中创建界面控件的初始化方法init(),该方法用于获取订单界面所要用到的控件并实现返回键与“去支付”按钮的点击事件。

2. 设置界面数据

在OrderActivity中创建一个setData()方法,该方法用于将数据设置到订单界面的控件上。

3. 修改ShopDetailActivity.java文件

 由于点击店铺详情界面的“去结算”按钮时,会跳转到订单界面,所以需要找到ShopDetailActivity中的onClick()方法,在该方法中的注释“//跳转到订单界面”下方添加跳转到订单界面的逻辑代码。
package cn.itcast.order.activity;
import android.app.Dialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.math.BigDecimal;
import java.util.List;
import cn.itcast.order.R;
import cn.itcast.order.adapter.OrderAdapter;
import cn.itcast.order.bean.FoodBean;
public class OrderActivity extends AppCompatActivity {private ListView lv_order;private OrderAdapter adapter;private List<FoodBean> carFoodList;private TextView tv_title, tv_back,tv_distribution_cost,tv_total_cost,tv_cost,tv_payment;private RelativeLayout rl_title_bar;private BigDecimal money,distributionCost;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_order);//获取购物车中的数据carFoodList= (List<FoodBean>) getIntent().getSerializableExtra("carFoodList");//获取购物车中菜的总价格money=new BigDecimal(getIntent().getStringExtra("totalMoney"));//获取店铺的配送费distributionCost=new BigDecimal(getIntent().getStringExtra("distributionCost"));initView();setData();}/*** 初始化界面控件*/private void initView(){tv_title = findViewById(R.id.tv_title);tv_title.setText("订单");rl_title_bar = findViewById(R.id.title_bar);rl_title_bar.setBackgroundColor(getResources().getColor(R.color.blue_color));tv_back =  findViewById(R.id.tv_back);lv_order= findViewById(R.id.lv_order);tv_distribution_cost = findViewById(R.id.tv_distribution_cost);tv_total_cost =  findViewById(R.id.tv_total_cost);tv_cost =  findViewById(R.id.tv_cost);tv_payment =  findViewById(R.id.tv_payment);// 返回键的点击事件tv_back.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {finish();}});tv_payment.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) { //“去支付”按钮的点击事件Dialog dialog = new Dialog(OrderActivity.this, R.style.Dialog_Style);dialog.setContentView(R.layout.qr_code);dialog.show();}});}/*** 设置界面数据*/private void setData() {adapter=new OrderAdapter(this);lv_order.setAdapter(adapter);adapter.setData(carFoodList);tv_cost.setText("¥"+money);tv_distribution_cost.setText("¥"+distributionCost);tv_total_cost.setText("¥"+(money.add(distributionCost)));}
}

总结

本项目用到的技术点

  • 异步线程访问网络

  • Tomcat服务器

  • Handler消息机制

  • JSON数据解析

创建一个setData()方法,该方法用于将数据设置到订单界面的控件上。

3. 修改ShopDetailActivity.java文件

 由于点击店铺详情界面的“去结算”按钮时,会跳转到订单界面,所以需要找到ShopDetailActivity中的onClick()方法,在该方法中的注释“//跳转到订单界面”下方添加跳转到订单界面的逻辑代码。
package cn.itcast.order.activity;
import android.app.Dialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.math.BigDecimal;
import java.util.List;
import cn.itcast.order.R;
import cn.itcast.order.adapter.OrderAdapter;
import cn.itcast.order.bean.FoodBean;
public class OrderActivity extends AppCompatActivity {private ListView lv_order;private OrderAdapter adapter;private List<FoodBean> carFoodList;private TextView tv_title, tv_back,tv_distribution_cost,tv_total_cost,tv_cost,tv_payment;private RelativeLayout rl_title_bar;private BigDecimal money,distributionCost;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_order);//获取购物车中的数据carFoodList= (List<FoodBean>) getIntent().getSerializableExtra("carFoodList");//获取购物车中菜的总价格money=new BigDecimal(getIntent().getStringExtra("totalMoney"));//获取店铺的配送费distributionCost=new BigDecimal(getIntent().getStringExtra("distributionCost"));initView();setData();}/*** 初始化界面控件*/private void initView(){tv_title = findViewById(R.id.tv_title);tv_title.setText("订单");rl_title_bar = findViewById(R.id.title_bar);rl_title_bar.setBackgroundColor(getResources().getColor(R.color.blue_color));tv_back =  findViewById(R.id.tv_back);lv_order= findViewById(R.id.lv_order);tv_distribution_cost = findViewById(R.id.tv_distribution_cost);tv_total_cost =  findViewById(R.id.tv_total_cost);tv_cost =  findViewById(R.id.tv_cost);tv_payment =  findViewById(R.id.tv_payment);// 返回键的点击事件tv_back.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {finish();}});tv_payment.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) { //“去支付”按钮的点击事件Dialog dialog = new Dialog(OrderActivity.this, R.style.Dialog_Style);dialog.setContentView(R.layout.qr_code);dialog.show();}});}/*** 设置界面数据*/private void setData() {adapter=new OrderAdapter(this);lv_order.setAdapter(adapter);adapter.setData(carFoodList);tv_cost.setText("¥"+money);tv_distribution_cost.setText("¥"+distributionCost);tv_total_cost.setText("¥"+(money.add(distributionCost)));}
}

总结

本项目用到的技术点

  • 异步线程访问网络

  • Tomcat服务器

  • Handler消息机制

  • JSON数据解析

CH12-综合项目—仿美团外卖相关推荐

  1. 基于Android的仿美团外卖系统设计与实现 文档+源码+视频

    基于Android的仿美团外卖系统设计与实现 演示视频 摘 要 为了巩固所学 Android 基础知识,要开发一款仿美团外卖的项目,该项目与我们平常看到的美团外卖项目界面比较类似,展示的内容包括店铺. ...

  2. 【uniapp前端组件】仿美团外卖商品列表

    仿美团外卖商品列表 简易实现美团外卖商品列表,没什么技术难点,简单来说就是两个scroll-view协作,并且两个scroll-view不会滑动冲突. 实际效果 仿美团外卖商品列表实际项目效果 简介 ...

  3. Vue开发的仿美团外卖Html5前端页面

    今天给大家开源一个仿美团外卖的Vue项目,介绍Vue和vue-router的基本用法. 工程结构 工程目录结构比较简单,如下图所示. 运行效果 部分运行效果如下图,实现了基本的页面切换,导航菜单,购物 ...

  4. Android仿美团外卖点菜联动列表

    Android高仿美团外卖点菜联动列表效果 最近项目中有一个添加购物车的需求,需要做成美团外卖点菜联动ListView的效果,可能有的朋友觉得这很简单,不就是2个Listview点击事件联动处理机制吗 ...

  5. 进云仿美团外卖平台 v1.39源码

    简介: 进云仿美团外卖源码是一个进云源生插件,支持多商户+多样化配送费模式+本土外卖平台+支持第三方配送,运行需要进云框架支撑! 特点: 1.多样化配送费模式: 2.板块-绑定商户分类机制: 3.板块 ...

  6. Android Studio初学者实例:Fragment学习--仿美团外卖界面

    本次课程为Fragment为主题,课程的示例仿美团外卖界面,不同于底部导航栏的Fragment案例,此界面分为左侧切换与顶部切换.本文先是发布代码与效果,后续讲解将会在后续补充.先看看效果: 首先是布 ...

  7. Android 高仿美团外卖详情页

    目录 1.需求分析 2.具体实现 2.1效果展示 2.2布局分析 2.3代码分析 2.3.1自定义 CoordinatorLayout.Behavior 2.3.2自定义 RecyclerView.I ...

  8. iOS高仿美团外卖店铺主页

    高仿美团外卖的店铺主页(包括下拉动画效果,解决各种手势问题,并且cell有列表样式,九宫格样式,卡片样式),各种动画效果纵享丝滑,因为写的比较急,还待优化.! 解决UIScrollView嵌套UISc ...

  9. 仿美团外卖菜单界面的实现

    仿美团外卖菜单界面的实现 布局文件 总布局 <?xml version="1.0" encoding="utf-8"?> <LinearLay ...

最新文章

  1. python语言编程环境-day02--编程语言的分类与Python开发环境的搭建
  2. 结合video.js播放rtmp格式、flv格式、mp4等格式的视频
  3. Spring定时器配置
  4. Git学习资源收集汇总
  5. 商户网站使用第三方支付的大致原理和实现
  6. c++用牛顿法开多次根_望远镜的历史之三:大神出世,改变望远镜历史的竟然是牛顿...
  7. hash进阶:使用字符串hash乱搞的姿势
  8. Hibernate保存对象出现 org.hibernate.NonUniqueObjectExce
  9. 编译android源码四(常见错误)
  10. [转载] python字符串数组字典_Python:字符串、列表、元组、字典
  11. 解决warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
  12. mysql reopen table_【MySql】关于临时表cann't reopen
  13. Zero Forex领汇来告诉你什么是WH?
  14. 循环神经网络RNN 2—— attention注意力机制(附代码)
  15. 云宏携手英特尔基于英特尔® 至强® 可扩展平台全面升级超融合一体机解决方案
  16. win10电脑耳机插上突然没音(已解决)
  17. 线性代数Python计算:向量的模及向量间的夹角
  18. Ubuntu: failed to fetch
  19. win10恢复经典开始菜单_怎么将win10界面切换成win7界面
  20. requests模块基本使用、代理ip、session访问

热门文章

  1. WEBAPP开发技巧总结
  2. 徽章系列8:生成个性徽章
  3. html禁用TAB按键
  4. 焊接工业机器人实训系统平台
  5. 红米note3支持html,红米Note3支持什么视频格式?支持RMVB播放吗?
  6. electron-vue使用教程
  7. PHP 生成海报(头像,昵称,简介)
  8. 最初的计算机主要用于科学计算判断对错,第一代电子计算机主要用于科学计算。...
  9. XtraBackup2.4下载及安装
  10. Web UI自动化录制工具-Selenium IDE