本文旨在经过重写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() }
尽管到目前为止,咱们已经实现了大部分的基本功能,但仍与正版有部分差别,例如频道列表内容的存储、部分动画的实现以及视图设计的差异等等,这一系列问题都将在以后的开发工做中继续优化