第一章需求分析
近几年来,随着互联网时代的发展,电子商务迅猛发展让人们的购物更方便,更随时随地。而商城购物App是专门为商业零售及服务企业开发的手机客户端,帮助商家快速形成自有的用户群体,通过免费的信息推送开展促销活动,有效提升销售业绩。简而言之,手机App购物商城就是通过网络推广把线上的消费者带到现实的商店中去在线支付,购买线下的商品和服务,再到线下去提货和享受服务。
一、项目定位
”机太美”手机商城APP是以实现用户足不出户就能够选购自己喜欢的手机为目的,以尽全力满足全体客户需求为宗旨的工具型应用APP软件。
二、APP竞品分析
表1 APP竞品分析
三、用户界面设计
“机太美”手机商城APP的视觉设计尽量提供用户特点鲜明的功能和界面,达到用户体验的最佳化,给予用户良好的视觉体验。
第二章 概要设计
图1是整个手机商城APP的项目框架,分为客户端和服务端。客户端有三个部分:视图,模块,其中界面视图(view),也就是Activity和一些View模块,包括登陆,注册,产品,订单,购物车等界面,同时该部分还包含用户事件的捕捉和少量的逻辑处理,比如用户单击某个图标,界面的切换方式等操作;模块部分对于于上图的网络模块和数据模块,各个模块能提供不同的功能,视图调用数据模块中的数据进行显示。在客户端对于登陆注册采用了SharedPreference存储和SQLite存储。对于用户注销时,采用了Android的Service技术和广播技术来不时提醒用户尚多久未登陆以期实现产品的更大销售可能。
从MVC设计模式的角度去分析,服务端采用了SSM框架技术,Controler层,对客户端请求的数据转发处理,包括用户,订单,购物车,产品等。Service层,业务逻辑处理,包括用户的登陆注册,订单的状态改变,购物车的增删改查,产品的显示等处理操作。Mapper采用了Mybatis逆向工程技术对数据库进行一系列访问更新等操作。远端数据库采用了mysql数据库。
数据设计
3.2 界面设计
3.2.1 欢迎界面
WelcomeActivity,实现在2秒后进入应用程序。
3.2.2 注册界面
登陆界面:前台向后台发送用户名密码,后台检验并返回数据判断是否登陆成功。在成功登陆后运用了Android的SharePreferences存储技术在本机上保存了相应的用户登陆数据。
亮点:在这两个页面中,我们对注册和登录按钮添加了监听机制,即按钮初始颜色未灰色,不可点击,为不可用状态。只有当用户将所有的输入栏都填写完整后,按钮才变为橘色,可以出发点击事件。
上图为订单界面,分为全部订单,待支付订单,待发货订单,待确认订单,和待评价订单。
当订单的状态改变时,数据库和客户端订单的状态同时更新。
3.3 关键技术
3.3.1网络连接访问
我们利用HttpURLConnection来进行网络通信,并封装了GET和POST两种方式用于与后台链接并传输数据。
private static final String MY_HOST_URL="http://192.168.1.107:8080/MarketServer";
public static final String IMAGE_PATH="http://192.168.1.107:8080/MarketServer/upload/";
// private static String sessionId;
//GET请求需将参数进行封装
public static String get(String api,String param) throws IOException {
String path=MY_HOST_URL+api+param;
URL url = new URL(path);
HttpURLConnection connection=(HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(3000);
int code = connection.getResponseCode();
String res=null;
Log.i("code",code+"");
if(code==200){
InputStream inputStream = connection.getInputStream();
InputStreamReader in=new InputStreamReader(inputStream);
BufferedReader bufferedReader=new BufferedReader(in);
res = bufferedReader.readLine();
bufferedReader.close();
}
return res;
}
//post请求直接传实体类
public static String post(String api, Object obj) throws IOException {
Log.i("httputil","httputil");
String path=MY_HOST_URL+api;
URL url = new URL(path);
HttpURLConnection connection=(HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type","application/json");
connection.setConnectTimeout(3000);
Gson gson = new Gson();
OutputStream outputStream = connection.getOutputStream();
outputStream.write(gson.toJson(obj).getBytes());
int code = connection.getResponseCode();
String res=null;
Log.i("code",code+"");
if(code==200){
InputStream inputStream = connection.getInputStream();
InputStreamReader in=new InputStreamReader(inputStream);
BufferedReader bufferedReader=new BufferedReader(in);
res = bufferedReader.readLine();
bufferedReader.close();
}
return res;
}
SharePreferences
public class UserSharePreference {
public static boolean SaveUserInfo(Context context, User user)
{
SharedPreferences sharedPreferences=context.getSharedPreferences("user_mes",Context.MODE_PRIVATE);
SharedPreferences.Editor editor=sharedPreferences.edit();
editor.putInt("user_id",user.getUserId());
editor.putString("user_name",user.getUserName());
editor.commit();
return true;
}
public static User getUser_mes(Context context)
{
SharedPreferences sharedPreferences=context.getSharedPreferences("user_mes",Context.MODE_PRIVATE);
String username=sharedPreferences.getString("user_name",null);
int userid=sharedPreferences.getInt("user_id",1);
if(username==null) return null;
User user = new User(username,userid);
return user;
}
public static void ClearData(Context context)
{
SharedPreferences sharedPreferences=context.getSharedPreferences("user_mes",Context.MODE_PRIVATE);
sharedPreferences.edit().clear().commit();
3.3.3 service 和Notification
当用户注销后,我们通过开启Service在后台运行使得该APP退出后仍然会在一定的时间内通知用户尝试登陆,提高该APP的关注度,以期得到更好地发展。
3.3.3 service 和Notification
当用户注销后,我们通过开启Service在后台运行使得该APP退出后仍然会在一定的时间内通知用户尝试登陆,提高该APP的关注度,以期得到更好地发展。
public class NotifyService extends Service {
private NotificationManager mManager;
public static final String ANDROID_CHANNEL_ID = “com.ncusoft.notificationchannel.IOS”;
public static final String ANDROID_CHANNEL_NAME = “IOS CHANNEL”;
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
private int id=0;
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onCreate() {
createChannels();
new Thread(){
@Override
public void run() {
while (true){
id++;
Notification.Builder builder = new Notification.Builder(NotifyService.this, ANDROID_CHANNEL_ID)
.setContentTitle("机太美")
.setContentText("你已经"+id+"分钟没有光顾我们了")
.setSmallIcon(R.drawable.about)
.setAutoCancel(true);
getManager().notify(id,builder.build());
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@RequiresApi(api = Build.VERSION_CODES.O)
public void createChannels() {
// create ios channel
NotificationChannel iosChannel = new NotificationChannel(ANDROID_CHANNEL_ID,
ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
iosChannel.enableLights(true);
iosChannel.enableVibration(true);
iosChannel.setLightColor(Color.BLUE);
iosChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
getManager().createNotificationChannel(iosChannel);
}
public NotificationManager getManager() {
if (mManager == null) {
mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mManager;
}
}
3.3.4 BroadcastReceiver
我们注册了两个广播。其中,MyReceiver用来监听网络请求的返回结果。当网络请求成功时,则利用Toast来提示用户请求成功。而FinishReceiver则用来监听用户退出程序的操作,当用户退出程序时则启动上述的service 。
package com.ncuedu.marketclient.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
Toast.makeText(context,msg,Toast.LENGTH_SHORT).show();
}
}
package com.ncuedu.marketclient.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.ncuedu.marketclient.service.NotifyService;
public class FinishReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//开启一个发送notification的service
Intent intentService = new Intent(context, NotifyService.class);
context.startService(intentService);
}
}
CityPickerView框架的使用
在填写收货人信息的界面中,我们使用了Github上的开源框架CityPickerView。该框架可以实现选择地址时的三级联动。
case R.id.edt_area:{
//隐藏键盘
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
//添加默认的配置,不需要自己定义
CityConfig cityConfig = new CityConfig.Builder().build();
CityPickerView.getInstance().setConfig(cityConfig);
//监听选择点击事件及返回结果
CityPickerView.getInstance().setOnCityItemClickListener(new OnCityItemClickListener() {
@Override
public void onSelected(ProvinceBean province, CityBean city, DistrictBean district) {
String res="";
//省份
if (province != null) {
res+=province.toString();
}
//城市
if (city != null) {
res+=city.toString();
}
//地区
if (district != null) {
res+=district.toString();
}
editArea.setText(res);
}
@Override
public void onCancel() {
ToastUtils.showLongToast(SetOrderActivity.this, "已取消");
}
});
CityPickerView.getInstance().showCityPicker( );
break;
}
轮播图banner组件的使用
我们使用了banner组件来实现商品信息界面的轮播图功能。
public class ProductImgFragment extends Fragment implements OnBannerListener {
private Banner banner;
private ArrayList<String> list_path;
private View view;
private Product product;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_product_img, container, false);
ShowProductActivity activity = (ShowProductActivity)getActivity();
int productId = activity.getProductId();
product=activity.getProduct();
Log.i("product2222111",product.toString());
initView();
return view;
}
private void initData(){
if(product.getProductImg1()!=null&&product.getProductImg1().length()>0){
list_path.add(HttpUtil.IMAGE_PATH+product.getProductImg1());
}
if(product.getProductImg2()!=null&&product.getProductImg2().length()>0){
list_path.add(HttpUtil.IMAGE_PATH+product.getProductImg2());
}
if(product.getProductImg3()!=null&&product.getProductImg3().length()>0){
list_path.add(HttpUtil.IMAGE_PATH+product.getProductImg3());
}
}
private void initView() {
banner = view.findViewById(R.id.banner);
list_path = new ArrayList<>();
initData();
banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR);
banner.setImageLoader(new ProductImgFragment.MyLoader());
banner.setBannerAnimation(Transformer.Default);
banner.setDelayTime(3000);
banner.isAutoPlay(true);
banner.setIndicatorGravity(BannerConfig.CENTER);
banner.setImages(list_path)
.setOnBannerListener(this)
.start();
}
@Override
public void OnBannerClick(int position) {
Toast.makeText(getContext(), "你点了第" + (position + 1) + "张轮播图", Toast.LENGTH_SHORT).show();
}
//自定义的图片加载器
private class MyLoader extends ImageLoader {
@Override
public void displayImage(Context context, Object path, ImageView imageView) {
Glide.with(context.getApplicationContext())
.load((String) path)
.into(imageView);
}
}
}
该手机商城APP后台采用了SSM整体架构
Spring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。
Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地new
一个对象,而是让Spring框架帮你来完成这一切。
SpringMVC在项目中拦截用户请求,它的核心Servlet即DispatcherServlet承担中介或是前台这样的职责,将用户请求通过HandlerMapping去匹配Controller,Controller就是具体对应请求所执行的操作。SpringMVC相当于SSH框架中struts。
mybatis是对jdbc的封装,它让数据库底层操作变的透明。mybatis的操作都是围绕一个sqlSessionFactory实例展开的。mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再执行sql命令。
下载地址