摘要:但由于這里僅僅是實(shí)現(xiàn)一個(gè),因此存儲(chǔ)功能僅通過(guò)一個(gè)單例類(lèi)來(lái)模擬實(shí)現(xiàn)。
本文旨在通過(guò)重寫(xiě)GridView,配合系統(tǒng)彈窗實(shí)現(xiàn)仿今日頭條的頻道編輯頁(yè)面
注:由于代碼稍長(zhǎng),本文僅列出關(guān)鍵部分,完整工程請(qǐng)參見(jiàn)【https://github.com/G9YH/YHChannelEdit】
在開(kāi)始講解盜版的實(shí)現(xiàn)方案前,讓我們先來(lái)看看正版與盜版的實(shí)際使用效果對(duì)比,首先是正版
接下來(lái)是盜版
當(dāng)然,在部分視圖的設(shè)計(jì)方面還是存在著不小的差異的,但這一頁(yè)面大部分基本功能已然實(shí)現(xiàn)了。那么接下來(lái),就讓我們開(kāi)始我們的模仿秀
實(shí)現(xiàn)思想事實(shí)上,我的頻道列表中,如何實(shí)現(xiàn)長(zhǎng)按拖拽并交換頻道位置是整個(gè)頁(yè)面的核心難點(diǎn)。大致實(shí)現(xiàn)思路如下
長(zhǎng)按某個(gè)頻道后,在該頻道上方生成一個(gè)與之相同的彈窗,同時(shí)隱藏該頻道視圖
當(dāng)手指按下時(shí),該彈窗跟隨觸摸點(diǎn)移動(dòng)
彈窗移動(dòng)過(guò)程中,根據(jù)觸摸點(diǎn)交換其他頻道位置
當(dāng)手指抬起時(shí),在觸摸點(diǎn)當(dāng)前對(duì)應(yīng)的位置處生成一個(gè)與彈窗相同的頻道視圖
拋開(kāi)這一問(wèn)題,其余部分的實(shí)現(xiàn)邏輯都較為簡(jiǎn)單,這里不再贅述,下文將更會(huì)有具體實(shí)現(xiàn)的介紹
實(shí)現(xiàn)要點(diǎn) 我的頻道正如前文所言,這一部分的核心在于重寫(xiě)GridView以及系統(tǒng)彈窗,那么,首先自然是系統(tǒng)彈窗權(quán)限的開(kāi)啟
接下來(lái)即是GridView的重寫(xiě)。首先定義了兩個(gè)常量用戶(hù)標(biāo)識(shí)當(dāng)前的模式,即編輯模式和普通模式
private static final int MODE_EDIT = 1; private static final int MODE_NORMAL = 2;
然后實(shí)現(xiàn)了OnItemLongClickListener接口
@Override public boolean onItemLongClick(AdapterView> adapterView, View view, int i, long l) { // 已處于移動(dòng)模式 if (mode == MODE_EDIT) { return false; } textEdit.setText("完成"); .... // 推薦標(biāo)簽無(wú)法移動(dòng)或刪除 if (i == 0) { return false; } // 判斷并獲取彈窗權(quán)限 permissionGetter.alertWindowPermissionRequest(); .... // 初始化彈窗 initWindow(); return true; }
這里需要注意到的是PermissionGetter類(lèi),我們知道,盡管在manifests中定義了系統(tǒng)彈窗的權(quán)限,但通常而言手機(jī)是需要用戶(hù)手動(dòng)為app開(kāi)啟相關(guān)權(quán)限的。PermissionGetter類(lèi)的作用即在于此,該類(lèi)通過(guò)分別處理小米、魅族以及華為等幾個(gè)較為特殊的Android系統(tǒng),基本實(shí)現(xiàn)了大部分機(jī)型的彈窗權(quán)限申請(qǐng)功能
/** * 判斷系統(tǒng)是否已為應(yīng)用開(kāi)啟某項(xiàng)權(quán)限 * * @param num 權(quán)限編號(hào) * @return 已開(kāi)啟則返回0,否則返回1 */ private int checkPermission(int num) { int version = Build.VERSION.SDK_INT; if (version >= 19) { .... } return -1; } .... /** * Android 6.0之后的手機(jī)需要進(jìn)行彈窗權(quán)限的申請(qǐng) * 其中小米、魅族以及華為三種機(jī)型需要特殊處理 */ public void alertWindowPermission() { if (this.checkPermission(24) == 1) { Toast toast = Toast.makeText( context, "請(qǐng)先為您的手機(jī)開(kāi)啟懸浮窗權(quán)限", Toast.LENGTH_SHORT); toast.show(); // 處理小米手機(jī)權(quán)限 if ("Xiaomi".equals(Build.MANUFACTURER)) { .... } } // 處理魅族手機(jī)權(quán)限 else if ("Meizu".equals(Build.MANUFACTURER)) { .... } // 處理華為手機(jī)權(quán)限 else if ("Huawei".equals(Build.MANUFACTURER)) { .... } // 處理其他手機(jī)權(quán)限 else if (Build.VERSION.SDK_INT >= 23) { .... } } }
在長(zhǎng)按接口中實(shí)現(xiàn)了彈窗的初始化后,將模式mode設(shè)置為MODE_EDIT。此時(shí)即可通過(guò)重寫(xiě)onTouchEvent(MovtionEvent motionEvent)方法來(lái)判斷何時(shí)進(jìn)行彈窗的更新以及關(guān)閉等工作
@Override public boolean onTouchEvent(MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: if (mode == MODE_EDIT) { updateWindow(motionEvent); } break; case MotionEvent.ACTION_UP: if (mode == MODE_EDIT) { closeWindow(); } break; } return super.onTouchEvent(motionEvent); }
當(dāng)手指按下時(shí),持續(xù)更新彈窗位置,并根據(jù)其位置交換其他頻道的位置,當(dāng)然不要忘記了交換動(dòng)作相應(yīng)的動(dòng)畫(huà)
當(dāng)手指抬起時(shí),將模式mode設(shè)置為MODE_NORMAL,并在彈窗當(dāng)前對(duì)應(yīng)的頻道處生成一個(gè)與彈窗相同的視圖,同時(shí)移除該彈窗視圖即可
頻道推薦這一部分的實(shí)現(xiàn)就較為簡(jiǎn)單了,只需利用GridView展示頻道,然后實(shí)現(xiàn)OnItemClickListener接口,點(diǎn)擊時(shí)將該item移除并添加至我的頻道視圖中即可
@OnItemClick(R.id.grid_recommend) void gridRecommend(int position) { String string = listHolder.getRecommendList().get(position); // 我的頻道中增加標(biāo)簽 listHolder.getMineList().add(string); // 頻道推薦中刪除標(biāo)簽 listHolder.getRecommendList().remove(position); // 更新各頻道數(shù)據(jù) mineAdapter.moveNotifyDataSetChanged(false, -1); recommendAdapter.notifyDataSetChanged(); }列表緩存
事實(shí)上,在實(shí)際開(kāi)發(fā)中,通??梢圆捎肧haredPreferences配合服務(wù)器端來(lái)實(shí)現(xiàn)我的頻道以及頻道推薦兩個(gè)列表內(nèi)容的持久化存儲(chǔ)。但由于這里僅僅是實(shí)現(xiàn)一個(gè)demo,因此存儲(chǔ)功能僅通過(guò)一個(gè)單例類(lèi)ListHolder來(lái)模擬實(shí)現(xiàn)。其中ListHolder單例的實(shí)現(xiàn)方式如下,參考了我之前的一篇博客《單例模式的終極實(shí)現(xiàn)方案》
public class ListHolder { private List優(yōu)化改進(jìn)mineList = new ArrayList<>(); private List recommendList = new ArrayList<>(); private static class Instance { private static ListHolder instance = new ListHolder(); } private ListHolder() { } public static ListHolder getInstance() { return Instance.instance; } public get() & set() }
盡管到目前為止,我們已經(jīng)實(shí)現(xiàn)了大部分的基本功能,但仍與正版有部分差異,例如頻道列表內(nèi)容的存儲(chǔ)、部分動(dòng)畫(huà)的實(shí)現(xiàn)以及視圖設(shè)計(jì)的差別等等,這一系列問(wèn)題都將在之后的開(kāi)發(fā)工作中繼續(xù)優(yōu)化
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67820.html
摘要:在下沒(méi)有該問(wèn)題。解決辦法部分這里隨意,需要用設(shè)定部分問(wèn)題懶加載解決方法稍后補(bǔ)充參考文獻(xiàn)組件按需加載路由懶加載項(xiàng)目中使用將多個(gè)組件合并打包并實(shí)現(xiàn)按需加載 vue 仿今日頭條 為了增加移動(dòng)端項(xiàng)目的經(jīng)驗(yàn),近一周通過(guò) vue 仿寫(xiě)今日頭條,以下就項(xiàng)目實(shí)現(xiàn)過(guò)程中遇到的問(wèn)題以及解決方法給出總結(jié),有什么不正確的地方,懇請(qǐng)大家批評(píng)指正^?_?^!,代碼倉(cāng)庫(kù)地址為 github 一、實(shí)現(xiàn)功能 首頁(yè)展示...
摘要:在下沒(méi)有該問(wèn)題。解決辦法部分這里隨意,需要用設(shè)定部分問(wèn)題懶加載解決方法稍后補(bǔ)充參考文獻(xiàn)組件按需加載路由懶加載項(xiàng)目中使用將多個(gè)組件合并打包并實(shí)現(xiàn)按需加載 vue 仿今日頭條 為了增加移動(dòng)端項(xiàng)目的經(jīng)驗(yàn),近一周通過(guò) vue 仿寫(xiě)今日頭條,以下就項(xiàng)目實(shí)現(xiàn)過(guò)程中遇到的問(wèn)題以及解決方法給出總結(jié),有什么不正確的地方,懇請(qǐng)大家批評(píng)指正^?_?^!,代碼倉(cāng)庫(kù)地址為 github 一、實(shí)現(xiàn)功能 首頁(yè)展示...
閱讀 1489·2021-10-14 09:43
閱讀 1454·2021-10-09 09:58
閱讀 1947·2021-09-28 09:42
閱讀 3738·2021-09-26 09:55
閱讀 1768·2021-08-27 16:23
閱讀 2769·2021-08-23 09:46
閱讀 917·2019-08-30 15:55
閱讀 1441·2019-08-30 15:54