重写GridView实现仿今日头条的频道编辑页(1)

贾芬
• 阅读 2305

本文旨在通过重写GridView,配合系统弹窗实现仿今日头条的频道编辑页面

注:由于代码稍长,本文仅列出关键部分,完整工程请参见【https://github.com/G9YH/YHChannelEdit

在开始讲解盗版的实现方案前,让我们先来看看正版与盗版的实际使用效果对比,首先是正版

重写GridView实现仿今日头条的频道编辑页(1)

接下来是盗版

重写GridView实现仿今日头条的频道编辑页(1)

当然,在部分视图的设计方面还是存在着不小的差异的,但这一页面大部分基本功能已然实现了。那么接下来,就让我们开始我们的模仿秀

实现思想

事实上,我的频道列表中,如何实现长按拖拽并交换频道位置是整个页面的核心难点。大致实现思路如下

  • 长按某个频道后,在该频道上方生成一个与之相同的弹窗,同时隐藏该频道视图
  • 当手指按下时,该弹窗跟随触摸点移动
  • 弹窗移动过程中,根据触摸点交换其他频道位置
  • 当手指抬起时,在触摸点当前对应的位置处生成一个与弹窗相同的频道视图

抛开这一问题,其余部分的实现逻辑都较为简单,这里不再赘述,下文将更会有具体实现的介绍

实现要点

我的频道

正如前文所言,这一部分的核心在于重写GridView以及系统弹窗,那么,首先自然是系统弹窗权限的开启

<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()
}

优化改进

尽管到目前为止,我们已经实现了大部分的基本功能,但仍与正版有部分差异,例如频道列表内容的存储、部分动画的实现以及视图设计的差别等等,这一系列问题都将在之后的开发工作中继续优化

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器!复制代码(https://oscimg.oschina.net/oscnet/675f5689159acfa2c39c91f4df40a00ce0f.gif)/cookie对象;rememberMeCookie()方法是设置Cookie的生成模
Stella981 Stella981
3年前
GridView实现九宫格
GridViewgv(GridView)findViewById(R.id.g1);    ArrayList<HashMap<String,ObjectdatanewArrayList<HashMap<String,Object();    for(inti0;i<images.length;i)
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
3年前
SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器!复制代码(https://oscimg.oschina.net/oscnet/675f5689159acfa2c39c91f4df40a00ce0f.gif)/cookie对象;rememberMeCookie()方法是设置Cookie的生成模
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin