可拖拽排序的GridView(高仿今日头条编辑频道效果)

本文介绍如何实现类似今日头条频道编辑的拖拽排序GridView。通过WindowManager添加悬浮窗口,结合onTouchEvent处理、平移动画和GridView API,打造无缝拖拽体验。详细步骤包括长按事件、悬浮视图定位、移动跟踪、动画应用以及手指抬起后的数据同步。
摘要由CSDN通过智能技术生成
最近在一直在用今日头条,发现在我的频道编辑时的拖拽排序体验非常有意思,这种拖拽功能其实在支付宝等app上也频繁使用,于是打算自己研究一下,网上虽然有很多类似于此类功能的博客,但实现的都不是特别完美,效果总有瑕疵,今天我分享一个完美体验版,大家用了就知道!

老规矩,先上效果:
这里写图片描述

准备工作

要想实现这个效果,首先你要了解这几个方面的知识,有欠缺的同学赶紧先补一下:
* 使用WindowManager添加悬浮窗口
* onTouchEvent触摸事件的处理
* 简单的TranslateAnimation平移动画
* GridView api的熟练使用

原理分析

首先它是一个gridview,里面放了很多item,至于item你可以自己随意布局,例子中我只用了一个textview。

1、触发gridview长按事件,用windowmanager添加一个悬浮view,并占位隐藏原来的item,这个悬浮窗就是我们长按的item的一个图片副本,并且将悬浮view定位到自己的手指触摸点,将这个悬浮view设置放大倍数及透明度;
2、监听手指的移动,实时改变悬浮view的位置;
3、当移动距离超过自己的position时,用TranslateAnimation动画平移从起始位置到目标位置之间的item;
4、当手机抬起时,改变adapter中数据的位置,刷新gridview,并释放悬浮view;

下面我们一步一步的来撸代码

//首先展示一下需要的成员变量


    private WindowManager.LayoutParams mWindowParams;
    private WindowManager mWindowManager;

    //被拖拽的item图片副本
    private ImageView mDragImageView;

    //按下时手指的坐标
    private float downX, downY;

    //是否正在拖拽
    private boolean isDraging = false;

    //是否正在进行移动item动画,防止高频率触发动画而发生抖动
    private boolean isMoving = false;

    //被拖拽和未被拖拽的标记
    private static final int NOT_DRAG_ITEM = 0x0;
    private static final int SHOW_DRAG_ITEM = 0x1;

    //拖动时副本放大倍数
    private static final float DRAG_SCALE = 1.2F;

    //记录拖拽的item位置
    private int mDragItemPosition;

    //记录最后一个item动画toString格式
    private String lastAnim;   

在构造方法中初始化必要的一些变量:

 public DragGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        //设置item长按事件
        setOnItemLongClickListener(this);
        mWindowParams = new WindowManager.LayoutParams();
        mDragImageView = new ImageView(getContext());
         //标记未被拖拽
        mDragImageView.setTag(NOT_DRAG_ITEM);
        //获取窗口管理对象,用于后面向窗口中添加dragImageView
        mWindowManager = (WindowManager)   getContext().getSystemService(Context.WINDOW_SERVICE);
    }

在长按事件中创建副本,隐藏拖拽item

  @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        mDragItemPosition = position;
        //清空item之前的图片缓存
        view.destroyDrawingCache();
        //开启图片缓存
        view.setDrawingCacheEnabled(true);
        //创建一个item的图片副本
        Bitmap dragBitmap = Bitmap.createBitmap(view.getDrawingCache());
        mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
        //定义副本的长和宽
        mWindowParams.width = (int) (DRAG_SCALE * dragBitmap.getWidth());
        mWindowParams.height = (int) (DRAG_SCALE * dragBitmap.getHeight());
        //定义副本的位置
        mWindowParams.x = (int) (downX - dragBitmap.getWidth() / 2);
        mWindowParams.y = (int) (downY - dragBitmap.getHeight() / 2);
        //定义副本的附加参数:不能点击、不能获取焦点、悬浮窗的形式
        mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        //定义副本支持透明格式
        mWindowParams.format = PixelFormat.TRANSLUCENT;
        mWindowParams.windowAnimations = 0;
        //如果之前有这个副本先移除
        if ((int) mDragImageView.getTag() == SHOW_DRAG_ITEM) {
            mWindowManager.removeView(mDragImageView);
            mDragImageView.setTag(NOT_DRAG_ITEM);
        }
        //将图片副本放入imageview
        mDragImageView.setImageBitmap(dragBitmap);
        //设置已有副本标记
        mDragImageView.setTag(SHOW_DRAG_ITEM);
        //设置imageView透明度
        mDragImageView.setAlpha(0.7f);
        //添加这个imageview到悬浮窗
        mWindowManager.addView(mDragImageView, mWindowParams);
        //此时状态变为可拖拽
        isDraging = true;
        //通知adapter隐藏拖拽的item
        ((DragAdapter) getAdapter()).hideItem(position);
        //将拖拽item的图片缓存功能关闭,释放内存
        view.setDrawingCacheEnabled(false);
        return true;
    }

监听手指的移动来移动item副本,并执行动画:

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //记录按下x、y轴位置
                downX = ev.getRawX();
                downY = ev.getRawY();
                break;

            case MotionEvent.ACTION_MOVE:
                if (isDraging) {
                    //移动时实时刷新副本的坐标位置
                    mWindowParams.x = (int) (ev.getRawX() - mDragImageView.getWidth() / 2);
                    mWindowParams.y = (int) (ev.getRawY() - mDragImageView.getHeight() / 2);
                    //改变副本的位置
                    mWindowManager.updateViewLayout(m
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值