控件拖拽置换位置

简单的一个控件拖拽交换位置Demo,有些场景下会用到,关于此类功能网上有很多例子,而且Google官方API中也有相应的接口,对这种业务需求进行了一定逻辑封装。这里没有采用官方接口,单纯的从触摸事件入手来简单的实现控件位置交换。
写代码之前先理清楚实现的思路,这里从控件的触摸事件入手,触摸事件有ACTION_DOWN、ACTION_MOVE、ACTION_UP这几个状态,下面先把实现逻辑的思路写出来:
1.界面加载完成后用一个List记录各个子控件相对于父控件的坐标,同时将这些子控件放入一个List中备用。
2.分别给每个子控件添加触摸事件,拖拽交换的主要逻辑在触摸事件中完成。
3.触摸事件:
    当手指落下时:记录当前手指坐标startX、startY,记录当前触摸的控件a的初始位置;
    当手指开始移动:移动过程中onTouch方法会不断执行,这里要做的就是不断的用View的layout(l,t,r,b)方法不断的将被触摸的控件定位到手指移动过的地方,产生的效果就是被触摸的控件a跟随手指进行滑动。获取手指的实时位置与ACTION_DOWN时的位置相减得出手指移动的差值,获取控件a四条边相对于父控件的坐标位置,将这四个坐标分别加上这个差值得出新的四边坐标,调用View的layout(l,t,r,b)方法重新定位控件a,然后重新给startX、startY赋值。
    当手指抬起:判断当前手指位置是否在某一个子控件b的四条边坐标内部,如果不在则让控件a定位回初始位置,如果在则让控件a和控件b交换位置。

有了以上的逻辑分析是不是思路清晰了许多呢,下面就一步步来实现上面的逻辑
首先定义一个布局文件,其中包含几个Button:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:id="@+id/ll_main"
    android:orientation="vertical"
    android:paddingTop="@dimen/DIMEN_80PX"
    >

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button1"
        android:paddingTop="@dimen/DIMEN_40PX"
        android:paddingBottom="@dimen/DIMEN_40PX"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/DIMEN_40PX"
        android:paddingBottom="@dimen/DIMEN_40PX"
        android:text="button2"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/DIMEN_40PX"
        android:paddingBottom="@dimen/DIMEN_40PX"
        android:text="button3"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/DIMEN_40PX"
        android:paddingBottom="@dimen/DIMEN_40PX"
        android:text="button4"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/DIMEN_40PX"
        android:paddingBottom="@dimen/DIMEN_40PX"
        android:text="button5"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/DIMEN_40PX"
        android:paddingBottom="@dimen/DIMEN_40PX"
        android:text="button6"
        />
</LinearLayout>

下面是MainActivity类

package com.donz.exchangebutton;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends ActionBarActivity {
    public static final String TAG = "MainActivity";
    private LinearLayout ll_main;
    private List<Map<String,Integer>> buttons_locations;

    private int parentt;
    private int parentl;
    private View[] views;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        buttons_locations = new ArrayList<>();
        initViews();
        initTouchEvent();
    }

    private void initTouchEvent() {
        for(int j = 0;j<ll_main.getChildCount();j++){
//            给每个按钮绑定点击事件
            ll_main.getChildAt(j).setOnTouchListener(new View.OnTouchListener() {

                int startX;
                int startY;
                int x;
                int y;

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    v.bringToFront();
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
//                      手指落下的起始位置
                            startX = (int) event.getRawX();
                            startY = (int) event.getRawY();
                            int[] location = new int[2];
                            v.getLocationOnScreen(location);
                            x = location[0];
                            y = location[1];
                            break;
                        case MotionEvent.ACTION_MOVE:
                            int newX = (int) event.getRawX();
                            int newY = (int) event.getRawY();
//                      计算出手指在x和y的偏移量
                            int dex = newX - startX;
                            int dey = newY - startY;
//                      实时计算出button的上下左右位置,相对于父元素
                            int l = v.getLeft();
                            int t = v.getTop();
                            int r = v.getRight();
                            int b = v.getBottom();
//                      button在手指移动后应该在的新位置
                            int newl = l + dex;
                            int newr = r + dex;
                            int newt = t + dey;
                            int newb = b + dey;
//                      将button布局到新位置
                            v.layout(newl, newt, newr, newb);

//                      重置手指的起始位置
                            startX = (int) event.getRawX();
                            startY = (int) event.getRawY();
                            break;
                        case MotionEvent.ACTION_UP:
                            int newfingerX = (int) event.getRawX();
                            int newfingerY = (int) event.getRawY();

//                      遍历整个位置列表比较是否当前手指位置和别的button位置有重合,如果有就互换位置否则将该button放回原位置
                            for (int i = 0; i < buttons_locations.size(); i++) {
                                if(i==v.getTag()){
                                    continue;
                                }
                                Map<String, Integer> m = buttons_locations.get(i);
                                int btnl = m.get("l");
                                int btnt = m.get("t");
                                int btnr = m.get("r");
                                int btnb = m.get("b");
                                if (newfingerX > btnl && newfingerY > btnt && newfingerX < btnr && newfingerY < btnb) {
//                                  在某个button范围内
                                    v.layout(btnl - parentl, btnt - parentt, btnr - parentl, btnb - parentt);
                                    views[i].layout(x - parentl, y - parentt, x + v.getWidth() - parentl, y + v.getHeight() - parentt);
//                                  替换view的值
                                    views[(int)(v.getTag())] = views[i];
                                    views[(int)(v.getTag())].setTag(v.getTag());
                                    views[i] = v;
                                    views[i].setTag(i);
//                                  替换map的值
                                    buttons_locations.set(i,buttons_locations.get((int)(v.getTag())));
                                    buttons_locations.set((int)(v.getTag()),m);
                                    return true;
                                }
                            }

//                      返回原位置
                            v.layout(x - parentl, y - parentt, x +v.getWidth() - parentl, y+v.getHeight() - parentt);

                            break;
                    }
                    return true;
                }
            });
        }

    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if(hasFocus){
            int[] parentLocation = new int[2];
            ll_main.getLocationOnScreen(parentLocation);
            parentl = parentLocation[0];
            parentt = parentLocation[1];
            int count = ll_main.getChildCount();
            views = new View[count];
            for (int i = 0;i<count;i++){
                Map<String,Integer> map = new HashMap<>();
                int[] location = new int[2];
                ll_main.getChildAt(i).getLocationOnScreen(location);
                ll_main.getChildAt(i).setTag(i);
                map.put("l", location[0]);
                map.put("t",location[1]);
                map.put("r",ll_main.getChildAt(i).getWidth()+location[0]);
                map.put("b",ll_main.getChildAt(i).getHeight()+location[1]);
                buttons_locations.add(map);
                views[i] = ll_main.getChildAt(i);
            }

        }
    }

    private void initViews() {
        ll_main = (LinearLayout) findViewById(R.id.ll_main);
    }
}

关键逻辑在代码中都给了详细的注释,相信不难看懂,需要注意的几点:

1.getRawX()、getRawY()方法是获取手指在手机屏幕中的坐标,而getX()、getY()则是获取点相对于父控件的坐标。getLeft()、getRight()、getTop()、getBottom()分别是获取控件四条边相对于控件父控件的坐标位置,他们之间和控件的宽度高度使可以换算的。

2.布局文件中控件的一些属性值必须在界面加载完成后才能通过方法获取到,如果直接在Activity的onCreate()方法中获取是获取不到的。这是因为布局渲染需要时间,详情可以去看View的onMeasure()和onLayout()方法。

这里只是提供一个思路和大概的实现,各方面都比较粗糙,因为置换的是空间本身而不是文本内容,各个控件的大小也没处理,如果大小不一的话会出问题。体验方面可以添加一些动画效果。

如果这篇文章内容对您有帮助,那就请动一下手在下方文章结尾后的按钮处给顶一下吧,不胜感激!

源码链接(Android Studio可以直接导入,Eclipse嫌麻烦的话直接建个新项目代码粘过去即可):

源码下载地址点我,我

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-26 20:42:24

控件拖拽置换位置的相关文章

100行代码解析Dojo树控件拖拽案例

案例设定: 创建2个树控件,左右排列. 使用拖动的方式,将树节点从左侧树控件拖拽的右侧树控件. 拖拽过程中右侧树控件要进行验证,确认是否可以方式拖拽中的节点. 放置的处理,识别要放置的节点,获取其信息并动态创建新的节点(基于基础类型进行实例化的过程). 右侧树控件内(实例化之后的节点),同类型节点间支持拖动排序. Dojo版本 1.10.3 图例1:创建2个树控件,左右排列. 图例2.3: 使用拖动的方式,将树节点从左侧树控件拖拽的右侧树控件. 拖拽过程中右侧树控件要进行验证,确认是否可以方式拖

【WPF】总结窗口和控件拖拽的实现

前文 本文只对笔者学习掌握的一般的拖动问题的实现方法进行整理和讨论,包括窗口.控件等内容的拖动. 希望本文能对一些寻找此问题的解决方法的人和一些刚入门的人一些帮助.笔者为WPF初学者,能得到各位的批评指正也是荣幸万分.有更好更多的方法,劳烦与我分享,不胜感激. 本文的各种实现方法其他博客中也都有提及,很多文章内容详实,有图有代码,笔者就不重复造轮子了.就写写自己的一些理解吧. 关键词 Window, UserControls, drag, Thumb 参考资料 http://www.cnblog

C#中实现控件拖拽效果(How to DragDrop Control in C#)

当产品间需要交互实现数据传递,或产品需要从外部导入文件时,通过控件拖拽来实现是个不错的选择.在UI上支持控件拖拽,可极大提升用户体验.拖拽本身并不神秘,它的本质实际是一个数据交换的过程.控件接受从其他地方来的数据,并进行处理.数据交换有多种方法,Windows中剪贴板可能就是用的最多,但最不被注意的一种方法.下面介绍用C#实现控件拖拽,并通过剪切板交换数据. 对于拖拽的对象,需要在MouseDown或ItemDrag中调用DoDragDrop,传递要拖拽的数据对象并触发拖拽.总的来说,当用户调用

duilib中控件拖拽功能的实现方法(附源码)

转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41144283 duilib库中原本没有显示的对控件增加拖拽的功能,而实际使用过程中拖拽功能也是有用武之地的.看群里有人问题duilib怎么支持拖拽,我也就写这篇文章说明一下duilib实现控件拖拽的方法. 当我刚接触duilib不就的时候,考虑过duilib拖拽这个功能,当时的想法是,在xml布局中设置一个浮动的控件,正常状态下他是隐藏的,当出发了拖拽条件后将他显示并且

未能加载视图状态。正在向其中加载视图状态的控件树必须与前一请求期间用于保存视图状态的控件树相匹配。例如,当以动态方式添加控件时,在回发期间添加的控件必须与在初始请求期间添加的控件的类型和位置相匹配

信息: 未能加载视图状态.正在向其中加载视图状态的控件树必须与前一请求期间用于保存视图状态的控件树相匹配.例如,当以动态方式添加控件时,在回发期间添加的控件必须与在初始请求期间添加的控件的类型和位置相匹配. 数据源: System.Web 堆栈信息: 在 System.Web.UI.Control.LoadViewStateRecursive(Object savedState) 在 System.Web.UI.Control.LoadChildViewStateByIndex(ArrayLis

MFC中改变控件的大小和位置(zz)

用CWnd类的函数MoveWindow()或SetWindowPos()可以改变控件的大小和位置. void MoveWindow(int x,int y,int nWidth,int nHeight); void MoveWindow(LPCRECT lpRect); 第一种用法需给出控件新的坐标和宽度.高度: 第二种用法给出存放位置的CRect对象: 例: CWnd *pWnd; pWnd = GetDlgItem( IDC_EDIT1 );    //获取控件指针,IDC_EDIT1为控件

winform groupbox控件放到窗体中间位置

1. 在Form中放一个控件,让其在启动时始终居中 int gLeft = this.Width / 2 - groupControl1.Width / 2; int gTop = this.Height / 2 - groupControl1.Height / 2; groupControl1.Location = new Point(gLeft, gTop);. 2.在设计界面时,先把groupbox控件放到窗体中间位置,然后anchor属性 设置为none 即可

zedgraph控件怎么取得鼠标位置的坐标值(转帖)

我想取得zedgraph控件上任意鼠标位置的坐标值,IsShowCursorValues可以显示鼠标位置的值但是不能提取赋值给其他的变量.用PointValueEvent这个事件又只能得到已经画出的点的坐标,而我想得到任意位置的坐标. c.MouseDownEvent += new ZedGraphControl.ZedMouseEventHandler(myMouseDown);        protected bool myMouseDown(object sender, MouseEve

jquery 拖拽变换位置

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>测试</title><style>.item_content ul { list-style: none; }.item_content ul li { width: 200px; height: 160px; float: left; margin: 10px }.item_content