上路傳送眼:
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>
出來的效果是這樣的:
嗯 還不錯。
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.lxweimin.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。