當前位置: 華文世界 > 手機

安卓期末大作業——番茄小說APP

2023-12-27手機

目 錄

一、概述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分)
指導教師簽字:

年 月 日