简单的一个控件拖拽交换位置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-11-12 01:20:34