20189200余超 2018-2019-2 移动平台应用开发实践作项目代码分析

20189200余超 2018-2019-2 移动平台应用开发实践作项目代码分析

项目名称

小说阅读器

项目功能

注册登录
用户信息、用户密码、用户图像修改
书籍分类
书架
书籍搜索(作者名或书籍名)
书籍阅读(仅txt格式,暂不支持PDF等其他格式)
阅读字体、背景颜色、翻页效果等设置
意见反馈(反馈信息发送到我的邮箱)

项目简介

本项目主要是一个小说阅读软件,我所做的小说阅读软件是一个可以根据用户的阅读兴趣、爱好、来进行完成的。第一,我的项目功能里面有注册用户的,用户可以进行注册和登录。第二,用户信息功能,用户可以查看自己的信息,修改自己的密码,修改用户图像等。第三,我还根据书的特点进行了书架的分类,分为了玄幻、奇幻、武侠、仙侠、都市、职场、历史、军事等。第四,书籍搜索功能,也即是用户可以根据小说的名字或者作者来进行搜索相应的小说。第五,书籍的阅读仅限于txt格式的阅读。第六,用户自己可以设置用户的阅读字体、背景颜色、翻页效果等功能。第七,用户可以进行信息的反馈,且发送到我的邮箱里面。

项目的运行截图

使用开源库

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 -在基础上修改

扫描二维码关注公众号,回复: 6249856 查看本文章

代码组成部分

1-1代码模块截图

Java代码

资源代码

配置文件代码

以上是本项目的整体代码结构

1-2代码模块分析
api:该包主要是用于网络接口的代码
db:该包主要是数据库连接,数据存储代码
event:该包主要是用于事件回调后的通知
Intenfces:该包主要是用于接口的编写
Model:该包主要是用于实体类的编写,如小说,用户等实体类
Util:该包主要是用于工具类代码的编写
View:该包主要是用于app界面的编写
Viewmodel:该包主要是用于界面数据显示的实体
Widget:该包用于app界面装置代码的编写

Anim:主要存放自定义按钮样式
Color:主要是用于存放颜色代码
Drawable:主要是用于存放图片等静态文件
Layout:主要是用于存放界面的代码
Mipmap-*:用于适配各种分辨的图标
Raw:存放文件 如txt
Values:存放各种文本变量
Xml:存放配置文件 如文件路径等

主要使用Android mvp开发模式进行开发,该模式有以下几个优点
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在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
/**
* 1、判断本地数据库有没有收藏书籍的数据。
* 2、本地数据库没有收藏书籍数据就网络请求。否则就取本地数据

@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运行结果

别踩白块

推箱子

猜你喜欢

转载自www.cnblogs.com/yuchao123/p/10877459.html