20189200餘超 2018-2019-2 移動平臺應用開發實踐做項目代碼分析

20189200餘超 2018-2019-2 移動平臺應用開發實踐做項目代碼分析

項目名稱

小說閱讀器java

項目功能

註冊登陸
用戶信息、用戶密碼、用戶圖像修改
書籍分類
書架
書籍搜索(做者名或書籍名)
書籍閱讀(僅txt格式,暫不支持PDF等其餘格式)
閱讀字體、背景顏色、翻頁效果等設置
意見反饋(反饋信息發送到個人郵箱)數據庫

項目簡介

本項目主要是一個小說閱讀軟件,我所作的小說閱讀軟件是一個能夠根據用戶的閱讀興趣、愛好、來進行完成的。第一,個人項目功能裏面有註冊用戶的,用戶能夠進行註冊和登陸。第二,用戶信息功能,用戶能夠查看本身的信息,修改本身的密碼,修改用戶圖像等。第三,我還根據書的特色進行了書架的分類,分爲了玄幻、奇幻、武俠、仙俠、都市、職場、歷史、軍事等。第四,書籍搜索功能,也便是用戶能夠根據小說的名字或者做者來進行搜索相應的小說。第五,書籍的閱讀僅限於txt格式的閱讀。第六,用戶本身能夠設置用戶的閱讀字體、背景顏色、翻頁效果等功能。第七,用戶能夠進行信息的反饋,且發送到個人郵箱裏面。json

項目的運行截圖

使用開源庫

Rx2網絡封裝 RxHttpUtils
6.0權限庫 RxPermissions
Glide圖片加載庫 Glide
下拉刷新庫 SmartRefreshLayout
RecyclerView簡化框架 BaseRecyclerViewAdapterHelper
MD風格Dialog material-dialogs
TabLaout選擇 NavigationTabStrip
數據加載動畫 Android-SpinKit
展開摺疊TextView ExpandTextView
流式標籤 FlowLayout
數據庫 greenDAO
版本更新進度條 NumberProgressBar
圖片選擇器 TakePhoto
項目首頁- GanK -在基礎上修改api

代碼組成部分

1-1代碼模塊截圖網絡

Java代碼
app

資源代碼
框架

配置文件代碼
ide

以上是本項目的總體代碼結構工具

1-2代碼模塊分析
api:該包主要是用於網絡接口的代碼
db:該包主要是數據庫鏈接,數據存儲代碼
event:該包主要是用於事件回調後的通知
Intenfces:該包主要是用於接口的編寫
Model:該包主要是用於實體類的編寫,如小說,用戶等實體類
Util:該包主要是用於工具類代碼的編寫
View:該包主要是用於app界面的編寫
Viewmodel:該包主要是用於界面數據顯示的實體
Widget:該包用於app界面裝置代碼的編寫單元測試

Anim:主要存放自定義按鈕樣式
Color:主要是用於存放顏色代碼
Drawable:主要是用於存放圖片等靜態文件
Layout:主要是用於存放界面的代碼
Mipmap-*:用於適配各類分辨的圖標
Raw:存放文件 如txt
Values:存放各類文本變量
Xml:存放配置文件 如文件路徑等

主要使用Android mvp開發模式進行開發,該模式有如下幾個優勢
一、模型與視圖徹底分離,咱們能夠修改視圖而不影響模型
二、能夠更高效地使用模型,由於全部的交互都發生在一個地方——Presenter內部
三、咱們能夠將一個Presenter用於多個視圖,而不須要改變Presenter的邏輯。這個特性很是的有用,由於視圖的變化老是比模型的變化頻繁。
四、若是咱們把邏輯放在Presenter中,那麼咱們就能夠脫離用戶接口來測試這些邏輯(單元測試)

代碼調用關係

代碼調用關係

一:用戶管理

0:用戶註冊

由VMUserRegisterInfo類中的register()對BookService中的接口進行調用,完成整個的註冊功能

2:登陸功能

由VMUseLoginInfo類中的login方法對BookService中的接口調用 完成登陸流程

3:修改密碼功能

由VMUseLoginInfo類中的updatePassword方法對BookService中的接口調用 完成修改密碼流程

4:更新我的信息

由VMUseLoginInfo類中的updateUserInfo方法對BookService中的接口調用 完成更新我的信息流程

5:更新頭像

由VMUseLoginInfo類中的uploadAvatar方法對BookService中的接口調用 完成更新頭像流程

從網絡接口獲取電子書

1 獲取全部的分類
由VMBookClassify類中的bookClassify方法對BookService中的接口進行調用,完成整個對分類的獲取

2:獲取分類下的書籍
由VMBookClassify類中的getBooks方法對BookService中的接口進行調用,完成整個對分類的獲取

3:獲取書籍信息
由VMBookClassify類中的bookInfo方法對BookService中的接口進行調用,完成整個對書籍信息的獲取

4 獲取書籍目錄
由VMBookClassify類中的setBookInfo方法對BookService中的接口進行調用,完成整個對書籍目錄的獲取

核心代碼分析

1:網絡爬蟲
該app的書籍信息主要來自於互聯網,咱們經過fider對追書神器的網絡請求進行抓取,獲取http請求,隨後,咱們在本app中使用Rxjava框架,進行對抓取的http連接進行請求,獲取數據後封裝顯示。如下是核心代碼

/**
 * 獲取書籍信息
 *
 * @param bookid
 */
public void bookInfo(String bookid) {
    iBookDetail.showLoading();
    RxHttpUtils.getSInstance().addHeaders(tokenMap())
            .createSApi(BookService.class).bookInfo(bookid)
            .compose(Transformer.switchSchedulers())
            .subscribe(new RxObserver<BookBean>() {
                @Override
                protected void onError(String errorMsg) {
                    iBookDetail.stopLoading();
                }

                @Override
                protected void onSuccess(BookBean bookBean) {
                    iBookDetail.stopLoading();
                    iBookDetail.getBookInfo(bookBean);
                }
            });
}
public void bookClassify() {
    if (!NetworkUtils.isConnected()) {
        if (mIBookClassify != null) {
            mIBookClassify.NetWorkError();
        }
        return;
    }

    RxHttpUtils.getSInstance().addHeaders(tokenMap()).createSApi(BookService.class)
   /* RxHttpUtils.createApi(BookService.class)*/
            .bookClassify()
            .compose(Transformer.switchSchedulers())
            .subscribe(new RxObserver<BookClassifyBean>() {
                @Override
                protected void onError(String errorMsg) {
                    if (mIBookClassify != null) {
                        mIBookClassify.stopLoading();
                        mIBookClassify.errorData(errorMsg);
                    }
                }

                @Override
                protected void onSuccess(BookClassifyBean data) {
                    if (mIBookClassify != null) {
                        mIBookClassify.stopLoading();
                        if (data == null) {
                            mIBookClassify.emptyData();
                            return;
                        }
                        mIBookClassify.getBookClassify(data);
                    }


                }

                @Override
                public void onSubscribe(Disposable d) {
                    addDisposadle(d);
                }
            });
}
public void setBookInfo(CollBookBean collBookBean) {
        LoadingHelper.getInstance().showLoading(mContext);
        if (CollBookHelper.getsInstance().findBookById(collBookBean.get_id()) == null) {
            RxHttpUtils.getSInstance().addHeaders(tokenMap()).createSApi(BookService.class)
                    .bookChapters(collBookBean.get_id())
                    .compose(Transformer.switchSchedulers())
                    .subscribe(new RxObserver<BookChaptersBean>() {
                        @Override
                        protected void onError(String errorMsg) {
                            LoadingHelper.getInstance().hideLoading();
                        }

                        @Override
                        protected void onSuccess(BookChaptersBean data) {
                            LoadingHelper.getInstance().hideLoading();
                            List<BookChapterBean> bookChapterList = new ArrayList<>();
                            for (BookChaptersBean.ChatpterBean bean : data.getChapters()) {
                                BookChapterBean chapterBean = new BookChapterBean();
                                chapterBean.setBookId(data.getBook());
                                chapterBean.setLink(bean.getLink());
                                chapterBean.setTitle(bean.getTitle());
//                                chapterBean.setTaskName("下載");
                                chapterBean.setUnreadble(bean.isRead());
                                bookChapterList.add(chapterBean);
                            }
                            collBookBean.setBookChapters(bookChapterList);
                            CollBookHelper.getsInstance().saveBookWithAsync(collBookBean);
                            iBookShelf.bookInfo(collBookBean);
                        }

                        @Override
                        public void onSubscribe(Disposable d) {
                            addDisposadle(d);
                        }
                    });
        } else {
            LoadingHelper.getInstance().hideLoading();
            iBookShelf.bookInfo(collBookBean);
        }


    }
private void getBooksByTag() {
    Map<String, Object> map = new HashMap<>();
    map.put("access-token", SharedPreUtils.getInstance().getString("token", "weyue"));
    map.put("app-type", "Android");
    RxHttpUtils.getSInstance().addHeaders(map).createSApi(BookService.class)
            .booksByTag(tagName, page)
            .compose(Transformer.switchSchedulers())
            .subscribe(new RxObserver<List<BookBean>>() {
                @Override
                protected void onError(String errorMsg) {
                    mRefreshLayout.finishLoadmore();
                }

                @Override
                protected void onSuccess(List<BookBean> data) {
                    mRefreshLayout.finishLoadmore();
                    mBeans.addAll(data);
                    if (mBeans.size() > 0) {
                        mBookTagsAdapter.notifyDataSetChanged();
                    }
                }

                @Override
                public void onSubscribe(Disposable d) {
                    super.onSubscribe(d);
                    mDisposable = d;
                }
            });

}

本身實現功能分析

1 :用戶管理模塊
該模塊主要是有用戶登陸,用戶註冊,修改密碼,修改我的信息,修改頭像這幾個功能。
(1)用戶登陸
功能分析:用戶在界面輸入用戶名和密碼後,經過http請求後臺接口,驗證用戶名和密碼。完成整個登陸流程

@OnClick({R.id.ctv_register, R.id.fab})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.ctv_register:
                startActivityForResult(new Intent(this, RegisterActivity.class), 10000);
                break;
            case R.id.fab:
                String username = mActvUsername.getText().toString();
                String password = mEtPassword.getText().toString();

                if (TextUtils.isEmpty(username)) {
                    ToastUtils.show("用戶名不能爲空");
                    return;
                }
                if (TextUtils.isEmpty(password)) {
                    ToastUtils.show("密碼不能爲空");
                    return;
                }
                mModel.login(username, password);
                break;
        }


    }

(2)用戶註冊
功能分析:用戶在未登陸的狀況下,能夠查閱電子書,可是沒法對喜歡的電子書進行添加到書架的操做,這時,我app會自動跳轉到註冊頁面,提示用戶註冊後可使用相映的功能,當用戶輸入用戶名和密碼後,請求後臺提供的接口,若是用戶名存在,則註冊失敗,不然,註冊成功,並對用戶密碼進行md5加密後存入數據庫

@Override
    protected void initView() {
        super.initView();
        initThemeToolBar("用戶註冊");

        mFab.setOnClickListener(v -> {
            mUsername = mActvUsername.getText().toString();
            mPassword1 = mEtPassword.getText().toString();
            String password2 = mEtPasswordConfirm.getText().toString();
            if (TextUtils.isEmpty(mUsername)) {
                ToastUtils.show("用戶名不能爲空");
                return;
            }
            if (TextUtils.isEmpty(mPassword1) || TextUtils.isEmpty(password2)) {
                ToastUtils.show("密碼不能爲空");
                return;
            }
            if (!mPassword1.equals(password2)) {
                ToastUtils.show("兩次輸入密碼不同");
                return;
            }
            mModel.register(mUsername, mPassword1);
        });
    }

(3)修改密碼
功能分析:用戶點擊修改密碼後,首先對原密碼進行驗證,驗證成功後則修改爲功。不然修改失敗

public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.fab_edit_password:
                mFabMenu.toggle();
                new MaterialDialog.Builder(this)
                        .title("修改用戶密碼")
                        .inputRange(2, 20, ThemeUtils.getThemeColor())
//                        .inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD)
                        .input("請輸入新密碼", null, (dialog, input) -> {
                            dialog.dismiss();
                            mModel.updatePassword(input.toString());
                        })
                        .show();
                break;
            case R.id.fab_edit_userinfo:
                mFabMenu.toggle();
                startEdit();
                break;
            case R.id.iv_avatar:
                /**
                 * 設置內容區域爲簡單列表項
                 */
                final String[] items = {"相冊", "拍攝"};
                new MaterialDialog.Builder(this)
                        .title("選擇照片方式")
                        .items(items)
                        .itemsCallback((dialog, itemView, position, text) -> {
                            switch (position) {
                                case 0:
                                    dialog.dismiss();
                                    imageUri = getImageCropUri();
                                    //從相冊中選取圖片並裁剪
                                    takePhoto.onPickFromGalleryWithCrop(imageUri, cropOptions);
                                    //從相冊中選取不裁剪
                                    //takePhoto.onPickFromGallery();
                                    break;
                                case 1:
                                    dialog.dismiss();
                                    imageUri = getImageCropUri();
                                    //拍照並裁剪
                                    takePhoto.onPickFromCaptureWithCrop(imageUri, cropOptions);
                                    //僅僅拍照不裁剪
                                    //takePhoto.onPickFromCapture(imageUri);
                                    break;
                            }
                        })
                        .show();
                break;
            case R.id.btn_confirm:
                new MaterialDialog.Builder(this)
                        .title("修改用戶信息")
                        .content("是否確認修改?")
                        .negativeText("取消")
                        .onNegative((dialog, which) -> dialog.dismiss())
                        .positiveText("肯定")
                        .onPositive((dialog, which) -> {
                            String nickname = mEtNickName.getText().toString();
                            String brief = mEtBrief.getText().toString();
                            if (TextUtils.isEmpty(nickname)) {
                                ToastUtils.show("暱稱不能爲空");
                                return;
                            }
                            if (TextUtils.isEmpty(brief)) {
                                ToastUtils.show("個人格言不能爲空");
                                return;
                            }
                            stopEdit();
                            dialog.dismiss();
                            mModel.updateUserInfo(nickname, brief);
                        })
                        .show();
                break;
        }
    }

(4)修改我的信息
功能分析:用戶能夠再app我的系信息界面修改本身的信息,新的信息填寫完成後,點擊保存,則完成整個信息的修改。

public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.fab_edit_password:
                mFabMenu.toggle();
                new MaterialDialog.Builder(this)
                        .title("修改用戶密碼")
                        .inputRange(2, 20, ThemeUtils.getThemeColor())
//                        .inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD)
                        .input("請輸入新密碼", null, (dialog, input) -> {
                            dialog.dismiss();
                            mModel.updatePassword(input.toString());
                        })
                        .show();
                break;
            case R.id.fab_edit_userinfo:
                mFabMenu.toggle();
                startEdit();
                break;
            case R.id.iv_avatar:
                /**
                 * 設置內容區域爲簡單列表項
                 */
                final String[] items = {"相冊", "拍攝"};
                new MaterialDialog.Builder(this)
                        .title("選擇照片方式")
                        .items(items)
                        .itemsCallback((dialog, itemView, position, text) -> {
                            switch (position) {
                                case 0:
                                    dialog.dismiss();
                                    imageUri = getImageCropUri();
                                    //從相冊中選取圖片並裁剪
                                    takePhoto.onPickFromGalleryWithCrop(imageUri, cropOptions);
                                    //從相冊中選取不裁剪
                                    //takePhoto.onPickFromGallery();
                                    break;
                                case 1:
                                    dialog.dismiss();
                                    imageUri = getImageCropUri();
                                    //拍照並裁剪
                                    takePhoto.onPickFromCaptureWithCrop(imageUri, cropOptions);
                                    //僅僅拍照不裁剪
                                    //takePhoto.onPickFromCapture(imageUri);
                                    break;
                            }
                        })
                        .show();
                break;
            case R.id.btn_confirm:
                new MaterialDialog.Builder(this)
                        .title("修改用戶信息")
                        .content("是否確認修改?")
                        .negativeText("取消")
                        .onNegative((dialog, which) -> dialog.dismiss())
                        .positiveText("肯定")
                        .onPositive((dialog, which) -> {
                            String nickname = mEtNickName.getText().toString();
                            String brief = mEtBrief.getText().toString();
                            if (TextUtils.isEmpty(nickname)) {
                                ToastUtils.show("暱稱不能爲空");
                                return;
                            }
                            if (TextUtils.isEmpty(brief)) {
                                ToastUtils.show("個人格言不能爲空");
                                return;
                            }
                            stopEdit();
                            dialog.dismiss();
                            mModel.updateUserInfo(nickname, brief);
                        })
                        .show();
                break;
        }
    }

(5)修改頭像
功能分析:用戶能夠選擇本身喜歡的頭像,點擊頭像後會提示用戶選擇新的照片做爲本身的頭像,提交後保存到數據庫,完成整個模塊的修改

public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.fab_edit_password:
                mFabMenu.toggle();
                new MaterialDialog.Builder(this)
                        .title("修改用戶密碼")
                        .inputRange(2, 20, ThemeUtils.getThemeColor())
//                        .inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD)
                        .input("請輸入新密碼", null, (dialog, input) -> {
                            dialog.dismiss();
                            mModel.updatePassword(input.toString());
                        })
                        .show();
                break;
            case R.id.fab_edit_userinfo:
                mFabMenu.toggle();
                startEdit();
                break;
            case R.id.iv_avatar:
                /**
                 * 設置內容區域爲簡單列表項
                 */
                final String[] items = {"相冊", "拍攝"};
                new MaterialDialog.Builder(this)
                        .title("選擇照片方式")
                        .items(items)
                        .itemsCallback((dialog, itemView, position, text) -> {
                            switch (position) {
                                case 0:
                                    dialog.dismiss();
                                    imageUri = getImageCropUri();
                                    //從相冊中選取圖片並裁剪
                                    takePhoto.onPickFromGalleryWithCrop(imageUri, cropOptions);
                                    //從相冊中選取不裁剪
                                    //takePhoto.onPickFromGallery();
                                    break;
                                case 1:
                                    dialog.dismiss();
                                    imageUri = getImageCropUri();
                                    //拍照並裁剪
                                    takePhoto.onPickFromCaptureWithCrop(imageUri, cropOptions);
                                    //僅僅拍照不裁剪
                                    //takePhoto.onPickFromCapture(imageUri);
                                    break;
                            }
                        })
                        .show();
                break;
            case R.id.btn_confirm:
                new MaterialDialog.Builder(this)
                        .title("修改用戶信息")
                        .content("是否確認修改?")
                        .negativeText("取消")
                        .onNegative((dialog, which) -> dialog.dismiss())
                        .positiveText("肯定")
                        .onPositive((dialog, which) -> {
                            String nickname = mEtNickName.getText().toString();
                            String brief = mEtBrief.getText().toString();
                            if (TextUtils.isEmpty(nickname)) {
                                ToastUtils.show("暱稱不能爲空");
                                return;
                            }
                            if (TextUtils.isEmpty(brief)) {
                                ToastUtils.show("個人格言不能爲空");
                                return;
                            }
                            stopEdit();
                            dialog.dismiss();
                            mModel.updateUserInfo(nickname, brief);
                        })
                        .show();
                break;
        }
    }

電子書模塊

該模塊主要是從互聯網上獲取電子書資源

(1)獲取追書神器url
功能分析:該功能主要是通fiddler抓取追書神器的網絡請求 由此能夠獲取到電子書相關的url
(2)數據解析
功能分析:該功能主要是經過http請求獲取抓取到的url請求中的數據,而後封裝成java對象,用戶頁面上內容的展現,格式爲json
/**
* 一、判斷本地數據庫有沒有收藏書籍的數據。
* 二、本地數據庫沒有收藏書籍數據就網絡請求。不然就取本地數據

@param collBookBean
*/

public void setBookInfo(CollBookBean collBookBean) {
        LoadingHelper.getInstance().showLoading(mContext);
        if (CollBookHelper.getsInstance().findBookById(collBookBean.get_id()) == null) {
            RxHttpUtils.getSInstance().addHeaders(tokenMap()).createSApi(BookService.class)
                    .bookChapters(collBookBean.get_id())
                    .compose(Transformer.switchSchedulers())
                    .subscribe(new RxObserver<BookChaptersBean>() {
                        @Override
                        protected void onError(String errorMsg) {
                            LoadingHelper.getInstance().hideLoading();
                        }

                        @Override
                        protected void onSuccess(BookChaptersBean data) {
                            LoadingHelper.getInstance().hideLoading();
                            List<BookChapterBean> bookChapterList = new ArrayList<>();
                            for (BookChaptersBean.ChatpterBean bean : data.getChapters()) {
                                BookChapterBean chapterBean = new BookChapterBean();
                                chapterBean.setBookId(data.getBook());
                                chapterBean.setLink(bean.getLink());
                                chapterBean.setTitle(bean.getTitle());
//                                chapterBean.setTaskName("下載");
                                chapterBean.setUnreadble(bean.isRead());
                                bookChapterList.add(chapterBean);
                            }
                            collBookBean.setBookChapters(bookChapterList);
                            CollBookHelper.getsInstance().saveBookWithAsync(collBookBean);
                            iBookShelf.bookInfo(collBookBean);
                        }

                        @Override
                        public void onSubscribe(Disposable d) {
                            addDisposadle(d);
                        }
                    });
        } else {
            LoadingHelper.getInstance().hideLoading();
            iBookShelf.bookInfo(collBookBean);
        }

    public void bookClassify() {
        if (!NetworkUtils.isConnected()) {
            if (mIBookClassify != null) {
                mIBookClassify.NetWorkError();
            }
            return;
        }

        RxHttpUtils.getSInstance().addHeaders(tokenMap()).createSApi(BookService.class)
       /* RxHttpUtils.createApi(BookService.class)*/
                .bookClassify()
                .compose(Transformer.switchSchedulers())
                .subscribe(new RxObserver<BookClassifyBean>() {
                    @Override
                    protected void onError(String errorMsg) {
                        if (mIBookClassify != null) {
                            mIBookClassify.stopLoading();
                            mIBookClassify.errorData(errorMsg);
                        }
                    }

                    @Override
                    protected void onSuccess(BookClassifyBean data) {
                        if (mIBookClassify != null) {
                            mIBookClassify.stopLoading();
                            if (data == null) {
                                mIBookClassify.emptyData();
                                return;
                            }
                            mIBookClassify.getBookClassify(data);
                        }


                    }

                    @Override
                    public void onSubscribe(Disposable d) {
                        addDisposadle(d);
                    }
                });
    }

隊友項目的app運行結果

別踩白塊

推箱子

相關文章
相關標籤/搜索