美文网首页Android开发Android开发经验谈Android知识
Android练手小项目(KTReader)基于mvp架构(七)

Android练手小项目(KTReader)基于mvp架构(七)

作者: yiuhet | 来源:发表于2017-06-15 17:39 被阅读163次

    上路传送眼:
    Android练手小项目(KTReader)基于mvp架构(六)

    Github地址: https://github.com/yiuhet/KTReader

    上篇文章我们完成了项目的收藏和历史功能,
    这篇我们将要完成该项目的最后的功能设置页和关于页。

    先看看完成图:

    效果图

    我们想要在设置和关于界面完成的功能有:

    • 设置夜间模式
    • 主题色的选择
    • 清空应用缓存
    • 查看项目源码
    • 分享好友应用

    所用到的知识点有:

    • PreferenceFragment
    • SharedPreferences
    • File的删除
    • 代码中设置theme
    • 剪切板

    可完善和加强的内容或功能有:

    • 检查更新
    • 关于UI界面美化

    ps:由于这里的功能并不复杂,所以我就没有按照mvp的架构来写,直接把业务逻辑写在了view层。(偷懒一波,毕竟期末事情太多,复习考试,课程设计,还有找实习工作/(ㄒoㄒ)/~~)

    1. 设置界面

    如果我们自己写设置的界面,还要保存相关的设置,虽然也不是很复杂,但是官方提供了更好的类:PreferenceFragment
    我们写个fragment继承自PreferenceFragment然后在xml文件中写布局就好了,系统会自动帮我们保存里面的偏好设置(通过SharedPreferences)。
    下面简述一下Preference的知识点(之后我会写一篇详解。):

    XML

    • PreferenceScreen
      根节点,若一个xml文件中有嵌套的PreferenceScreen,点击后将通过另一屏来显示它包含的内容。

    • PreferenceCategory
      用来分组的东西,可以让布局更有层次感。
      组件的通用属性有

    • android:key 唯一标识id

    • android:defaultValue 默认值

    • android:title 主标题

    • android:summary 副标题
      用到的基本组件

    • ListPreference
      基本组件之一,点击弹出列表对话框。

    • android:dialogTitle
      对话框标题

    • android:entries
      列表显示的文本()

    • android:entryValues
      实际系统保存的值,和entries一一对应

    • SwitchPreference
      基本组件之一,有on,off两种状态。

    • Preference

    PreferenceFragment

    我们可以通过相关的监听接口来写对其需要的功能:

    • onPreferenceClick(Preference preference)
      点击事件发生,回调该方法
    • onPreferenceChange(Preference preference, Object newValue)
      值改变,回调该方法

    我们可以通过以下方式在其他activity中得到Preference保存的值:

    SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    mPrefs.getString(key, defaultValue);
    

    OK,知道了这些,要写一个设置界面就很简单了。
    1.要先在res文件下建个xml文件夹(如果没有),写xml文件。
    2.建立SettingsFragment继承自PreferenceFragment,
    3.在回调方法里写具体操作。
    4.创建单例,里面实现获取Preference的保存值。
    res.xml.preferences:

    <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:title="设置">
    
    <PreferenceCategory
    android:title="基本设置">
    <SwitchPreference
    android:title="夜间模式"
    android:summary="夜间保护你的眼睛"
    android:key="settings_safe"/>
    <ListPreference
    android:title="主题色"
    android:key="settings_theme"
    android:entries="@array/theme_entities"
    android:entryValues="@array/theme_values"
    android:defaultValue="@string/default_theme"
    />
    <Preference
    android:title="清空缓存"
    android:key="settings_cache" />
    </PreferenceCategory>
    
    <PreferenceCategory
    android:title="其他设置">
    <Preference
    android:title="检查更新"
    android:summary="当前版本: 1.0"
    android:key="settings_check"/>
    <Preference
    android:title="查看源码"
    android:summary="给开发者github上点个Star"
    android:key="settings_look"/>
    </PreferenceCategory>
    </PreferenceScreen>
    

    数组文件为:

    <resources>
    <string-array name="theme_entities">
    <item>楞头青</item>
    <item>少女粉</item>
    <item>画韩红</item>
    <item>原谅绿</item>
    <item>基佬紫</item>
    </string-array>
    <string-array name="theme_values">
    <item>indigo</item>
    <item>pink</item>
    <item>red</item>
    <item>green</item>
    <item>purple</item>
    </string-array>
    
    <string name="default_theme">indigo</string>
    <string name="default_lr">right</string>
    </resources>
    

    出来的效果是这样的:

    image.png

    嗯 还不错。

    SettingsFragment

    下面开始写具体业务逻辑:
    官方推荐的是使用fragment继承自PreferenceFragment而不是继承PreferenceActivity的activity来呈现界面和处理业务。所以我们跟着官方走使用PreferenceFragment。我们要在onCreate中添加这样一句话来加载xml文件:

    addPreferencesFromResource(R.xml.preferences);

    然后我们要初始化一些数据,写个初始化方法:

    private void initPer() {
            mOptionBlack = (SwitchPreference) findPreference("settings_safe");
            mOptionTheme = (ListPreference) findPreference("settings_theme");
            mOptionCache = findPreference("settings_cache");
            mOptionCheck = findPreference("settings_check");
            mOptionLook = findPreference("settings_look");
    
            mOptionBlack.setOnPreferenceChangeListener(this);
            mOptionTheme.setOnPreferenceChangeListener(this);
            mOptionCache.setOnPreferenceClickListener(this);
            mOptionCheck.setOnPreferenceClickListener(this);
            mOptionLook.setOnPreferenceClickListener(this);
    
    
            mOptionTheme.setSummary(String.format("当前主题: %s",
                    mOptionTheme.getEntry() == null ? "愣头青" : mOptionTheme.getEntry()));
    
            mOptionCache.setSummary(FileSizeUtil.getAutoFileOrFilesSize(MyApplication.getAppCacheDir() + "/KTReaderCache"));
        }
    

    在这个方法里,我们创建了对应xml文件中的控件对象,通过设置的key。
    然后给它们设置了监听器。并对副标题动态显示。

    下面就是在回调里写对它们的监听处理:
    首先是当主题设置被改变时,我们要更改副标题,并且弹出提示框,提示用户重启以使设置生效。

    @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            if (preference == mOptionTheme) {
               // mOptionTheme.setSummary("当前主题: " + mOptionTheme.getEntry());
                if (newValue.equals("indigo")) {
                    mOptionTheme.setSummary("当前主题: 愣头青");
                } else if (newValue.equals("pink")) {
                    mOptionTheme.setSummary("当前主题: 少女粉");
                }else if (newValue.equals("red")) {
                    mOptionTheme.setSummary("当前主题: 画韩红");
                }else if (newValue.equals("green")) {
                    mOptionTheme.setSummary("当前主题: 原谅绿");
                }else if (newValue.equals("purple")) {
                    mOptionTheme.setSummary("当前主题: 基佬紫");
                }
            }
            Snackbar.make(getView(),"主题切换成" +
                    "功,重启应用后生效",Snackbar.LENGTH_INDEFINITE)
                    .setAction("重启", new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Intent intent = new Intent(getActivity(), MainActivity.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                            getActivity().startActivity(intent);
                            getActivity().finish();
                        }
                    })
                    .show();
            return true;
        }
    

    之后我们对清除缓存的监听处理为弹出对话框再次询问是否清除,防止误操作,然后当用户选择清除时,删除缓存文件。对查看源码处理为跳转到项目github网址:

    @Override
        public boolean onPreferenceClick(Preference preference) {
            if (preference == mOptionCache) {
                new AlertDialog.Builder(getActivity())
                        .setTitle("提示")
                        .setMessage("是否清除缓存?")
                        .setPositiveButton("清除", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                clearCache();
                            }
                        })
                        .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
    
                            }
                        })
                        .show();
            } else if (preference == mOptionCheck) {
                Snackbar.make(getView(), "已是最新版本", Snackbar.LENGTH_SHORT).show();
            } else if (preference == mOptionLook) {
                goToHtml("https://github.com/yiuhet/KTReader");
            }
            return true;
        }
    

    完整的SettingsFragment代码如下
    ui.fragment.SettingsFragment

    public class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
    
        SwitchPreference mOptionBlack ;
        ListPreference mOptionTheme;
        Preference mOptionCache;
        Preference mOptionCheck;
        Preference mOptionLook;
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);
        }
    
        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            initPer();
        }
    
        private void initPer() {
            mOptionBlack = (SwitchPreference) findPreference("settings_safe");
            mOptionTheme = (ListPreference) findPreference("settings_theme");
            mOptionCache = findPreference("settings_cache");
            mOptionCheck = findPreference("settings_check");
            mOptionLook = findPreference("settings_look");
    
            mOptionBlack.setOnPreferenceChangeListener(this);
            mOptionTheme.setOnPreferenceChangeListener(this);
            mOptionCache.setOnPreferenceClickListener(this);
            mOptionCheck.setOnPreferenceClickListener(this);
            mOptionLook.setOnPreferenceClickListener(this);
    
    
            mOptionTheme.setSummary(String.format("当前主题: %s",
                    mOptionTheme.getEntry() == null ? "愣头青" : mOptionTheme.getEntry()));
    
            mOptionCache.setSummary(FileSizeUtil.getAutoFileOrFilesSize(MyApplication.getAppCacheDir() + "/KTReaderCache"));
        }
    
        @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            if (preference == mOptionTheme) {
               // mOptionTheme.setSummary("当前主题: " + mOptionTheme.getEntry());
                if (newValue.equals("indigo")) {
                    mOptionTheme.setSummary("当前主题: 愣头青");
                } else if (newValue.equals("pink")) {
                    mOptionTheme.setSummary("当前主题: 少女粉");
                }else if (newValue.equals("red")) {
                    mOptionTheme.setSummary("当前主题: 画韩红");
                }else if (newValue.equals("green")) {
                    mOptionTheme.setSummary("当前主题: 原谅绿");
                }else if (newValue.equals("purple")) {
                    mOptionTheme.setSummary("当前主题: 基佬紫");
                }
            }
            Snackbar.make(getView(),"主题切换成" +
                    "功,重启应用后生效",Snackbar.LENGTH_INDEFINITE)
                    .setAction("重启", new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Intent intent = new Intent(getActivity(), MainActivity.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                            getActivity().startActivity(intent);
                            getActivity().finish();
                        }
                    })
                    .show();
            return true;
        }
    
        @Override
        public boolean onPreferenceClick(Preference preference) {
            if (preference == mOptionCache) {
                new AlertDialog.Builder(getActivity())
                        .setTitle("提示")
                        .setMessage("是否清除缓存?")
                        .setPositiveButton("清除", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                clearCache();
                            }
                        })
                        .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
    
                            }
                        })
                        .show();
            } else if (preference == mOptionCheck) {
                Snackbar.make(getView(), "已是最新版本", Snackbar.LENGTH_SHORT).show();
            } else if (preference == mOptionLook) {
                goToHtml("https://github.com/yiuhet/KTReader");
            }
            return true;
        }
    
        private void goToHtml(String url) {
            Uri uri = Uri.parse(url);   //指定网址
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);           //指定Action
            intent.setData(uri);                            //设置Uri
            startActivity(intent);        //启动Activity
        }
    
        public void clearCache() {
            Observable.just(deleteFile(new File(MyApplication.getAppCacheDir() + "/KTReaderCache")))
                    .subscribeOn(Schedulers.io())
                    .unsubscribeOn(AndroidSchedulers.mainThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<Boolean>() {
                        @Override
                        public void accept(@NonNull Boolean aBoolean) throws Exception {
                            mOptionCache.setSummary(FileSizeUtil.getAutoFileOrFilesSize(MyApplication.getAppCacheDir() + "/KTReaderCache"));
                            Snackbar.make(getView(), "缓存已清除", Snackbar.LENGTH_SHORT).show();
                        }
                    });
        }
    
        public boolean deleteFile(File file) {
            if (file.isFile()) {
                return file.delete();
            }
    
            if (file.isDirectory()) {
                File[] childFiles = file.listFiles();
                if (childFiles == null || childFiles.length == 0) {
                    return file.delete();
                }
    
                for (File childFile : childFiles) {
                    deleteFile(childFile);
                }
                return file.delete();
            }
            return false;
        }
    }
    
    

    这样,我们的设置页就大功告成了。

    2.关于界面

    之后的关于页就没啥东西了,只是简单的铺个界面,加几个控件,其中相应的操作有:

    • 打开源码地址,上面有讲;
    • 把我的联系方式复制到粘贴版;
    • 分享应用给好友。

    复制功能

    ClipboardManager manager = (ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE);
    ClipData clipData = ClipData.newPlainText("msg", "965846580");
    manager.setPrimaryClip(clipData);
    Snackbar.make(view,"我的qq号已经复制到剪切板啦( •̀ .̫ •́ )✧",Snackbar.LENGTH_SHORT).show();
    

    分享功能

    Intent sharingIntent = new Intent(Intent.ACTION_SEND);
    sharingIntent.setType("text/plain");
    sharingIntent.putExtra(Intent.EXTRA_SUBJECT, "分享app");
    sharingIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_txt));//string为感谢使用“KTReader”,您可以在https://github.com/yiuhet/KTReader查看源码或下载;
    startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_app)));//string为分享-KTReader;
    

    完整代码
    ui.activity.AboutActivity

    
    public class AboutActivity extends BaseActivity {
    
        @BindView(R.id.toolbar)
        Toolbar mToolbar;
        @BindView(R.id.button_github)
        Button mButtonGithub;
        @BindView(R.id.button_jianshu)
        Button mButtonJianshu;
        @BindView(R.id.button_share)
        Button mButtonShare;
        @BindView(R.id.button_tucao)
        Button mButtonTucao;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ButterKnife.bind(this);
            mToolbar.setTitle("关于");
            setSupportActionBar(mToolbar);
        }
    
        @Override
        protected int getLayoutRes() {
            return R.layout.activity_about;
        }
    
        @OnClick({R.id.button_github, R.id.button_jianshu, R.id.button_share, R.id.button_tucao})
        public void onViewClicked(View view) {
            switch (view.getId()) {
                case R.id.button_github:
                    goToHtml("https://github.com/yiuhet/KTReader");
                    break;
                case R.id.button_jianshu:
                    goToHtml("http://www.jianshu.com/u/8857dea54ec2");
                    break;
                case R.id.button_share:
                    Intent sharingIntent = new Intent(Intent.ACTION_SEND);
                    sharingIntent.setType("text/plain");
                    sharingIntent.putExtra(Intent.EXTRA_SUBJECT, "分享app");
                    sharingIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_txt));
                    startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_app)));
                    break;
                case R.id.button_tucao:
                    ClipboardManager manager = (ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE);
                    ClipData clipData = ClipData.newPlainText("msg", "965846580");
                    manager.setPrimaryClip(clipData);
                    Snackbar.make(view,"我的qq号已经复制到粘贴板啦( •̀ .̫ •́ )✧",Snackbar.LENGTH_SHORT).show();
                    break;
            }
        }
    
        private void goToHtml(String url) {
            Uri uri = Uri.parse(url);   //指定网址
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);           //指定Action
            intent.setData(uri);                            //设置Uri
            startActivity(intent);        //启动Activity
        }
    
    }
    
    

    3.结语

    KTReader(Kill Time Reader)这个小项目,开发周期近30天,算是初学Android的我做的第一个有着完整结构的应用,它的功能并不完善,也仍有一些bug存在,可为了完善它,我仍费了好大一番功夫,有过完成一个小功能而沾沾自喜,也有过被bug折磨的焦头烂额。
    总之在今天我完成了它,在开发期间我也学到了很多东西,感谢开源精神让我成长并瞻仰大佬们的代码,感谢各位大牛的干货分享,让我能降低学习成本快速学习。
    我把这款应用的开发过程写下来并分享,一是为了记录以便今后使用,二是希望能够结识到更多的朋友,当然如果能够帮到你的话,就更加美好了,为了方便大家阅读,我之后会写一个总结篇。
    KTReader这款应用,万一有幸被屏幕前的你所发现,这是我的幸运,如果你能点个心,更是对我莫大的鼓励。如果你是大神,请帮我指正我的不足之处;如果你和我一样是初学者,欢迎和我一块学习讨论。这里留下我的联系qq:965846580,加好友请备注Android。

    相关文章

      网友评论

        本文标题:Android练手小项目(KTReader)基于mvp架构(七)

        本文链接:https://www.haomeiwen.com/subject/weyyqxtx.html