移动电商APP搭建(一) 架构设计
正在学习搭建一个完整的APP,由于工作量较大,时间跨度较长,这里分为几个阶段,不定时更新
主要有五个阶段
架构设计
开发首页和会员模块
开发商品展示模块
开发购物车和移动支付模块
适配与发布
架构设计
架构设计主要是搭建框架
初始化项目
先把必要的依赖导进来
1 | dependencies { |
还有AndroidManifest.xml中的必要的一些,还有将引导页设置为开启页
1 |
|
合理化设计包结构,添加资源文件夹
搭建主页框架
主页结构分析
采用Activity+Fragment相结合
主界面实现
activity_main.xml
1 |
|
MainActivity
1 | package com.huatec.edu.mobileshop.activity; |
fragment_navigation.xml
1 |
|
NavigationFragment
1 | package com.huatec.edu.mobileshop.fragment; |
四个页面框架
home 首页 fragment_home.xml
1 |
|
category 分类 fragment_category.xml
1 |
|
cart 购物车 fragment_cart.xml
1 |
|
person 我的 fragment_person.xml
1 |
|
设计引导页和广告页
在设计引导页和广告页之前,先构建三个通用类 Constants ImageLoaderManager MyApplication
Constants
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28package com.huatec.edu.mobileshop.common;
/**
* Created by Administrator on 2016/9/26.
*/
public class Constants {
/**
* 广告显示时长 ,单位:ms
*/
public static int AD_TIME_SECOND = 3000;
/**
* Base url
*/
public static String BASE_URL = "http://192.168.31.238:8080/MobileShop/";
/**
* 广告url
*/
// public static String AD_URL = BASE_URL + "";
public static String AD_URL = "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg"+"";
public static String API_KEY_FOR_MOB_SMS = "182489edf1060";
public static String API_SECRET_FOR_MOB_SMS = "5955939eb23eb90c2baa227f87de43a0";
/**
* 列表页面右侧列表的列数
*/
public static int SPAN_COUNT = 3;
}ImageLoaderManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119package com.huatec.edu.mobileshop.common;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import com.huatec.edu.mobileshop.R;
import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
import com.nostra13.universalimageloader.utils.StorageUtils;
import java.io.File;
/**
* Created by Administrator on 2016/8/31.
*/
public class ImageLoaderManager {
private static ImageLoaderManager mInstance;
public static ImageLoaderManager getInstance() {
if (mInstance == null) {
synchronized (ImageLoaderManager.class) {
if (mInstance == null) {
mInstance = new ImageLoaderManager();
}
}
}
return mInstance;
}
public ImageLoaderManager() {
if (mInstance == null) {
//采用自定义配置
ImageLoader.getInstance().init(customImageLoaderConfig(MyApplication.getContext()));
//采用默认配置
ImageLoader.getInstance().init(defaultImageLoaderConfig());
}
}
//Image-loader框架默认配置
private ImageLoaderConfiguration defaultImageLoaderConfig() {
return ImageLoaderConfiguration.createDefault(MyApplication.getContext());
}
//Image-loader框架自定义配置
private ImageLoaderConfiguration customImageLoaderConfig(Context context) {
File cacheDir = StorageUtils.getCacheDirectory(context); //缓存文件夹路径
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions 内存缓存文件的最大长宽
//.memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通过自己的内存缓存实现
//.memoryCache(new WeakMemoryCache())
.memoryCacheSize(2 * 1024 * 1024) // 内存缓存的最大值
.memoryCacheSizePercentage(13) // default
.diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)缓存的最大值
.diskCacheFileCount(100) // 可以缓存的文件数量
// default为使用HASHCODE对UIL进行加密命名, 还可以用MD5(new Md5FileNameGenerator())加密
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
//.diskCacheExtraOptions(480, 800, null) // 本地缓存的详细信息(缓存的最大长宽),最好不要设置这个
.threadPoolSize(3) // default 线程池内加载的数量
.threadPriority(Thread.NORM_PRIORITY - 2) // default 设置当前线程的优先级
.denyCacheImageMultipleSizesInMemory()
.imageDownloader(new BaseImageDownloader(context)) // default
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
.writeDebugLogs() // 打印debug log
.build(); //开始构建
return config;
}
//Image-loader框架显示图片的配置参数
public static DisplayImageOptions product_options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.image_loading) // 设置图片下载期间显示的图片
.showImageForEmptyUri(R.drawable.image_empty) // 设置图片Uri为空或是错误的时候显示的图片
.showImageOnFail(R.drawable.image_error) // 设置图片加载或解码过程中发生错误显示的图片
.resetViewBeforeLoading(false) // default 设置图片在加载前是否重置、复位
.delayBeforeLoading(1000) // 下载前的延迟时间
.cacheInMemory(false) // default 设置下载的图片是否缓存在内存中
.cacheOnDisk(false) // default 设置下载的图片是否缓存在SD卡中
.considerExifParams(false) // default
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default 设置图片以如何的编码方式显示
.bitmapConfig(Bitmap.Config.ARGB_8888) // default 设置图片的解码类型
.displayer(new SimpleBitmapDisplayer()) // default 还可以设置圆角图片new RoundedBitmapDisplayer(20)
.handler(new Handler()) // default
.build();
public static DisplayImageOptions user_options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.image_loading) // 设置图片下载期间显示的图片
.showImageForEmptyUri(R.drawable.face_default) // 设置图片Uri为空或是错误的时候显示的图片
.showImageOnFail(R.drawable.face_default) // 设置图片加载或解码过程中发生错误显示的图片
.resetViewBeforeLoading(false) // default 设置图片在加载前是否重置、复位
.delayBeforeLoading(1000) // 下载前的延迟时间
.cacheInMemory(false) // default 设置下载的图片是否缓存在内存中
.cacheOnDisk(false) // default 设置下载的图片是否缓存在SD卡中
.considerExifParams(false) // default
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default 设置图片以如何的编码方式显示
.bitmapConfig(Bitmap.Config.ARGB_8888) // default 设置图片的解码类型
.displayer(new SimpleBitmapDisplayer()) // default 还可以设置圆角图片new RoundedBitmapDisplayer(20)
.handler(new Handler()) // default
.build();
/**
* 缩放类型mageScaleType:
EXACTLY :图像将完全按比例缩小的目标大小
EXACTLY_STRETCHED:图片会缩放到目标大小完全
IN_SAMPLE_INT:图像将被二次采样的整数倍
IN_SAMPLE_POWER_OF_2:图片将降低2倍,直到下一减少步骤,使图像更小的目标大小
NONE:图片不会调整
*/
/**
* 显示方式displayer:
RoundedBitmapDisplayer(int roundPixels)设置圆角图片
FakeBitmapDisplayer()这个类什么都没做
FadeInBitmapDisplayer(int durationMillis)设置图片渐显的时间
SimpleBitmapDisplayer()正常显示一张图片
*/
}MyApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32package com.huatec.edu.mobileshop.common;
import android.app.Application;
import android.content.Context;
//import com.huatec.edu.mobileshop.db.GreenDaoManager;
//import com.huatec.edu.mobileshop.http.HttpMethods;
/**
* Created by Administrator on 2016/8/15.
*/
public class MyApplication extends Application {
private static Context mContext;
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
//greenDao全局配置
// GreenDaoManager.getInstance();
//全局配置image-loader
ImageLoaderManager.getInstance();
//全局配置Retrofit
// HttpMethods.getInstance();
}
public static Context getContext() {
return mContext;
}
}
引导页
引导页这里设计一个进度条动画,从左到右,动画结束跳转广告页
引导页布局 activity_splash.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.SplashActivity"
android:background="@drawable/splash_bg">
<ImageView
android:id="@+id/splash_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="72.5dp"
android:layout_marginTop="512dp"
android:layout_marginEnd="72.5dp"
app:srcCompat="@drawable/splash_logo" />
<RelativeLayout
android:id="@+id/relativeLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/splash_logo"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/splash_loading_bg" />
<ImageView
android:id="@+id/splash_loading_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="@drawable/splash_loading_item" />
</RelativeLayout>
</RelativeLayout>进度条动画 splash_loading.xml
1
2
3
4
5
6
7
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="60%p"
android:fillAfter="true" />进度条从左到右动画 push_left_in.xml、push_left_out.xml
1
2
3
4
5
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%" android:toXDelta="0"
android:duration="600" />
</set>
1 |
|
- SplashActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package com.huatec.edu.mobileshop.activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import com.huatec.edu.mobileshop.R;
public class SplashActivity extends AppCompatActivity {
private ImageView mSplashItem_iv;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
//去掉顶部标题
getSupportActionBar().hide();
initView();
}
private void initView() {
mSplashItem_iv = findViewById(R.id.splash_loading_item);
Animation translate = AnimationUtils.loadAnimation(this, R.anim.splash_loading);
translate.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
startActivity(new Intent(SplashActivity.this, AdActivity.class));
overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
finish();
}
public void onAnimationRepeat(Animation animation) {
}
});
mSplashItem_iv.setAnimation(translate);
}
}
广告页
广告页这里设计从网络加载图片,三秒跳转主页,点击跳过直接跳转主页
activity_ad.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.AdActivity"
android:orientation="vertical">
<ImageView
android:id="@+id/ad_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ad"
android:layout_gravity="center" />
<Button
android:id="@+id/skip_button"
android:layout_width="50dip"
android:layout_height="30dp"
android:layout_gravity="right"
android:layout_marginBottom="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
android:background="#FFFFFF"
android:gravity="center"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:text="跳过"
android:textSize="12.0sp"/>
</FrameLayout>AdActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94package com.huatec.edu.mobileshop.activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import com.huatec.edu.mobileshop.R;
import com.huatec.edu.mobileshop.common.Constants;
import com.huatec.edu.mobileshop.common.ImageLoaderManager;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
public class AdActivity extends AppCompatActivity {
private ImageView adImage;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ad);
//去掉顶部标题
getSupportActionBar().hide();
adImage = findViewById(R.id.ad_image);
//加载广告图
loadAd(Constants.AD_URL);
}
private void loadAd(String url) {
ImageLoader.getInstance().displayImage(url, adImage,
ImageLoaderManager.product_options, new ImageLoadingListener() {
public void onLoadingStarted(String imageUri, View view) {
}
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
//直接跳转
skip();
}
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
adImage.setImageBitmap(loadedImage);
//倒数3秒跳转
timer();
}
public void onLoadingCancelled(String imageUri, View view) {
}
});
}
//过3秒后跳转
private void timer() {
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == -1)
skip();
}
};
new Thread() {
public void run() {
try {
Thread.sleep(Constants.AD_TIME_SECOND);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(-1);
}
}.start();
}
/**
* 跳过
*/
private void skip() {
startActivity(new Intent(AdActivity.this, MainActivity.class));
}
}