Android学习之网上商城(上)
前言
又到了課設的時候,這次課設比較難受,因為兩周時間中還有3門考試,在課設的時候還要復習,著實耗費了不少的精力,不過也收獲多多,接下來總結一下本次課設中學到的東西
Android學習之網上商城(上)
Android學習之網上商城(下)
源碼下載:
鏈接:https://pan.baidu.com/s/1Z17xBHV9iq70LwdgXxwDIQ
提取碼:Lin2
Android學習之網上商城源代碼
本博客內容原創,創作不易,轉載請注明
本文鏈接
個人博客:https://ronglin.fun/?p=118
PDF鏈接:見博客網站
CSDN: https://blog.csdn.net/RongLin02/article/details/121876257
開發環境:
- Android Studio版本:(Android Studio Arctic Fox 2020.3.1 Patch 3)
- SDK版本:(Android 7.0 API24 Revision 2)
- Gradle版本:(7.0.2)
- Android Gradle Plugin版本:(7.0.3)
選題
題目
本次選題為網上商城/外賣小助手。要求如下:
功能要求
- 要求實現商品展示、商品詳細介紹、下訂單、購物車。
- 要求實現用戶注冊、登錄、查看歷時訂單。
- 數據:可以采用靜態的固定的數據來模擬(如果動手能力較強,可以嘗試自己動手搭后臺,利用 Android 網絡編程)。
目的:
- 掌握 Android 中的菜單及導航框架。
- 掌握自定義布局。
- 掌握 Android 中的數據存儲。
分析
1. 商品展示
商品展示準備用一個ListView展示內容,主要包括商品的名稱、價格、預覽圖、加入購物車功能等
2. 商品詳細介紹
這個界面準備用一個自定義Dialog實現,主要是布局設計,展示商品的名稱、價格、預覽圖、描述、標簽等
3. 購物車
購物車用一個ListView維護,主要是用來顯示用戶的購物車內容,這個界面的主要功能就是對購物車中的商品刪除和下單扣費,因為是靜態數據,準備用ArrayList數組維護
4. 下訂單
暫定實現用戶的扣費和將訂單加入歷史訂單中,用ArrayList維護,主要是內容的增加和刪除
5. 注冊
用單獨的一個Activity實現,主要是用于用戶的注冊,有三個數據,第一個是用戶名,第二個是密碼,第三個是確認密碼,然后注冊成功之后將數據插入本地數據庫,用戶列表主要用SQLite存儲。
6. 登錄
登錄就是比對用戶輸入和數據庫中的數據是否匹配,匹配則登錄成功,失敗則提示
7. 歷史訂單
歷史訂單用SQLite存儲,主要記錄的是用戶的用戶名,商品名稱和購物時間
8. 數據
由于時間有限,本次Android設計主要是用靜態數據,商品數據由本地寫死,用戶信息用SQLite數據庫維護
效果展示
注冊登錄
實現賬號的注冊與登錄,當用戶注冊完成后,會將注冊好的賬號密碼自動填入登錄界面中
商品展示
商品的展示界面,點擊每一行展示商品的詳細信息,點擊+號可以將商品添加到購物車中
購物車
購物車中需要扣費,在個人中心可以充值,然后購物的時候可以扣費
用法
這個模塊主要用來說明,在本次安卓開發用主要用的部分,一個ListView,一個是viewBinding,一個是Fragment,還有一些其他的用法
ListView
ListView是這其中最常用的控件,包括商品展示,購物車列表展示,個人中心中的歷史清單列表,都是用的ListView顯示數據。
item
在ListView中,每一行都是一個item,所以說要用ListView首先就是先設計一個item的布局文件,如下圖
部分代碼如下:
這樣新建一個item,并且為其中每一個按鈕設置好id
BaseAdapter
設計完每一行的顯示之后還不行,還要用代碼來設計每一個item是如何顯示的,這里就用到了Adapter適配器,因為我的數據顯示比較復雜,只能用自定義的適配器,定義一個類,繼承自BaseAdapter類,然后實現其中的幾個抽象方法,如下
package com.ronglin.linshopping.application;public class GoodsListAdapter extends BaseAdapter{private ArrayList<Goods> list_goods;public GoodsListAdapter(ArrayList<Goods> list, Context context){this.list_goods = list;this.context = context;}public void setListGoods(ArrayList<Goods> list){this.list_goods = list;}@Overridepublic int getCount() {return list_goods.size();}@Overridepublic Object getItem(int i) {return list_goods.get(i);}@Overridepublic long getItemId(int i) {return i;}@Overridepublic View getView(int i, View view, ViewGroup viewGroup) {View item_view;item_view = View.inflate(this.context, R.layout.list_item_goods,null);//設置列表的顯示形式TextView textViewGoodsName = item_view.findViewById(R.id.textViewGoodsName);textViewGoodsName.setText(list_goods.get(i).getGoodsName());return item_view;} }這個GoodsListAdapter類就是用來控制每一行如何顯示的,為了實現動態數據,用了一個ArrayList實現數據存儲,然后顯示的數據都從ArrayList中提取這幾個方法簡單提示一下
getCount()用來獲取到底有多少行
getItem(int i)用來獲取第i行(從0開始)的數據類
getItemId(int i)用來獲取第i行(從0開始)的數據id
getView(int i, View view, ViewGroup viewGroup)用來設置第i行(從0開始)的顯示形式
Bottom Navigation Activity
因為要實現多個界面切換,在設計時看到Android Studio中的Activity的時候,看到了Bottom Navigation Activity,是用底邊欄按鈕切換界面,下面簡單介紹一下它的用法
文件分布
bottom_nav_menu.xml
首先是/res/menu下的bottom_nav_menu.xml,這個文件的作用是控制底邊欄的樣式,比如購物車的圖標,名稱之類的,基本格式如下:
文件中主要就是item,根據所查的資料,item的個數是3–5個,icon就是底邊欄的按鈕圖標,title就是底邊欄的名稱
mobile_navigation.xml
然后就是在/res/navigation下的mobile_navigation.xml,這個文件就是設置每一個item中面板內容,基本內容如下
android:name 是用來配置控制界面的類,格式是包名.類名
tools:layout 是用來設置每一個界面的布局文件
簡單使用
切換界面
如果想要切換手動的切換界面,要這樣使用
這句代碼的作用就是切換界面
然后就是Fragment的用法,關于Fragment下面還有用法說明
viewBinding
因為我用的是系統自動生成的Bottom Navigation Activity(底邊欄按鈕切換界面),在自動生成的代碼中,用到了viewBinding,查了一下資料,發現用起來很方便,這里簡單的說一下使用
build.gradle
首先要是想使用viewBinding,要在build.gradle(Module:xxx)中開啟viewBinding
android {compileSdk 30......buildFeatures {viewBinding true} }然后就可以用了
使用說明
開啟viewBinding后,它會把每一個layout目錄下的 xml 文件按照 駝峰命名法 生成了一個類,例如activity_main.xml文件就被生成類ActivityMainBinding然后就可以通過類的實例來訪問其中的控件
例如在test_layout.xml中如下定義
然后在Activity中就可以這樣使用
public class MainActivity extends AppCompatActivity {private TestLayoutBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = TestLayoutBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());binding.editTextSearch.setText("test");} }通過binding.Id名稱就可以訪問到特定的控件了,如果沒有在Activity中,只有Context對象的話,可以這樣初始化
binding = TestLayoutBinding.inflate(LayoutInflater.from(context));之后就基本上可以不用findViewById方法了,就可以直接用binding來訪問控件了。
注意:
當和ListView中的BaseAdapter一起使用時,不能用binding,還是要用findViewById方法,不知道是不是自己用錯了,幾次嘗試之后都顯示失敗,無果后只能放棄
Fragment
在使用系統自動生成的Bottom Navigation Activity時,它生成了3個Fragment。
以下部分內容來官方API文檔
Fragment 表示應用界面中可重復使用的一部分。Fragment 定義和管理自己的布局,具有自己的生命周期,并且可以處理自己的輸入事件。Fragment 不能獨立存在,而是必須由 Activity 或另一個 Fragment 托管。Fragment 的視圖層次結構會成為宿主的視圖層次結構的一部分,或附加到宿主的視圖層次結構。
生命周期
這是它的生命周期圖,和Activity很像,簡單說明一下,Fragment不能夠單獨使用,要嵌套在Activity中使用,其生命周期也受到所在Activity的生命周期的影響,需要注意的是,在多個Fragment之中的切換,會調用onDestroyView和onCreateView同時數據會清空,但是對應的類并沒有被銷毀重構,只是界面View被銷毀重構
ViewModel
在使用Bottom Navigation Activity的時候,會發現,每一個界面類還會跟隨一個ViewModel類,這個類主要是用來存儲數據,用來適配Controller和Model之間的橋梁,同時用ViewModel也可以在多個Fragment中實現數據共享,接下來簡單的說明用法
初始化
private GoodsViewModel goodsViewModel;private FragmentGoodsBinding binding;public View onCreateView(@NonNull LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {binding = FragmentGoodsBinding.inflate(inflater, container, false);View root = binding.getRoot();goodsViewModel =new ViewModelProvider(this).get(GoodsViewModel.class);return root;}可以看到ViewModel的初始化是在onCreateView中,所以說每當點擊切換Fragment的時候,ViewMode的數據都會重新初始化,這點要尤為注意
設置數據
先來簡單的看一下ViewModel類中的變量和方法
public class GoodsViewModel extends ViewModel {private final MutableLiveData<Goods> goods;public GoodsViewModel() {goods = new MutableLiveData<>();}public LiveData<Goods> getGoods() {return goods;}public void setGoods(Goods goods){this.goods.setValue(goods);} }主要的變量就是一個MutableLiveData<?>類,這個類可以動態監聽值的變化,在對應的Fragment類中可以看到以下方法
goodsViewModel.getGoods().observe(getViewLifecycleOwner(), new Observer<Goods>() {@Overridepublic void onChanged(Goods goods) {Log.i("TAG",Goods.toString());}});當類中Goods的值變化的時候就會自動執行onChanged的代碼,參數中的goods是變化之后的值
經過我的開發嘗試,只有在調用goodsViewModel.setGoods(goods)的時候才會被監聽到,所以說當用goodsViewModel.getGoods()方法獲取到數據之后,對數據的操作不會引起監聽變化,所以說當改變數據之后要調用一下goodsViewModel.setGoods()方法,如下:
這要變化之后就會調用監聽了
然后就是在其他的Fragment獲取數據
GoodsViewModel goodsViewModel; goodsViewModel = new ViewModelProvider(this).get(GoodsViewModel.class);SQLite
在Android中,數據庫是使用SQLite的,關于SQLite用法網上有很多資料,這里簡單說一下用法
創建
數據庫的創建是需要創建一個類來繼承自SQLiteOpenHelper,然后在類中實現它的抽象方法,如下:
public class MySQLiteHelper extends SQLiteOpenHelper {public MySQLiteHelper(@Nullable Context context) {super(context,"LinShopping.db", null, 1);}@Overridepublic void onCreate(SQLiteDatabase sqLiteDatabase) {sqLiteDatabase.execSQL("create table person(username varchar(30) primary key,password varchar(30))");}@Overridepublic void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {} }其中構造函數有很多,我這里只是實現了其中一個,主要是需要一個Context類來初始化,然后還要設置一下數據庫的名稱,這個數據庫的創建是在應用剛安裝的時候創建的,只會創建一次,然后在onCreate方法用來調用execSQL方法來創建新表
之后有關所有數據庫的操作都會用到這個SQLiteOpenHelper類
操作
我本人習慣將數據庫的操縱封裝成一個類來調用,所以說新建一個Database類來實現數據庫操作
public class Database {private MySQLiteHelper mySQLiteHelper;private SQLiteDatabase database;public Database(MySQLiteHelper mySQLiteHelper){this.mySQLiteHelper = mySQLiteHelper;} }因為獲取數據庫需要用到SQLiteOpenHelper類,所以在構造函數中就需要傳入一個SQLiteOpenHelper類。
同時可以用execSQL方法直接輸入SQL語句操作數據,這里不再說明
增
增加方法比較簡單,如下
public void insertPersonToSQLite(Person person){database = mySQLiteHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put("username",person.getUsername());values.put("password",person.getPassword());long id = database.insert("person",null,values);database.close();}需要定義一個ContentValues類來保存Key-Value對,然后通過insert方法插入數據庫
刪
刪除用法同樣比較簡單,如法如下:
public int deletePersonToSQLite(Person person){database = mySQLiteHelper.getWritableDatabase();int number = database.delete("person","username =?",new String[]{person.getUsername()});database.close();return number;}改
用法如下
public int updatePersonToSQLite(Person person){database = mySQLiteHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put("password",person.getPassword());int number = database.update("person",values,"username =?",new String[]{person.getUsername()});database.close();return number;}查
查詢方法因為需要返回很多數據,所以說用法稍微麻煩一點點,實例如下:
public ArrayList<Person> findPersonFromSQLite(String username){database = mySQLiteHelper.getReadableDatabase();ArrayList<Person> list = new ArrayList<>();Cursor cursor = database.query("person",null,"username=?",new String[]{person.getUsername()},null,null,null);if (cursor.getCount() == 0){cursor.close();database.close();return list;} else {cursor.moveToFirst();list.add(new Person(cursor.getString(0),cursor.getString(1)));while (cursor.moveToNext()){list.add(new Person(cursor.getString(0),cursor.getString(1)));}cursor.close();database.close();return list;}}簡單來說就是需要一個cursor游標來存儲返回的數據,然后通過操作游標來實現數據的獲取
其他功能
接下來是一些常用的小功能,用法
監聽文本框
有時候我們希望,我們的EditText只能輸入特定的內容或者當用戶輸入完畢后立刻處理結果,這樣就需要用到TextWatcher類了
binding.editTextSearch.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {String search = s.toString().trim();Log.i("TAG",search);}@Overridepublic void afterTextChanged(Editable editable) {} });簡單的說明一下用法
beforeTextChanged(CharSequence s, int start, int count, int after)
s: 修改之前的文字。
start: 字符串中即將發生修改的位置。
count: 字符串中即將被修改的文字的長度。如果是新增的話則為0。
after: 被修改的文字修改之后的長度。如果是刪除的話則為0。
onTextChanged(CharSequence s, int start, int before, int count)
s: 改變后的字符串
start: 有變動的字符串的序號
before: 被改變的字符串長度,如果是新增則為0。
count: 添加的字符串長度,如果是刪除則為0。
afterTextChanged(Editable s)
s: 修改后的文字
修改機制如下:文字改變->watcher接收到通知->setText->文字改變->watcher接受到通知->…
參考:
Android TextWatcher內容監聽死循環
可以實現限制用戶輸入
定時器
因為Android中的UI界面是一個單線程,所以說如果要實現一個定時器,比如幾秒之后干什么,有點小困難,先看實例代碼
@SuppressLint("HandlerLeak") Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {//這里寫到時間之后的操作}};TimerTask task = new TimerTask(){public void run() {Message message = new Message();mHandler.sendMessage(message);}};Timer timer = new Timer();timer.schedule(task, 1000);就是需要定義一個Handler類來處理消息,然后定義一個TimerTask類來發送消息,用一個Timer類來啟動
顯示圖片
圖片有很多類型,我這里用的是Bitmap類,從drawable目錄下構造Bitmap的方法如下:
Bitmap bitmap1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.renzituo));用這個方法在生成release版本的時候,同樣會顯示,同時操作圖片也很方便.
總結
到此,一些本次課設中常用功能實現就總結完畢,接下來就是對單獨某些模塊的實現總結,未完待續,=w=
總結
以上是生活随笔為你收集整理的Android学习之网上商城(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java redis hash_我爱ja
- 下一篇: linux svn 服务 关闭,Linu