界面需求:
界面整齐的排列各个图标,长按其中一个,可拖动选中的图标。拖动到想要的位置之后,界面可将图标再次排列整齐。类似一般android手机桌面布局。
控件介绍:
Android为我们提供了GridView 网格视图。GridView 可在控件内添加view子控件,实现view的整齐排列,但是GridView并没有给我们提供拖拽view的功能,因此需要重新编写GridView来实现需要的功能。
public class DragGrid extends GridView{}
如上述代码,重新定义一个DragGrid控件,继承于GridView。
Android事件处理:
事件是我们在与UI交互式发生的,我们点击一个按键时,可能就已经出发好几个事件,例如我们点击数字键“0”,他会涉及到按下事件,和一个弹起(松开)事件,在我们android中还可能涉及到触摸屏事件,所以在android系统中,事件是作为常用的功能之一。而这里我用到的主要有两个:
1 长按LongClick;
2 触碰事件TouchEvent;
适配器Adapter:
适配器(Adapter)是一种界面对象,他用于列表组件和数据来源之间的桥梁。在这里我们用于GridView的数据填充,以及拖动后的布局改变。Android本身提供给我们的配置器不满足我们的需求因此也需要重写一个新的Adapter;
public class DateAdapter extends BaseAdapter {}
主要思路:
触发GridView的子控件的长按监听,需要重写GridView的LongClick的监听。之后进入图标拖拽功能,此功能需要重写触碰事件TouchEvent。但是仅仅如此,会带来一个问题,在没有长按的时候也会触发触碰事件TouchEvent。因此我们需要一个机制来判断LongClick和TouchEvent的先后顺序,在LongClick触发之后才执行TouchEvent。好在android为我们提供了另一个事件——onInterceptTouchEvent。onInterceptTouchEvent是用于拦截手势事件的,每个手势事件都会先调用onInterceptTouchEvent。重写GridView的onInterceptTouchEvent使得在手指触碰屏幕的时候调用LongClick而不是TouchEvent,就可以避免两者之间的矛盾。之后使用Adapter对GridView的内容进行填充,即可完成布局。
具体实现与代码:
具体实现还需要还需要具体的页面布局。布局文件分为两个,一个是主界面,之间调用重写的GridView即可。另一个是GridView的item,我们可以用一个ImageView来显示图标,下面用一个TextView来注释名称。
而将item填充到GridView里就是Adapter的工作了。
获取item:
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(context).inflate(R.layout.item, null);
txtAge = (TextView) convertView.findViewById(R.id.txt_userAge);
txtAge.setText(lstDate.get(position));
return convertView;
}
}
将item填充到GridView中:
final DateAdapter adapter = new DateAdapter(MainActivity.this, l1);
gridView.setAdapter(adapter);
重写onInterceptTouchEvent:
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
return setOnItemLongClickListener(ev);
}
return super.onInterceptTouchEvent(ev);
}
ACTION_DOWN代表手指放下,此时返回setOnItemLongClickListener(ev)即可先执行长按监听。
重写TouchEvent:
public boolean onTouchEvent(MotionEvent ev) {
if (dragImageView != null
&& dragPosition != AdapterView.INVALID_POSITION) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
if(!isCountXY) {
xtox = x-mLastX;
ytoy = y-mLastY;
isCountXY= true;
}
onDrag(x, y);
if(!isMoving )
OnMove(x,y);
break;
case MotionEvent.ACTION_UP:
stopDrag();
onDrop(x, y);
break;
}
}
return super.onTouchEvent(ev);
}
重写LongClick事件:
public boolean setOnItemLongClickListener(final MotionEvent ev) {
this.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView arg0, View arg1,
int arg2, long arg3) {
int x = (int) ev.getX();
int y = (int) ev.getY();
Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache(true));
Bitmap bitmap = Bitmap.createBitmap(bm, 8, 8, bm.getWidth()-8, bm.getHeight()-8);
startDrag(bitmap, x, y);
hideDropItem();
itemView.setVisibility(View.INVISIBLE);
isMoving = false;
return false;
};
});
return super.onInterceptTouchEvent(ev);
}
由于相关代码过于复杂,在此只做简单思路描述:x,y分别代表拖动item的初始坐标。ACTION_MOVE代表手指在屏幕上滑动。长按事件执行时,获取长按item的位置,和截图,隐藏原先Item。事实上用户拖动的是item的截图,item本身是无法拖动的,但这不影响用户体验。之后执行onTouchEvent,当“item”移动到新的位置是,使用动画效果使原先在此位置的item被挤到空位,当然这里的item移动也是一个障眼法。而当手指松开的时候再利用Adapter重新排列item位置。解除原先item的隐藏。至此,android图标拖动布局的实现。