本文旨在經過重寫GridView,配合系統彈窗實現仿今日頭條的頻道編輯頁面java
注:因爲代碼稍長,本文僅列出關鍵部分,完整工程請參見【https://github.com/G9YH/YHChannelEdit】android
在開始講解盜版的實現方案前,讓咱們先來看看正版與盜版的實際使用效果對比,首先是正版git
接下來是盜版github
固然,在部分視圖的設計方面仍是存在着不小的差別的,但這一頁面大部分基本功能已然實現了。那麼接下來,就讓咱們開始咱們的模仿秀緩存
事實上,個人頻道列表中,如何實現長按拖拽並交換頻道位置是整個頁面的核心難點。大體實現思路以下服務器
拋開這一問題,其他部分的實現邏輯都較爲簡單,這裏再也不贅述,下文將更會有具體實現的介紹app
正如前文所言,這一部分的核心在於重寫GridView以及系統彈窗,那麼,首先天然是系統彈窗權限的開啓ide
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
接下來便是GridView的重寫。首先定義了兩個常量用戶標識當前的模式,即編輯模式和普通模式優化
private static final int MODE_EDIT = 1; private static final int MODE_NORMAL = 2;
而後實現了OnItemLongClickListener接口動畫
@Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { // 已處於移動模式 if (mode == MODE_EDIT) { return false; } textEdit.setText("完成"); .... // 推薦標籤沒法移動或刪除 if (i == 0) { return false; } // 判斷並獲取彈窗權限 permissionGetter.alertWindowPermissionRequest(); .... // 初始化彈窗 initWindow(); return true; }
這裏須要注意到的是PermissionGetter類,咱們知道,儘管在manifests中定義了系統彈窗的權限,但一般而言手機是須要用戶手動爲app開啓相關權限的。PermissionGetter類的做用即在於此,該類經過分別處理小米、魅族以及華爲等幾個較爲特殊的Android系統,基本實現了大部分機型的彈窗權限申請功能
/** * 判斷系統是否已爲應用開啓某項權限 * * @param num 權限編號 * @return 已開啓則返回0,不然返回1 */ private int checkPermission(int num) { int version = Build.VERSION.SDK_INT; if (version >= 19) { .... } return -1; } .... /** * Android 6.0以後的手機須要進行彈窗權限的申請 * 其中小米、魅族以及華爲三種機型須要特殊處理 */ public void alertWindowPermission() { if (this.checkPermission(24) == 1) { Toast toast = Toast.makeText( context, "請先爲您的手機開啓懸浮窗權限", Toast.LENGTH_SHORT); toast.show(); // 處理小米手機權限 if ("Xiaomi".equals(Build.MANUFACTURER)) { .... } } // 處理魅族手機權限 else if ("Meizu".equals(Build.MANUFACTURER)) { .... } // 處理華爲手機權限 else if ("Huawei".equals(Build.MANUFACTURER)) { .... } // 處理其餘手機權限 else if (Build.VERSION.SDK_INT >= 23) { .... } } }
在長按接口中實現了彈窗的初始化後,將模式mode設置爲MODE_EDIT。此時便可經過重寫onTouchEvent(MovtionEvent motionEvent)方法來判斷什麼時候進行彈窗的更新以及關閉等工做
@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); }
當手指按下時,持續更新彈窗位置,並根據其位置交換其餘頻道的位置,固然不要忘記了交換動做相應的動畫
當手指擡起時,將模式mode設置爲MODE_NORMAL,並在彈窗當前對應的頻道處生成一個與彈窗相同的視圖,同時移除該彈窗視圖便可
這一部分的實現就較爲簡單了,只需利用GridView展現頻道,而後實現OnItemClickListener接口,點擊時將該item移除並添加至個人頻道視圖中便可
@OnItemClick(R.id.grid_recommend) void gridRecommend(int position) { String string = listHolder.getRecommendList().get(position); // 個人頻道中增長標籤 listHolder.getMineList().add(string); // 頻道推薦中刪除標籤 listHolder.getRecommendList().remove(position); // 更新各頻道數據 mineAdapter.moveNotifyDataSetChanged(false, -1); recommendAdapter.notifyDataSetChanged(); }
事實上,在實際開發中,一般能夠採用SharedPreferences配合服務器端來實現個人頻道以及頻道推薦兩個列表內容的持久化存儲。但因爲這裏僅僅是實現一個demo,所以存儲功能僅經過一個單例類ListHolder來模擬實現。其中ListHolder單例的實現方式以下,參考了我以前的一篇博客《單例模式的終極實現方案》
public class ListHolder { private List<String> mineList = new ArrayList<>(); private List<String> 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() }
儘管到目前爲止,咱們已經實現了大部分的基本功能,但仍與正版有部分差別,例如頻道列表內容的存儲、部分動畫的實現以及視圖設計的差異等等,這一系列問題都將在以後的開發工做中繼續優化