天天看点

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

下面先上这次实现功能的效果图:(注:这个效果图没有拖拽的时候移动动画,demo里面有,可以下载看看)

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

刚开始接触这个的时候,不知道要如何实现,去网上翻了一大堆资料,懂了个大概,就是目前可以找到的都是拖拽的时候,不带移动动画的,和线上的客户端交互效果相差甚远,在反反复复的尝试查看相关东西,大致的做了出来,目前在模拟器上似乎有一点小bug,真机测试没有问题,就先放上来,如果发现问题在修改优化。代码反面,没有好好的修改调整,可能会有点乱,请见谅哈。

1.了解重写view的相关知识,并且知道gridview的一些内部方法,如:怎么通过触摸的坐标获取对应的position等(这里我采用的是继承gridview控件)

3.了解位移动画animation,本demo中主要用到:translateanimation  平移动画

4.了解windowmanager的窗口机制,这里的item拖拽等都要设计到这个。

5.了解sqlitedatabase 以及sqliteopenhelper等数据库操作相关的类,本demo中主要用到数据库进行存储频道信息,如果你要用文档进行存储读取也可以。

1.  获取数据库中频道的列表,如果为空,赋予默认列表,并存入数据库,之后通过对应的适配器赋给对应的gridview

2.  2个gridview--(1.draggrid   2. othergridview)

draggrid 用于显示我的频道,带有长按拖拽效果

othergridview用于显示更多频道,不带推拽效果

注:由于屏幕大小不一定,外层使用scrollview,所以2者都要重写计算高度

3.  点击2个gridview的时候,根据点击的item对应的position,获取position对应的view,进行创建一层移动的动画层

起始位置:点击的positiongetlocationinwindow()获取。终点位置:另一个gridview的最后个item 的position + 1的位置。

并赋予移动动画,等动画结束后对2者对应的频道列表进行数据的remove和add操作。

4.  设置点击和拖动的限制条件,如  推荐  这个item是不允许用户操作的。

5.  拖动的draggrid的操作:

(1)长按获取长按的item的position  -- dragposition 以及对应的view ,手指触摸屏幕的时候,调用onintercepttouchevent来获取motionevent.action_down事件,获取对应的数据。由于这里是继承了gridview,所以长按时间可以通过setonitemlongclicklistener监听来执行,或则你也可以通过计算点击时间来监听是否长按。

(2)通过ontouchevent(motionevent ev)来监听手指的移动和抬起动作。当它移动到 其它的item下面,并且下方的item对应的position  不等于 dragposition,进行数据交换,并且2者之间的所有item进行移动动画,动画结束后,数据更替刷新界面。

(3) 抬起手后,清除掉拖动时候创建的view,让gridview中的数据显示。

6.  退出时候,将改变后的频道列表存入数据库。

下面是大体的流程图:

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

点击进行添加删除:

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

/** gridview对应的item点击监听接口  */  

    @override  

    public void onitemclick(adapterview<?> parent, final view view, final int position,long id) {  

        //如果点击的时候,之前动画还没结束,那么就让点击事件无效  

        if(ismove){  

            return;  

        }  

        switch (parent.getid()) {  

        case r.id.usergridview:  

            //position为 0,1 的不可以进行任何操作  

            if (position != 0 && position != 1) {  

                final imageview moveimageview = getview(view);  

                if (moveimageview != null) {  

                    textview newtextview = (textview) view.findviewbyid(r.id.text_item);  

                    final int[] startlocation = new int[2];  

                    newtextview.getlocationinwindow(startlocation);  

                    final channelitem channel = ((dragadapter) parent.getadapter()).getitem(position);//获取点击的频道内容  

                    otheradapter.setvisible(false);  

                    //添加到最后一个  

                    otheradapter.additem(channel);  

                    new handler().postdelayed(new runnable() {  

                        public void run() {  

                            try {  

                                int[] endlocation = new int[2];  

                                //获取终点的坐标  

                                othergridview.getchildat(othergridview.getlastvisibleposition()).getlocationinwindow(endlocation);  

                                moveanim(moveimageview, startlocation , endlocation, channel,usergridview);  

                                useradapter.setremove(position);  

                            } catch (exception localexception) {  

                            }  

                        }  

                    }, 50l);  

                }  

            }  

            break;  

        case r.id.othergridview:  

            final imageview moveimageview = getview(view);  

            if (moveimageview != null){  

                textview newtextview = (textview) view.findviewbyid(r.id.text_item);  

                final int[] startlocation = new int[2];  

                newtextview.getlocationinwindow(startlocation);  

                final channelitem channel = ((otheradapter) parent.getadapter()).getitem(position);  

                useradapter.setvisible(false);  

                //添加到最后一个  

                useradapter.additem(channel);  

                new handler().postdelayed(new runnable() {  

                    public void run() {  

                        try {  

                            int[] endlocation = new int[2];  

                            //获取终点的坐标  

                            usergridview.getchildat(usergridview.getlastvisibleposition()).getlocationinwindow(endlocation);  

                            moveanim(moveimageview, startlocation , endlocation, channel,othergridview);  

                            otheradapter.setremove(position);  

                        } catch (exception localexception) {  

                    }  

                }, 50l);  

        default:  

    }  

移动动画:

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

<span style="font-size:14px;">private void moveanim(view moveview, int[] startlocation,int[] endlocation, final channelitem movechannel,  

            final gridview clickgridview) {  

        int[] initlocation = new int[2];  

        //获取传递过来的view的坐标  

        moveview.getlocationinwindow(initlocation);  

        //得到要移动的view,并放入对应的容器中  

        final viewgroup moveviewgroup = getmoveviewgroup();  

        final view mmoveview = getmoveview(moveviewgroup, moveview, initlocation);  

        //创建移动动画  

        translateanimation moveanimation = new translateanimation(  

                startlocation[0], endlocation[0], startlocation[1],  

                endlocation[1]);  

        moveanimation.setduration(300l);//动画时间  

        //动画配置  

        animationset moveanimationset = new animationset(true);  

        moveanimationset.setfillafter(false);//动画效果执行完毕后,view对象不保留在终止的位置  

        moveanimationset.addanimation(moveanimation);  

        mmoveview.startanimation(moveanimationset);  

        moveanimationset.setanimationlistener(new animationlistener() {  

            @override  

            public void onanimationstart(animation animation) {  

                ismove = true;  

            public void onanimationrepeat(animation animation) {  

            public void onanimationend(animation animation) {  

                moveviewgroup.removeview(mmoveview);  

                // instanceof 方法判断2边实例是不是一样,判断点击的是draggrid还是othergridview  

                if (clickgridview instanceof draggrid) {  

                    otheradapter.setvisible(true);  

                    otheradapter.notifydatasetchanged();  

                    useradapter.remove();  

                }else{  

                    useradapter.setvisible(true);  

                    useradapter.notifydatasetchanged();  

                    otheradapter.remove();  

                ismove = false;  

        });  

    }</span>  

可拖拽的draggrid代码: 

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

public class draggrid extends gridview {  

    /** 点击时候的x位置 */  

    public int downx;  

    /** 点击时候的y位置 */  

    public int downy;  

    /** 点击时候对应整个界面的x位置 */  

    public int windowx;  

    /** 点击时候对应整个界面的y位置 */  

    public int windowy;  

    /** 屏幕上的x */  

    private int win_view_x;  

    /** 屏幕上的y */  

    private int win_view_y;  

    /** 拖动的里x的距离 */  

    int dragoffsetx;  

    /** 拖动的里y的距离 */  

    int dragoffsety;  

    /** 长按时候对应postion */  

    public int dragposition;  

    /** up后对应的item的position */  

    private int dropposition;  

    /** 开始拖动的item的position */  

    private int startposition;  

    /** item高 */  

    private int itemheight;  

    /** item宽 */  

    private int itemwidth;  

    /** 拖动的时候对应item的view */  

    private view dragimageview = null;  

    /** 长按的时候item的view */  

    private viewgroup dragitemview = null;  

    /** windowmanager管理器 */  

    private windowmanager windowmanager = null;  

    /** */  

    private windowmanager.layoutparams windowparams = null;  

    /** item总量 */  

    private int itemtotalcount;  

    /** 一行的item数量 */  

    private int ncolumns = 4;  

    /** 行数 */  

    private int nrows;  

    /** 剩余部分 */  

    private int remainder;  

    /** 是否在移动 */  

    private boolean ismoving = false;  

    private int holdposition;  

    /** 拖动的时候放大的倍数 */  

    private double dragscale = 1.2d;  

    /** 震动器 */  

    private vibrator mvibrator;  

    /** 每个item之间的水平间距 */  

    private int mhorizontalspacing = 15;  

    /** 每个item之间的竖直间距 */  

    private int mverticalspacing = 15;  

    /* 移动时候最后个动画的id */  

    private string lastanimationid;  

    public draggrid(context context) {  

        super(context);  

        init(context);  

    public draggrid(context context, attributeset attrs, int defstyle) {  

        super(context, attrs, defstyle);  

    public draggrid(context context, attributeset attrs) {  

        super(context, attrs);  

    public void init(context context) {  

        mvibrator = (vibrator) context  

                .getsystemservice(context.vibrator_service);  

        // 将布局文件中设置的间距dip转为px  

        mhorizontalspacing = datatools.dip2px(context, mhorizontalspacing);  

    public boolean onintercepttouchevent(motionevent ev) {  

        // todo auto-generated method stub  

        if (ev.getaction() == motionevent.action_down) {  

            downx = (int) ev.getx();  

            downy = (int) ev.gety();  

            windowx = (int) ev.getx();  

            windowy = (int) ev.gety();  

            setonitemclicklistener(ev);  

        return super.onintercepttouchevent(ev);  

    public boolean ontouchevent(motionevent ev) {  

        boolean bool = true;  

        if (dragimageview != null  

                && dragposition != adapterview.invalid_position) {  

            // 移动时候的对应x,y位置  

            bool = super.ontouchevent(ev);  

            int x = (int) ev.getx();  

            int y = (int) ev.gety();  

            switch (ev.getaction()) {  

            case motionevent.action_down:  

                downx = (int) ev.getx();  

                windowx = (int) ev.getx();  

                downy = (int) ev.gety();  

                windowy = (int) ev.gety();  

                break;  

            case motionevent.action_move:  

                ondrag(x, y, (int) ev.getrawx(), (int) ev.getrawy());  

                if (!ismoving) {  

                    onmove(x, y);  

                if (pointtoposition(x, y) != adapterview.invalid_position) {  

                    break;  

            case motionevent.action_up:  

                stopdrag();  

                ondrop(x, y);  

                requestdisallowintercepttouchevent(false);  

            default:  

        return super.ontouchevent(ev);  

    /** 在拖动的情况 */  

    private void ondrag(int x, int y, int rawx, int rawy) {  

        if (dragimageview != null) {  

            windowparams.alpha = 0.6f;  

            windowparams.x = rawx - win_view_x;  

            windowparams.y = rawy - win_view_y;  

            windowmanager.updateviewlayout(dragimageview, windowparams);  

    /** 在松手下放的情况 */  

    private void ondrop(int x, int y) {  

        // 根据拖动到的x,y坐标获取拖动位置下方的item对应的postion  

        int temppostion = pointtoposition(x, y);  

        dropposition = temppostion;  

        dragadapter mdragadapter = (dragadapter) getadapter();  

        // 显示刚拖动的item  

        mdragadapter.setshowdropitem(true);  

        // 刷新适配器,让对应的item显示  

        mdragadapter.notifydatasetchanged();  

    /** 

     * 长按点击监听 

     * @param ev 

     */  

    public void setonitemclicklistener(final motionevent ev) {  

        setonitemlongclicklistener(new onitemlongclicklistener() {  

            public boolean onitemlongclick(adapterview<?> parent, view view,  

                    int position, long id) {  

                int x = (int) ev.getx();// 长安事件的x位置  

                int y = (int) ev.gety();// 长安事件的y位置  

                startposition = position;// 第一次点击的postion  

                dragposition = position;  

                if (startposition <= 1) {  

                    return false;  

                viewgroup dragviewgroup = (viewgroup) getchildat(dragposition  

                        - getfirstvisibleposition());  

                textview dragtextview = (textview) dragviewgroup  

                        .findviewbyid(r.id.text_item);  

                dragtextview.setselected(true);  

                dragtextview.setenabled(false);  

                itemheight = dragviewgroup.getheight();  

                itemwidth = dragviewgroup.getwidth();  

                itemtotalcount = draggrid.this.getcount();  

                int row = itemtotalcount / ncolumns;// 算出行数  

                remainder = (itemtotalcount % ncolumns);// 算出最后一行多余的数量  

                if (remainder != 0) {  

                    nrows = row + 1;  

                } else {  

                    nrows = row;  

                // 如果特殊的这个不等于拖动的那个,并且不等于-1  

                if (dragposition != adapterview.invalid_position) {  

                    // 释放的资源使用的绘图缓存。如果你调用builddrawingcache()手动没有调用setdrawingcacheenabled(真正的),你应该清理缓存使用这种方法。  

                    win_view_x = windowx - dragviewgroup.getleft();// view相对自己的x,半斤  

                    win_view_y = windowy - dragviewgroup.gettop();// view相对自己的y,半斤  

                    dragoffsetx = (int) (ev.getrawx() - x);// 手指在屏幕的上x位置-手指在控件中的位置就是距离最左边的距离  

                    dragoffsety = (int) (ev.getrawy() - y);// 手指在屏幕的上y位置-手指在控件中的位置就是距离最上边的距离  

                    dragitemview = dragviewgroup;  

                    dragviewgroup.destroydrawingcache();  

                    dragviewgroup.setdrawingcacheenabled(true);  

                    bitmap dragbitmap = bitmap.createbitmap(dragviewgroup  

                            .getdrawingcache());  

                    mvibrator.vibrate(50);// 设置震动时间  

                    startdrag(dragbitmap, (int) ev.getrawx(),  

                            (int) ev.getrawy());  

                    hidedropitem();  

                    dragviewgroup.setvisibility(view.invisible);  

                    ismoving = false;  

                    requestdisallowintercepttouchevent(true);  

                    return true;  

                return false;  

    public void startdrag(bitmap dragbitmap, int x, int y) {  

        stopdrag();  

        windowparams = new windowmanager.layoutparams();// 获取window界面的  

        // gravity.top|gravity.left;这个必须加  

        windowparams.gravity = gravity.top | gravity.left;  

        // 得到preview左上角相对于屏幕的坐标  

        windowparams.x = x - win_view_x;  

        windowparams.y = y - win_view_y;  

        // 设置拖拽item的宽和高  

        windowparams.width = (int) (dragscale * dragbitmap.getwidth());// 放大dragscale倍,可以设置拖动后的倍数  

        windowparams.height = (int) (dragscale * dragbitmap.getheight());// 放大dragscale倍,可以设置拖动后的倍数  

        this.windowparams.flags = windowmanager.layoutparams.flag_not_focusable  

                | windowmanager.layoutparams.flag_not_touchable  

                | windowmanager.layoutparams.flag_keep_screen_on  

                | windowmanager.layoutparams.flag_layout_in_screen;  

        this.windowparams.format = pixelformat.translucent;  

        this.windowparams.windowanimations = 0;  

        imageview iv = new imageview(getcontext());  

        iv.setimagebitmap(dragbitmap);  

        windowmanager = (windowmanager) getcontext().getsystemservice(  

                context.window_service);// "window"  

        windowmanager.addview(iv, windowparams);  

        dragimageview = iv;  

    /** 停止拖动 ,释放并初始化 */  

    private void stopdrag() {  

            windowmanager.removeview(dragimageview);  

            dragimageview = null;  

    /** 在scrollview内,所以要进行计算高度 */  

    public void onmeasure(int widthmeasurespec, int heightmeasurespec) {  

        int expandspec = measurespec.makemeasurespec(integer.max_value >> 2,  

                measurespec.at_most);  

        super.onmeasure(widthmeasurespec, expandspec);  

    /** 隐藏 放下 的item */  

    private void hidedropitem() {  

        ((dragadapter) getadapter()).setshowdropitem(false);  

    /** 获取移动动画 */  

    public animation getmoveanimation(float toxvalue, float toyvalue) {  

        translateanimation mtranslateanimation = new translateanimation(  

                animation.relative_to_self, 0.0f, animation.relative_to_self,  

                toxvalue, animation.relative_to_self, 0.0f,  

                animation.relative_to_self, toyvalue);// 当前位置移动到指定位置  

        mtranslateanimation.setfillafter(true);// 设置一个动画效果执行完毕后,view对象保留在终止的位置。  

        mtranslateanimation.setduration(300l);  

        return mtranslateanimation;  

    /** 移动的时候触发 */  

    public void onmove(int x, int y) {  

        // 拖动的view下方的postion  

        int dposition = pointtoposition(x, y);  

        // 判断下方的postion是否是最开始2个不能拖动的  

        if (dposition > 1) {  

            if ((dposition == -1) || (dposition == dragposition)) {  

                return;  

            dropposition = dposition;  

            if (dragposition != startposition) {  

                dragposition = startposition;  

            int movecount;  

            // 拖动的=开始拖的,并且 拖动的 不等于放下的  

            if ((dragposition == startposition)  

                    || (dragposition != dropposition)) {  

                // 移需要移动的动item数量  

                movecount = dropposition - dragposition;  

            } else {  

                // 移需要移动的动item数量为0  

                movecount = 0;  

            if (movecount == 0) {  

            int movecount_abs = math.abs(movecount);  

            if (dposition != dragposition) {  

                // draggroup设置为不可见  

                viewgroup draggroup = (viewgroup) getchildat(dragposition);  

                draggroup.setvisibility(view.invisible);  

                float to_x = 1;// 当前下方positon  

                float to_y;// 当前下方右边positon  

                // x_vlaue移动的距离百分比(相对于自己长度的百分比)  

                float x_vlaue = ((float) mhorizontalspacing / (float) itemwidth) + 1.0f;  

                // y_vlaue移动的距离百分比(相对于自己宽度的百分比)  

                float y_vlaue = ((float) mverticalspacing / (float) itemheight) + 1.0f;  

                log.d("x_vlaue", "x_vlaue = " + x_vlaue);  

                for (int i = 0; i < movecount_abs; i++) {  

                    to_x = x_vlaue;  

                    to_y = y_vlaue;  

                    // 像左  

                    if (movecount > 0) {  

                        // 判断是不是同一行的  

                        holdposition = dragposition + i + 1;  

                        if (dragposition / ncolumns == holdposition / ncolumns) {  

                            to_x = -x_vlaue;  

                            to_y = 0;  

                        } else if (holdposition % 4 == 0) {  

                            to_x = 3 * x_vlaue;  

                            to_y = -y_vlaue;  

                        } else {  

                    } else {  

                        // 向右,下移到上,右移到左  

                        holdposition = dragposition - i - 1;  

                            to_x = x_vlaue;  

                        } else if ((holdposition + 1) % 4 == 0) {  

                            to_x = -3 * x_vlaue;  

                            to_y = y_vlaue;  

                    viewgroup moveviewgroup = (viewgroup) getchildat(holdposition);  

                    animation moveanimation = getmoveanimation(to_x, to_y);  

                    moveviewgroup.startanimation(moveanimation);  

                    // 如果是最后一个移动的,那么设置他的最后个动画id为lastanimationid  

                    if (holdposition == dropposition) {  

                        lastanimationid = moveanimation.tostring();  

                    moveanimation.setanimationlistener(new animationlistener() {  

                        @override  

                        public void onanimationstart(animation animation) {  

                            // todo auto-generated method stub  

                            ismoving = true;  

                        public void onanimationrepeat(animation animation) {  

                        public void onanimationend(animation animation) {  

                            // 如果为最后个动画结束,那执行下面的方法  

                            if (animation.tostring().equalsignorecase(  

                                    lastanimationid)) {  

                                dragadapter mdragadapter = (dragadapter) getadapter();  

                                mdragadapter.exchange(startposition,  

                                        dropposition);  

                                startposition = dropposition;  

                                dragposition = dropposition;  

                                ismoving = false;  

                    });  

}  

数据库sqlhelper文件

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

public class sqlhelper extends sqliteopenhelper {  

    public static final string db_name = "database.db";// 数据库名称  

    public static final int version = 1;  

    public static final string table_channel = "channel";//数据表   

    public static final string id = "id";//  

    public static final string name = "name";  

    public static final string orderid = "orderid";  

    public static final string selected = "selected";  

    private context context;  

    public sqlhelper(context context) {  

        super(context, db_name, null, version);  

        this.context = context;  

    public context getcontext(){  

        return context;  

    public void oncreate(sqlitedatabase db) {  

        // todo 创建数据库后,对数据库的操作  

        string sql = "create table if not exists "+table_channel +  

                "(_id integer primary key autoincrement, " +  

                id + " integer , " +  

                name + " text , " +  

                orderid + " integer , " +  

                selected + " selected)";  

        db.execsql(sql);  

    public void onupgrade(sqlitedatabase db, int oldversion, int newversion) {  

        // todo 更改数据库版本的操作  

        oncreate(db);  

注:本demo中,加入了长按震动,所以在权限里面记得加上“

gradeview可拖动效果实现 一、开发心里历程 二、开发前的准备 三、开发思路 四、流程图 五、核心代码 六、源码下载

<!-- 在sdcard中创建与删除文件权限 -->  

<uses-permission android:name="android.permission.mount_unmount_filesystems" />  

<!-- 往sdcard写入数据权限 -->  

<uses-permission android:name="android.permission.write_external_storage" />  

<!-- 震动权限 -->  

<uses-permission android:name="android.permission.vibrate"/>