目 录
一、概述11
1.1 课题描述11
1.2 需求分析22
1.3 开发环境33
二、分析与概要设计55
2.1 系统功能分析55
2.2 系统模块结构图66
2.3 数据库表的设计66
三、旅游系统的登录功能模块的详细设计88
3.1 登录模块的功能描述88
3.2 登录模块的界面布局的设计99
3.3 注册模块的Activity的设计1111
3.3.1 添加用户模块信息1414
3.3.2 删除用户模块信息1616
四、北京旅游系统的旅游攻略功能模块的详细设计2121
4.1 旅游攻略模块的功能描述2121
4.2 旅游攻略模块的界面布局的设计2222
4.3 旅游攻略模块的Activity的设计2424
4.3.1旅游攻略的列表的实现2626
4.3.2旅游攻略的菜单的实现2929
4.3.3 查找旅游攻略模块信息3131
五、系统测试3636
5.1 登录功能模块测试3636
5.2 景点攻略功能模块测试3636
5.3 社区评论功能模块测试3737
六、项目总结3838
七、参考文献3939
一、概述
1.1 课题描述
中国旅游业发展趋势众所周知已经越来越火爆,而北京的旅游景点更是许多人的第一选择,但是只从线下购票的方式很难实现持续发展。随着生活水平的不断提高,旅游成为人们生活不可或缺的一部分;旅游本是享受,然而传统旅游的一成不变的模式、千篇一律的线路,成为游客集中抱怨的焦点;因此,自助游越来越受到人们的青睐。
现在旅游行业不同往日,逐年的高速发展人们已经离不开网络了。以前旅游的方法和现在旅游已经有很大的不同,互联网可以让人们更好的生活,在想要旅游点击网络就可以获得大量的旅游攻略、景点介绍、小理票务、预订酒店等信息,利用网络的方便来旅行已经是大势所趋,利用互联网对于旅游爱好者来说方使了很多,也节省了很多的时间来做一些排队买票这类的事,有关旅游这方面的服务都得到了经济上的增长,现在旅游的趋势是旅游的主要竞争手段转移到了于机应用上,利用手机的便捷及手机应用的全面性取得了广大旅游用户的喜爱。
现在有关旅游的应用软件主攻的服务方向不同,其中一类是包容性的,也就是在一个应用上可以选择多个应用,另外一类具有专攻性质,如酒店预订功能,票务预订功能、旅游攻略功能等,不论它们倾向哪一种,每一位旅行爱好者的手机中都会有一款旅游应用。现在是物联网大数据时代,各种各样的信息将向每一个人袭来,手机中的旅游应用也会提供游客各种有关旅游的信息。
大部分使用者都希望有一种全面的旅游手机应用软件,所以制作手机旅游软件需要有更多的实用功能,任何一个使用者都可以在软件中找到自己想要的服务,这就促成了一体化服务的产生,这些用户想要功能在一起,就可以让用户更加快捷的去旅游,利用一些零散的时间,完全足够旅行者了解想要去的地方。这些年来手机上的旅游应用越来越多,让人们不再以以前单一的旅游方式活动,旅游是放松人精神的一种活动,可以让人们更加热爱生活享受生活,旅游应用逐年的改进,方便了广大的旅行爱好者,只要是用户有可能需要的服务,手机旅游应用中都会有,这就让游客感受到旅游是一种纯粹的亨受生活的方式,现在人们使用的手机应用有很多。
在科技飞速发展的今天,我们已经离不开网络,手机旅游应用中的各种功能不断改善,越来越强大,各种旅游软件公司都想让自己在旅游业有更强的竞争力,让更多的用户使用它们的产品。网络与手机的结合使旅行更加方便,因此旅游应用软件也越来越火爆,成为了人们必备的手机应用。随着自助游的快速发展,市场上关于旅游的应用程序方兴未艾,但针对旅游景点开发的应用程序尚处于不成熟阶段,所以此次我开发一个关于北京旅游的APP。
1.2 需求分析
1.2.1 技术需求
北京旅游App方便广大游客更容易针对性的对地方进行旅游攻略,北京旅游APP系统在Java、Android、XML、SQLite数据库上的技术应用需求极其广泛,包含界面和后端程序的开发。通过借助这些技术使整个前后端的衔接变得更加顺利,并且在开发程序时所使用的系统环境、应用环境上都有较为明显的适用体现,能够做到功能完善、整体架构清晰、整齐划一的操作流程。在对于数据库的建立和维护方面,都更贴合系统要求,实现数据正确安全,保证系统的顺利进行。同时,旅游APP对于整个类似系统的技术设计进行了详细的分析,这对于开发本系统所使用的技术环境提供了极大的帮助。
1.2.2 功能需求
(1)登录
首先进行登录操作,通过输入正确的用户名、密码即可登录成功,还有记住密码和自动登录功能,然后进入旅游首页界面,从而对数据进行相关操作,达到可视化的实时效果。
注册
在注册页面,可以进行游客信息的注册,能够选择账号和填写密码并添加到数据库中。
首页
首页头部有搜索,可以搜索旅游信息功能,往下就是一个轮播图,是北京旅游景点的特色地方,其次是各个景点的点击,能进行查看景点。
发现
采用瀑布式布局,发现页面可以实现旅游信息的查看,可查看景点的具体信息,点击图片进行具体历史文化的展示,包含RecyclerView展示各种景点,点击图片可以跳转到相应的景点介绍。
社区评论
展示各个景点评论的界面。
个人中心
游客信息展示,包含4个按钮,包含个人信息、账号安全、我的收藏、旅行日记4个按钮,可以进行自己的旅游心得记录。
1.3 开发环境
1.3.1 Android简介
Android是一种基于Linux的自由及开放源代码的操作系统,Android 分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和 Linux内核层。
Android有四大基本组件:Activity、Service、broadcast receiver、Content Provider。其中Activity是四大组件中最基本的一个,但基础并不等同于简单、不重要。恰恰相反,Activity作为基础组件,学好Activity就是我们开发者学好Android的前提。
1.3.2 Java简介
Java语言是一款非常热门的并且又具有代表性的编程语言,在实际应用过程中有着不可替代的作用。Java语言实际上是一种面向对象类型的编程语言,在实际开发领域中也会借助系统自带的功能去实现系统的设计,包含类、接口等功能的辅助实现,以此完成整个系统的设计。那么对于其中的接口来说,封装可以说是一种很好的辅助方式,可以按照人们传统的思想观念,实现父与子之间的功能的继承,免去编写代码时的重复度,简化复杂性,实现动态、多态的应用环境,达到标准的应用效果。Java语言实际上就是整个系统的后台,主要实现程序的后台管理,通过结合类、接口并配置数据库连接等操作来完成整个后端数据的动态显示,实现对存储数据的高效衔接和日常交互、维护的一体化管理。
1.3.3 XML简介
XML的简单易于在任何应用程序中读/写数据,这使XML很快成为数据交换语言,虽然不同的应用软件也支持其他的数据交换格式,但不久之后它们都将支持XML,那就意味着程序可以更容易的与Windows、Mac OS、Linux以及其他平台下产生的信息结合,然后可以很容易加载XML数据到程序中并分析它,并以XML格式输出结果。
扩展标记语言与Access,Oracle和SQL Server等数据库不同,数据库提供了更强有力的数据存储和分析能力,例如:数据索引、排序、查找、相关一致性等,可扩展标记语言仅仅是存储数据。事实上它与其他数据表现形式最大的不同是:可扩展标记语言极其简单,这是一个看上去有点琐细的优点,但正是这点使它与众不同。
1.3.4 SQLite数据库简介
SQLite是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。
1.3.5运行环境需求
操作系统: Android手机基于Linux 操作系统
Android系统要求无响应时间为5秒,所以旅游攻略、景点查看、社区评论、个人中心、发表看法等等操作的系统响应时间最长均不能超过5秒。
支持环境:Android 11-13版本。
二、系统分析与概要设计
2.1 系统功能分析
1.第一个页面是用户登录界面,有用户注册、用户登录、退出、记住我的下次再说功能。退出Dialog与用户交互。有记录登录功能(Sharedpreferences实现):设计注册和登陆界面,要求实现保存用户登录信息的功能,用户第一次登录时可设置是否记录密码和是否自动登录。如果用户勾选记住密码复选框,则下次登录时,会直接默认登录系统。用户如果想切换账号,可以先退出账号再登录。
2. 注册界面,若用户合法完成注册后返回到登录页面,并使用Toast提示注册成功,密码和账号都不能为空,否则会使用Toast提示账号不能为空,密码不能为空 。
3. 登录成功后,默认显示的页面是首页,下方导航栏的是旅游攻略、社区评论和个人中心。
4. 首页Fragment,上面有一个搜索框,搜索框下面紧挨着是每一个景点的介绍图,可以点击到关于北京海洋馆、南宫旅游景区、长城等的页面进行景点展示介绍。
发现Fragment,包含RecyclerView展示各种景点,点击图片可以跳转到相应的景点介绍。
6. 社区评论Fragment,展示各个景点评论的界面。
7.个人中心Fragment,包含个人信息、账号安全、我的收藏、旅行日记4个按钮,可以进行自己的旅游心得记录。
2.2 系统模块结构图
图2-1
2.3 数据库表的设计
用户管理模块
用户管理tb_user用来存储用户个人信息,主要包括username、passwordphone等字段。
tb_user表结构如表2-2所示:
表2-2 tb_user表
名称 类型 是否可空 备注
username text 是 登录名
password text 是 密码
景点信息管理模块
旅游信息表tb_goods用来存储景点信息,主要包括id、imgId、goodsName、goodsStock等字段。
t_goods表结构如表2-3所示:
表2-3 t_goods表
名称 类型 是否可空 备注
id integer 否 主键自动递增
imgId integer 是 图片id
goodsName text 是 景点名
客户端、服务器和数据库之间的访问过程图:
图2-2
3.1 登录模块的功能描述
登录模块的功能是游客在文本框中输入用户名和密码后,点击「登录」按钮,activity会获取到输入的用户名和密码,并且使用数据库进行条件查询,将查询到的数据放入cursor游标对象中,然后进行判断,若cursor能够进行游标下移操作,则证明用户名和密码正确,然后将从数据库中取出的id等字段数据添加到bundle中用,再将bundle通过intent.putExtras(bundle)方法存入intent以进行activity之间的数据传输,最后通过startActivity跳转到MActivity,登录成功。若输入的用户名和密码不正确或有未填写项,则使用Toast提示用户名或密码不正确。
3.2 登录模块的界面布局的设计
图3-1
public class LoginActivity extends Activity {
MySqliteOpenHelper helper = null;
private static final String TAG = 「LoginActivity」;
private Activity activity;
private ActionBar mTitleBar;//标题栏
private EditText etAccount;//手机号
private EditText etPassword;//密码
private TextView tvRegister;//注册
private Button btnLogin;//登录按钮
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity=this;
helper = new MySqliteOpenHelper(this);
setContentView(R.layout.activity_login);//加载页面
etAccount =(EditText) findViewById(R.id.et_account);//获取手机号
etPassword=(EditText)findViewById(R.id.et_password);//获取密码
tvRegister=(TextView)findViewById(R.id.tv_register);//获取注册
btnLogin=(Button)findViewById(R.id.btn_login);//获取登录
mTitleBar = (ActionBar)findViewById(R.id.myActionBar);
mTitleBar.setData(activity,」登录」,0, 0, 0,getResources().getColor(R.color.colorPrimary), new ActionBar.ActionBarClickListener() {
@Override
public void onLeftClick() {
finish();
}
@Override
public void onRightClick() {
}
});
登录页面使用了线性布局,利用一个ImageView图片控件、两个EditText和两个Button实现了用户名和密码的输入框以及登录和注册两个按钮。
3.3 注册模块的Activity的设计
图3-2
用户注册页面采用相对布局,注册页面结构上使用线性布局的排列,该页面含有若干个文本输入框和按钮,一个可点击的图片按钮和一个单选按钮组,可以满足用户填写信息的需要。
tvRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//跳转到注册页面
Intent intent=new Intent(activity, RegisterActivity. class);
startActivity(intent);
}
});
//设置点击按钮
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//关闭虚拟键盘
InputMethodManager inputMethodManager= (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(),0);
//获取请求参数
String account= etAccount.getText().toString();
String password=etPassword.getText().toString();
if (「」.equals(account)){//账号不能为空
Toast.makeText(activity,」账号不能为空」, Toast.LENGTH_LONG).show();
return;
}
if (「」.equals(password)){//密码为空
Toast.makeText(activity,」密码为空」, Toast.LENGTH_LONG).show();
return;
}
User mUser = null;
String sql = 「select * from user where account = ?」;
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = db.rawQuery(sql, new String[]{account});
if (cursor != null && cursor.getColumnCount() > 0) {
while (cursor.moveToNext()) {
Integer dbId = cursor.getInt(0);
String dbAccount = cursor.getString(1);
String dbPassword = cursor.getString(2);
String dbNickName = cursor.getString(3);
Integer dbAge = cursor.getInt(4);
String dbEmail = cursor.getString(5);
mUser = new User(dbId, dbAccount,dbPassword,dbNickName,dbAge,dbEmail);
}
}
db.close();
if (mUser != null) {
if (!password.equals(mUser.getPassword())) {
Toast.makeText(LoginActivity.this, 「密码错误」, Toast.LENGTH_SHORT).show();
}else{
SPUtils.put(LoginActivity.this,SPUtils.USER_ID,mUser.getId());
Intent intent = new Intent(LoginActivity.this, MainActivity. class);
startActivity(intent);
finish();
}
}else{
Toast.makeText(LoginActivity.this, 「账号不存在」, Toast.LENGTH_SHORT).show();
}
}
});
}
@Override
protected void onResume() {
super.onResume();
}
}
图3-3
3.3.1 添加用户模块信息
private void initView() {
User user = null;
String sql = 「select * from user where id = ?」;
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(userId)});
if (cursor != null && cursor.getColumnCount() > 0) {
while (cursor.moveToNext()) {
Integer dbId = cursor.getInt(0);
String dbAccount = cursor.getString(1);
String dbPassword = cursor.getString(2);
String dbNickName = cursor.getString(3);
Integer dbAge = cursor.getInt(4);
String dbEmail = cursor.getString(5);
user = new User(dbId, dbAccount,dbPassword,dbNickName,dbAge,dbEmail);
}
}
db.close();
if (user != null) {
tvAccount.setText(user.getAccount());
etNickName.setText(user.getNickName());
etAge.setText(String.valueOf(user.getAge()));
etEmail.setText(user.getEmail());
}
//保存
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = helper.getWritableDatabase();
String nickName = etNickName.getText().toString();
String age = etAge.getText().toString();
String email = etEmail.getText().toString();
if (「」.equals(nickName)) {
Toast.makeText(mActivity,」昵称不能为空」, Toast.LENGTH_SHORT).show();
return;
}
if (「」.equals(age)) {
Toast.makeText(mActivity,」年龄不能为空」, Toast.LENGTH_SHORT).show();
return;
}
if (「」.equals(email)) {
Toast.makeText(mActivity,」邮箱不能为空」, Toast.LENGTH_SHORT).show();
return;
}
String updateSql = 「update user set nickName=?,age=?,email=? where id =?」;
db.execSQL(updateSql,new Object[]{nickName,age,email,userId});
db.close();
Toast.makeText(mActivity,」保存成功」, Toast.LENGTH_SHORT).show();
finish();//关闭页面
}
});
}
图3-4
3.3.2 删除用户模块信息
public class CollectActivity extends AppCompatActivity {
MySqliteOpenHelper helper = null;
private Activity myActivity;
private ActionBar mTitleBar;//标题栏
private LinearLayout llEmpty;
private RecyclerView rvCollectList;
private CollectAdapter mCollectAdapter;
private List<Strategy> strategyList;
private Integer userId;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collect);
myActivity = this;
helper = new MySqliteOpenHelper(this);
rvCollectList = findViewById(R.id.rv_collect_list);
llEmpty = findViewById(R.id.ll_empty);
mTitleBar = (ActionBar) findViewById(R.id.myActionBar);
mTitleBar.setData(myActivity, 「我的收藏」, R.drawable.ic_back, R.drawable.ic_clear, 0, getResources().getColor(R.color.colorPrimary), new ActionBar.ActionBarClickListener() {
@Override
public void onLeftClick() {
finish();
}
@Override
public void onRightClick() {
AlertDialog.Builder dialog = new AlertDialog.Builder(myActivity);
dialog.setMessage(「确认要清空所有数据吗」);
dialog.setPositiveButton(「确定」, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SQLiteDatabase db = helper.getWritableDatabase();
String sql = 「delete from collect where userId=」+userId;
db.execSQL(sql);
db.close();
Toast.makeText(myActivity, 「删除成功」, Toast.LENGTH_SHORT).show();
loadData();
}
});
dialog.setNeutralButton(「取消」, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dialog.show();
}
});
initView();
}
图3-5
3.3.3 修改个人信息模块信息
public class PasswordActivity extends AppCompatActivity {
MySqliteOpenHelper helper = null;
private Activity activity;
private ActionBar mTitleBar;//标题栏
private EditText etEmail;
private EditText etNewPassword;
private Integer userId;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activity =this;
setContentView(R.layout.activity_password);
helper = new MySqliteOpenHelper(this);
userId = (Integer) SPUtils.get(activity,SPUtils.USER_ID,0);
etEmail = findViewById(R.id.et_email);
etNewPassword = findViewById(R.id.et_new_password);
mTitleBar = (ActionBar)findViewById(R.id.myActionBar);
mTitleBar.setData(activity,」重置密码」, R.drawable.ic_back, 0, 0,getResources().getColor(R.color.colorPrimary), new ActionBar.ActionBarClickListener() {
@Override
public void onLeftClick() {
finish();
}
@Override
public void onRightClick() {
}
});
}
图3-6
四、北京旅游系统的旅游攻略功能模块的详细设计
4.1 旅游攻略模块的功能描述
旅游攻略页面通过Fragment的方式放置在fragment_tab_home中的,fragment_tab_home使用了约束布局,在页面底部用了BottomNavigationView底部导航视图放置四个自定义的菜单导航按钮,用fragment标签填充页面内容,从而实现点击底部按钮跳转到相应fragment的功能,还有轮播图是实现以及图片的点击时间,旅游攻略点点击以及页面跳转。
页面使用了适配器来显示旅游景点的信息,采用的是相对布局,在布局文件中将高度设置为wrap_content,用来确保适配器加载完毕后不会出现一条数据独占一整个页面的情况,布局里面使用了一个ImageView和两个TextView来进行布局,定义轮播图居中显示和圆角的设定是页面更美化。
4.2 旅游攻略模块的界面布局的设计
图4-1
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myActivity = this;
setContentView(R.layout.activity_main);
MyApplication.Instance.setMainActivity(myActivity);
userId = (Integer) SPUtils.get(myActivity,SPUtils.USER_ID,0);
llContent = (LinearLayout) findViewById(R.id.ll_main_content);
rbStrategy = (RadioButton) findViewById(R.id.rb_main_strategy);
rbCommunity = (RadioButton) findViewById(R.id.rb_main_community);
rbUser = (RadioButton) findViewById(R.id.rb_main_user);
mActionBar = (ActionBar) findViewById(R.id.myActionBar);
//侧滑菜单
mActionBar.setData(myActivity,」旅游攻略」, 0, 0, 0, getResources().getColor(R.color.colorPrimary), new ActionBar.ActionBarClickListener() {
@Override
public void onLeftClick() {
}
@Override
public void onRightClick() {
}
});
initView();
setViewListener();
}
private void setViewListener() {
rbStrategy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mActionBar.setTitle(「旅游攻略」);
switchFragment(0);
}
4.3 旅游攻略模块的Activity的设计
图4-2
public class StrategyFragment extends Fragment {
MySqliteOpenHelper helper = null;
private Activity myActivity;//上下文
private RecyclerView rvList;
private StrategyAdapter strategyAdapter;
private LinearLayout llEmpty;
private EditText etQuery;//搜索内容
private ImageView ivSearch;//搜索图标
private List<Strategy> strategyList;
private Integer userId;
@Override
public void onAttach(Context context) {
super.onAttach(context);
myActivity = (Activity) context;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_strategy, container, false);
helper = new MySqliteOpenHelper(myActivity);
rvList = (RecyclerView) view.findViewById(R.id.rv_list);
llEmpty = view.findViewById(R.id.ll_empty);
etQuery = view.findViewById(R.id.et_query);
ivSearch = view.findViewById(R.id.iv_search);
//获取控件
initView();
//软键盘搜索
ivSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loadData();//加载数据
}
});
//点击软键盘中的搜索
etQuery.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
loadData();//加载数据
return true;
}
return false;
}
});
return view;
}
图4-3
4.3.1旅游攻略的列表的实现
图4-4
private void initView() {
userId = (Integer) SPUtils.get(myActivity, SPUtils.USER_ID, 0);
GridLayoutManager layoutManager = new GridLayoutManager(myActivity, 2);//两列
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
//=1.3、设置recyclerView的布局管理器
rvList.setLayoutManager(layoutManager);
HashMap<String, Integer> mapSpaces = new HashMap<>();//间距
mapSpaces.put(RecyclerViewSpaces.TOP_DECORATION, 20);//上间距
mapSpaces.put(RecyclerViewSpaces.BOTTOM_DECORATION, 20);//下间距
mapSpaces.put(RecyclerViewSpaces.LEFT_DECORATION, 20);//左间距
mapSpaces.put(RecyclerViewSpaces.RIGHT_DECORATION, 20);//右间距
rvList.addItemDecoration(new RecyclerViewSpaces(mapSpaces));//设置间距
//==2、实例化适配器
//=2.1、初始化适配器
strategyAdapter = new StrategyAdapter();
//=2.3、设置recyclerView的适配器
rvList.setAdapter(strategyAdapter);
loadData();
strategyAdapter.setItemListener(new StrategyAdapter.ItemListener() {
@Override
public void ItemClick(Strategy strategy) {
Intent intent = new Intent(myActivity, StrategyDetailActivity. class);
intent.putExtra(「strategy」, strategy);
startActivityForResult(intent, 100);
}
});
}
private void loadData() {
String contentStr = etQuery.getText().toString();//获取搜索内容
strategyList = new ArrayList<>();
Strategy strategy = null;
String sql = 「select * from strategy」;
if (!」」.equals(contentStr)){
sql+=」 where title like ?」;
}
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = db.rawQuery(sql,!」」.equals(contentStr)?new String[]{「%」+contentStr+」%」}:null);
if (cursor != null && cursor.getColumnCount() > 0) {
while (cursor.moveToNext()) {
Integer dbId = cursor.getInt(0);
String title = cursor.getString(1);
String img = cursor.getString(2);
String content = cursor.getString(3);
String ticket = cursor.getString(4);
strategy = new Strategy(dbId,title, img, content, ticket);
strategyList.add(strategy);
}
}
Collections.reverse(strategyList);
if (strategyList != null && strategyList.size() > 0) {
rvList.setVisibility(View.VISIBLE);
llEmpty.setVisibility(View.GONE);
strategyAdapter.addItem(strategyList);
} else {
rvList.setVisibility(View.GONE);
llEmpty.setVisibility(View.VISIBLE);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 100 && resultCode == RESULT_OK) {
loadData();//加载数据
}
}
}
4.3.2旅游攻略的菜单的实现
public class StrategyDetailActivity extends AppCompatActivity {
MySqliteOpenHelper helper = null;
private Activity mActivity;
private ImageView ivImg;
private TextView tvTitle;
private TextView tvTicket;
private TextView tvContent;
private EditText etContent;
private TextView tvLike;
private TextView tvCollect;
private ImageView ivvCollect;
private ImageView ivCollectCheck;
private ImageView ivLike;
private ImageView ivLikeCheck;
private ActionBar mActionBar;//标题栏
private SimpleDateFormat sf = new SimpleDateFormat(「yyyy-MM-dd HH:mm:ss」);
private Integer userId;
private SQLiteDatabase db;
private Strategy strategy;
private LinearLayout llEmpty;
private RecyclerView rvList;
private CommentAdapter commentAdapter;
private List<Comment> commentList;
private int praiseCount = 0;//点赞数
private int collectCount = 0;//收藏数
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = this;
setContentView(R.layout.activity_strategy_detail);
helper = new MySqliteOpenHelper(this);
ivImg = findViewById(R.id.img);
tvTitle = findViewById(R.id.title);
tvTicket = findViewById(R.id.ticket);
tvContent = findViewById(R.id.content);
etContent = findViewById(R.id.et_content);
tvLike = findViewById(R.id.tv_like);
tvCollect = findViewById(R.id.tv_collect);
ivvCollect = findViewById(R.id.iv_collect);
ivCollectCheck = findViewById(R.id.iv_collect_check);
ivLike = findViewById(R.id.iv_like);
ivLikeCheck = findViewById(R.id.iv_like_check);
llEmpty = findViewById(R.id.ll_empty);
rvList = findViewById(R.id.rv_list);
mActionBar = findViewById(R.id.myActionBar);
//侧滑菜单
mActionBar.setData(mActivity, 「明细信息」, R.drawable.ic_back, 0, 0, getResources().getColor(R.color.colorPrimary), new ActionBar.ActionBarClickListener() {
@Override
public void onLeftClick() {
finish();
}
@Override
public void onRightClick() {
}
});
userId = (Integer) SPUtils.get(mActivity, SPUtils.USER_ID, 0);
strategy = (Strategy) getIntent().getSerializableExtra(「strategy」);
tvTicket.setText(strategy.getTitle());
tvTitle.setText(String.format(「¥%s」, strategy.getTitle()));
tvContent.setText(strategy.getContent());
Glide.with(mActivity)
.asBitmap()
.skipMemoryCache(true)
.load(strategy.getImg())
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(ivImg);
db = helper.getWritableDatabase();
if (userId > 0) {//已登录
//查询点赞状态
String selectPraiseSql = 「select * from praise where articleId=」 + strategy.getId() + 」 and type=」 + ArticleTypeEnum.Strategy.getCode() + 」 and userId=」 + userId;
Praise praise = null;
Cursor cursorPraise = db.rawQuery(selectPraiseSql, null);
if (cursorPraise != null && cursorPraise.getColumnCount() > 0) {
while (cursorPraise.moveToNext()) {
Integer dbId = cursorPraise.getInt(0);
Integer dbArticleId = cursorPraise.getInt(1);
Integer dbUserId = cursorPraise.getInt(2);
Integer dbType = cursorPraise.getInt(3);
praise = new Praise(dbId, dbArticleId, dbUserId, dbType);
}
}
图4-5
4.3.3 查找旅游攻略模块信息
图4-6
图4-7
private void getCommentList() {
String commentSql = 「select c.*,u.nickName from comment c,user u where c.userId = u.id and c.type=」 + ArticleTypeEnum.Strategy.getCode() + 」 and c.articleId=」 + strategy.getId();
List<CommentVo> list = new ArrayList<>();
Cursor cursor1 = db.rawQuery(commentSql, null);
if (cursor1 != null && cursor1.getColumnCount() > 0) {
while (cursor1.moveToNext()) {
Integer dbId = cursor1.getInt(0);
Integer dbArticleId = cursor1.getInt(1);
Integer dbUserId = cursor1.getInt(2);
String dbContent = cursor1.getString(3);
String dbDate = cursor1.getString(4);
Integer dbType = cursor1.getType(5);
String nickName = cursor1.getString(6);
CommentVo comment = new CommentVo(dbId, dbArticleId, dbUserId, dbContent, dbDate, dbType, nickName);
list.add(comment);
}
}
if (list.size() > 0) {
commentAdapter.addItem(list);
rvList.setVisibility(View.VISIBLE);
llEmpty.setVisibility(View.GONE);
} else {
rvList.setVisibility(View.GONE);
llEmpty.setVisibility(View.VISIBLE);
}
}
//发表
public void publish(View view) {
if (userId == 0) {////未登录,跳转到登录页面
Toast.makeText(mActivity, 「请登录后操作」, Toast.LENGTH_SHORT).show();
}else {//已经登录
String content = etContent.getText().toString();
if (「」.equals(content)) {
Toast.makeText(mActivity, 「请输入查找内容」, Toast.LENGTH_SHORT).show();
return;
}
String insertSql = 「insert into comment(articleId,userId,content,date,type) values(?,?,?,?,?)」;
db.execSQL(insertSql, new Object[]{strategy.getId(), userId, content, sf.format(new Date()), ArticleTypeEnum.Strategy.getCode()});
Toast.makeText(mActivity, 「成功」, Toast.LENGTH_SHORT).show();
etContent.setText(「」);
getCommentList();
}
}
}
图4-8
五、系统测试
5.1 登录功能模块测试
5.1.1软件的测试环境
软硬件要求:系统环境 win7;硬件系统环境core i3、3G内存、500G硬盘。此外对其他软件几乎没有依赖性,程序健壮性较好。
5.1.2测试
建立一张模拟的SD卡,上传软件;测试软件的注册登录功能用户体验是否良好。
5.1.3测试阶段
按照软件测试的策略和过程分类,软件测试可分为单元测试、集成测试、确认测试、系统测试和验收测试。它们被依次顺序地执行,此模块实验主要是进行功能测试。
5.1.4测试结果
此北京旅游攻略软件登录模块用户体验良好,基本符合设计要求。
5.2 景点攻略功能模块测试
5.2.1 软件的测试环境
软硬件要求:系统环境 win7;硬件系统环境core i3、3G内存、500G硬盘。此外对其他软件儿乎没有依赖性,程序健壮性较好。
5.2.2测试
建立一张模拟的SD卡,上传软件;测试软件的景点攻略功能是否能完好查看、还有在线查看门票价格、在线评论功能是否顺畅、用户体验是否良好。
5.2.3软件测试的评价
软件功能评价:此北京旅游攻略软件景点攻略模块用户体验良好,基本符合设计要求。
结论:此软件的设计和论证是可行的。
5.2.4测试阶段
软件测试工程量大、过程复杂,在整个软件开发周期中占据着举足轻重的地位,需求调研和软什测试占据了软件开发三分之二时间。此模块实验主要是进行功能测试。
5.2.5测试结果
在课题后期进行了大力度的软件测试,按照测试的流程和规则,主要进行了功能测试。软件主要的 bug 表现为闪退、卡死等现象,在器上观看log则能发现主要问题是运行时异常。我们主要对bug采取了异常拦截的方式进行调试完善,测试完成后软件稳定运行。
5.3 社区评论功能模块测试
5.3.1软件的测试环境
软硬件要求:系统环境 win7;硬件系统环境core i3、3G内存、500G硬盘。此外对其他软件儿乎没有依赖性,程序健壮性较好。
5.3.2测试
建立一张模拟的SD卡,上传软件;测试软件的社区评论功能用户体验是否良好。
5.3.3测试阶段
对于软什测试我们需要从不同的角度考虑,明确测试的目的,精细化测试的过程,对测试过程进行明确的分类,制定好测试计划,尽量做到细致以及全面的测试。此模块实验主要是进行功能测试。
5.3.4测试结果
此北京旅游攻略软件社区评论模块用户体验良好,基本符合设计要求。
六、项目总结
本文主要是对Android平台北京旅游攻略的分析设计,主要实现了景点介绍、在线评论、景点购票、旅行日记、景点收藏、个人登录退出等功能。在本文中第一章简要的介绍了课题目前背景、研究现状等;第二章中讲述了Android的系统分析与概要设计;第三章进行了详尽的登录模块功能分析;第四章进行详细设计以及编码实现工作;最后的第五章进行了软件的测试工作,完善软件。本设计的北京旅游攻略软件能够稳定运行,体积小且反映灵敏,但还是有几点不足的地方,例如最近查看旅游景点无法排序、代码编写冗余重复率高、图片素材布局不太美观等等。在以后我会改进软件的不足,以更优的编码水平,更高超的布局思维模型,并且学习更多新的知识重构这款软件。这款北京旅游攻略软件还是比较好的,适用于智能手机市场,有良好的用户体验和反应速度。相信随着智能手机在发展中国家的普及,它们市场上会拥有庞大的用户群。
七、参考文献
[1]王兴中.中国旅游资源开发模式与旅游区域可持续发展理念[J.地理科学,2014,24 (1) : 51-58.
[2]王慧云.旅游教育与旅游业可持续发展[J].桂林旅游高等专科学校学报,2015,32(3) : 101-106.
[3]章锦河.中国国际旅游业与国际贸易比较研究〔J.安徽师大学报,2014,17 (2) : 101-106.
[4]柯元旦. Android内核剖析[M].北京:电子工业出版社,2012: 59-70.
[5]丰生强. Android软件安全与逆向分析[M].北京:人民邮电出版社,2013:78-90.
[6]余成锋,李代平,毛永华. Android3.0内存管理机制分析[M].北京:北京人民
邮电出版社,2013: 55-80.
[7]佐冰冰. Android平台下Launcher启动器的设计与实现[D].哈尔滨:哈尔滨工业大学博士学位论文. 2012: 4-9.
[8]杜吉志,徐明昆. Android系统内存管理研究及优化[J].软件工程,2012,24 (5): 69-80.
[9]高巍. Android操作系统软件自动化测试方案的设计与实施[D].北京:北京邮电大学博士学位论文.2014: 8-14.
[10〕孙剑. Android系统上应用程序按需加载机制的设计与实现 [M].北京:北京大学出版社,2014: 99-110.
[11]卢娜.基于Android平台的手机桌面资讯系统的设计与实现〔M].西安:西安电子科技大学出版社,2012: 290-300.
(注:此页为报告内容末页,打印在单面,与报告装订在一起)
课程报告评分:
评分点 得分
报告格式和排版(10分)
系统概要设计(20分)
系统详细设计(20分)
系统测试(20分)
总结(10分)
代码注释(20分)
总分(100分)
加分项(10分)
指导教师签字:
年 月 日