Android可拖拽的GridView的实现以及与ViewPager的冲突避免

最近要用Android开发一个桌面应用,要求桌面的图标能随意拖动,所以第一念头就是用重写GridView来实现这个功能

小编在网上搜罗了别人写好的DragGridView拿来直接用了,下面就是自定义的GridView

参考链接 http://blog.csdn.net/vipzjyno1/article/details/25005851

package com.debla.minicomp.Activity.SimpleMode;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ImageView;

/**
 * Created by Dave-PC on 2017/2/12.
 */

public class DragGridView extends GridView {
    private static final int DRAG_IMG_SHOW = 1;
    private static final int DRAG_IMG_NOT_SHOW = 0;
    private static final String LOG_TAG = "DragGridView";
    private static final float AMP_FACTOR = 1.2f;

    /**被拖动的视图*/
    private ImageView dragImageView;
    private WindowManager.LayoutParams dragImageViewParams;
    private WindowManager windowManager;
    private static boolean isViewOnDrag = false;

    /**前一次拖动的位置*/
    private int preDraggedOverPositon = AdapterView.INVALID_POSITION;
    private int downRawX;
    private int downRawY;

    public static boolean IsViewOnDrag(){
        return isViewOnDrag;
    }
    /**长按选中拖动item*/
    private OnItemLongClickListener onLongClickListener = new OnItemLongClickListener(){

        @Override
        //长按item开始拖动
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {

            //记录长按item位置
            preDraggedOverPositon = position;
            //获取被长按item的drawing cache
            view.destroyDrawingCache();
            view.setDrawingCacheEnabled(true);
            //通过被长按item,获取拖动item的bitmap
            Bitmap dragBitmap = Bitmap.createBitmap(view.getDrawingCache());

            //设置拖动item的参数
            dragImageViewParams.gravity = Gravity.TOP | Gravity.LEFT;
            //设置拖动item为原item 1.2倍
            dragImageViewParams.width = (int)(AMP_FACTOR*dragBitmap.getWidth());
            dragImageViewParams.height = (int)(AMP_FACTOR*dragBitmap.getHeight());
            //设置图片透明度
            dragImageViewParams.alpha=(float)0.5;
            //设置触摸点为绘制拖动item的中心
            dragImageViewParams.x = (downRawX - dragImageViewParams.width/2);
            dragImageViewParams.y = (downRawY - dragImageViewParams.height/2);
            dragImageViewParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
            dragImageViewParams.format = PixelFormat.TRANSLUCENT;
            dragImageViewParams.windowAnimations = 0;

            //dragImageView为被拖动item的容器,清空上一次的显示
            if((int)dragImageView.getTag() == DRAG_IMG_SHOW) {
                windowManager.removeView(dragImageView);
                dragImageView.setTag(DRAG_IMG_NOT_SHOW);
            }

            //设置本次被长按的item
            dragImageView.setImageBitmap(dragBitmap);

            //添加拖动item到屏幕
            windowManager.addView(dragImageView, dragImageViewParams);
            dragImageView.setTag(DRAG_IMG_SHOW);
            isViewOnDrag = true;

            //设置被长按item不显示
            ((GridViewAdapter)getAdapter()).hideView(position);
            return true;
        }
    };

    public DragGridView(Context context) {
        super(context);
        initView();
    }

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

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

    public void initView() {
        setOnItemLongClickListener(onLongClickListener);
        //初始化显示被拖动item的image view
        dragImageView = new ImageView(getContext());
        dragImageView.setTag(DRAG_IMG_NOT_SHOW);
        //初始化用于设置dragImageView的参数对象
        dragImageViewParams = new WindowManager.LayoutParams();
        //获取窗口管理对象,用于后面向窗口中添加dragImageView
        windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        //被按下时记录按下的坐标
        if(ev.getAction() == MotionEvent.ACTION_DOWN) {
            //获取触摸点相对于屏幕的坐标
            downRawX = (int)ev.getRawX();
            downRawY = (int)ev.getRawY();
        }
        //dragImageView处于被拖动时,更新dragImageView位置
        else if((ev.getAction() == MotionEvent.ACTION_MOVE) && (isViewOnDrag == true)) {
            Log.i(LOG_TAG, "" + ev.getRawX() + " " + ev.getRawY());
            //设置触摸点为dragImageView中心
            dragImageViewParams.x = (int)(ev.getRawX() - dragImageView.getWidth()/2);
            dragImageViewParams.y = (int)(ev.getRawY() - dragImageView.getHeight()/2);
            //更新窗口显示
            windowManager.updateViewLayout(dragImageView, dragImageViewParams);
            //获取当前触摸点的item position
            int currDraggedPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
            //如果当前停留位置item不等于上次停留位置的item,交换本次和上次停留的item
            if((currDraggedPosition != AdapterView.INVALID_POSITION) && (currDraggedPosition != preDraggedOverPositon)) {
                ((GridViewAdapter)getAdapter()).swapView(preDraggedOverPositon, currDraggedPosition);
                preDraggedOverPositon = currDraggedPosition;
            }
        }
        //释放dragImageView
        else if((ev.getAction() == MotionEvent.ACTION_UP) && (isViewOnDrag == true)) {
            ((GridViewAdapter)getAdapter()).showHideView();
            if((int)dragImageView.getTag() == DRAG_IMG_SHOW) {
                windowManager.removeView(dragImageView);
                dragImageView.setTag(DRAG_IMG_NOT_SHOW);
            }
            isViewOnDrag = false;
        }
        return super.onTouchEvent(ev);
    }
}

其中GridView的Item布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="5px"
    android:background="#000000">

    <LinearLayout
        android:id="@+id/item_view"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:id="@+id/item_img"
            android:layout_marginTop="20dp"
            android:scaleType="centerCrop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:padding="3dp"
            android:layout_centerHorizontal="true"  />
        <TextView
            android:id="@+id/item_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="28sp"
            android:layout_gravity="center"/>
    </LinearLayout>


</LinearLayout>
重写的BaseAdapter

package com.debla.minicomp.Activity.SimpleMode;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.debla.minicomp.Activity.Public.ItemView;
import com.debla.minicomp.Activity.Public.ScreenUtils;
import com.debla.minicomp.minicomp.R;

import java.util.List;

/**
 * Created by Dave-PC on 2017/2/12.
 */

public class GridViewAdapter extends BaseAdapter{
    private Context context;
    private List<ItemView> list;
    //public static int ROW_NUMBER = 3;
    //private int[] picIds = null;
    //private DragGridView mGv;
    private int hidePosition = AdapterView.INVALID_POSITION;

    public GridViewAdapter(Context context, List<ItemView> list) {
        //this.mGv=mGv;
        this.context = context;
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        if(convertView == null) {
            //view = new ImageView(context);
            view = LayoutInflater.from(context).inflate(R.layout.grid_layout,null);
        }
        else {
            view = (View)convertView;
        }

        //hide时隐藏Text
        if(position != hidePosition) {
            //view.setText(strList.get(position));
            LinearLayout layout = (LinearLayout)view.findViewById(R.id.item_view);
            layout.setBackgroundColor(list.get(position).getColor());
            ImageView img = (ImageView)view.findViewById(R.id.item_img);
            img.setScaleType(ImageView.ScaleType.FIT_CENTER);
            img.setImageResource(list.get(position).getItem_img());
            TextView tv = (TextView) view.findViewById(R.id.item_title);
            tv.setText(list.get(position).getItem_title());
            view.setVisibility(View.VISIBLE);
        }
        else {
            //view.setText("");
            view.setVisibility(View.INVISIBLE);
        }


        int screenWidth = ScreenUtils.getScreenWidth(context);
        int screenHeight = ScreenUtils.getScreenHeight(context);
        int viewWidth = screenWidth/2;
        int viewHeight = screenHeight/3;
        AbsListView.LayoutParams params = new AbsListView.LayoutParams(viewWidth,viewHeight-20);
        view.setLayoutParams(params);
        //view.setScaleType(ImageView.ScaleType.CENTER);
        view.setId(position);
        //Log.e("debug",String.valueOf(params));

        return view;
    }

    public void hideView(int pos) {
        hidePosition = pos;
        notifyDataSetChanged();
    }

    public void showHideView() {
        hidePosition = AdapterView.INVALID_POSITION;
        notifyDataSetChanged();
    }

    public void removeView(int pos) {
        //strList.remove(pos);
        list.remove(pos);
        notifyDataSetChanged();
    }

    //更新拖动时的gridView
    public void swapView(int draggedPos, int destPos) {
        //从前向后拖动,其他item依次前移
        if(draggedPos < destPos) {
            list.add(destPos+1, (ItemView)getItem(draggedPos));
            list.remove(draggedPos);
        }
        //从后向前拖动,其他item依次后移
        else if(draggedPos > destPos) {
            list.add(destPos, (ItemView)getItem(draggedPos));
            list.remove(draggedPos+1);
        }
        hidePosition = destPos;
        notifyDataSetChanged();
    }
}
需要存放在List中的ItemView类的定义,要包含图标的背景颜色,图片,以及文字描述,这些都要在GridView的元素中显示

package com.debla.minicomp.Activity.Public;

/**
 * Created by Dave-PC on 2017/2/13.
 */

public class ItemView {
    private int Color;
    private int item_img;
    private String item_title;

    public int getColor() {
        return Color;
    }

    public void setColor(int color) {
        Color = color;
    }

    public String getItem_title() {
        return item_title;
    }

    public void setItem_title(String item_title) {
        this.item_title = item_title;
    }

    public int getItem_img() {

        return item_img;
    }

    public void setItem_img(int item_img) {
        this.item_img = item_img;
    }
}
在这里我是用ViewPager和Fragment来实现滑动分页的,因此在对应的Fragment中定义我们的DragGridView对象,findViewById来绑定布局,再通过setAdapter()方法来实现数据绑定,当然在绑定前需要初始化数据,这里我就不描述了。

在这些代码完成后应该就已经完成主要功能了

然而就在小编以为能够完美地跑起来的时候,却发现了一个严重的bug,由于在DragGridView中拖拽元素的时候,会和ViewPager的滑动产生冲突,造成拖拽时卡住

在各种百度膜拜大神之后,找到了第一个解决方法:重写ViewPager,通过判断滑动的程度(手指滑动的距离)来分离ViewPager和DragGridView的滑动事件,相关链接

http://blog.csdn.net/qiumeqiu/article/details/46652005

在参考了那段代码后小编跑起来一看,诶,果然有效,但是整个滑动ViewPager的效果变得特别差而且依然有可能发生冲突,最后小编灵机一动,在DragGridView中将

isViewOnDrag变量变成static类型,这个变量就是用来标识DragGridView的拖拽状态,写下这个变量的getter方法后,我们只需要在ViewPager方法中写入对DragGridView的拖拽状态进行判断,来实现滑动分离,下面是重写的ViewPager代码

package com.debla.minicomp.Activity.Public;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

import com.debla.minicomp.Activity.SimpleMode.DragGridView;

/**
 * Created by Dave-PC on 2017/2/13.
 * 自定义的ViewPager,用于防止DragView在拖拽的时候触发ViewPager对其滑动做设置
 *
 */

public class MyViewPager extends ViewPager {
    private float xPosition;// 手指触摸点的x轴坐标
    private final float MINIMUM_DISTANCE = 100.0f;// 手指移动的最小距离
    private int distance;// 根据屏幕密度计算出来的,手指移动的最小距离

    public static int dip2px (Context context, float dpValue)
    {
        final float scale = context.getResources ().getDisplayMetrics ().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public MyViewPager (Context context)
    {
        super (context);
        distance = dip2px (context,MINIMUM_DISTANCE);
    }

    public MyViewPager (Context context, AttributeSet attrs)
    {
        super (context, attrs);
        distance = dip2px (context,MINIMUM_DISTANCE);
    }

    @Override
    public boolean onInterceptTouchEvent (MotionEvent ev)
    {
        // 先保存手指按下的x轴的坐标
        if (ev.getAction () == MotionEvent.ACTION_DOWN)
            xPosition = ev.getX ();
        if (ev.getAction () == MotionEvent.ACTION_MOVE)
        {
            /*
             计算手指移动时的坐标跟按下的坐标之间的绝对值,如果超过给定的值,
             就认为viewpager需要滚动。通过调节distance的大小,可以改变滑动
             灵敏度
              */
            if(DragGridView.IsViewOnDrag())
                return false;
            if (Math.abs (ev.getX () - xPosition) < distance)
                return false;
            else// 意思就是:touch事件已经被PeopleViewPager自己消费了,不会传递到子控件
                return true;
        }
        // 其他情况,依旧保持默认的处理方法
        return super.onInterceptTouchEvent (ev);
    }
}
最终程序完美的运行,能够实现GridView的拖拽,而且不受ViewPager冲突的影响,最后感谢那些前辈们的帮助。

猜你喜欢

转载自blog.csdn.net/bonsoirdave/article/details/55057998