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

别踩白块

推箱子

相关文章
相关标签/搜索