android 控件拖拽功能,Android自定义图片拖拽控件
效果图
基本思路
布局示意图
我们先考虑简单的情况,两个控件之间的图片拖拽,首先我们需要准备ImageViewA和ImageViewB两个ImageView,然后在里面设置图片。接着我们需要考虑拖拽事件的触发条件,这里假设为手指从ImageView的某个边缘滑出一段距离即触发拖拽事件
假设我们此时在ImageViewA的边缘向下滑动了一段距离,在触发拖拽事件的时候我们需要将ImageViewA的图片隐藏,然后将A的图片传递给一个半透明的ImageViewC并将C显示出来,由于这个ImageViewC同一时间只会有一个,所以我们可以在自定义的layout中创建一个ImageView类型的成员变量进行复用,在触发拖拽事件的时候ImageViewC会跟随手指滑动,在手指抬起来的时候判断ImageViewC的中心是否在另一个ImageViewB上,若在则交换A和B的图片
整体思路还是比较清晰的,主要是要创建一个自定义的layout对子View进行管理并自定义事件分发规则
代码实现
自定义Layout
此处选择继承自FrameLayout,因为可以通过margin属性自由控制View所在的位置并且不会影响到其他的View,后期可能会向Layout中添加一些EditText、TextView或者各种自定义View,而这些View可能是要叠在ImageView上面的,因此选择继承自FrameLayout无疑是最合适的,而且它已经帮我们处理了很多细节,我们可以专注于功能的实现
获取子View所在的区域信息
因为对事件进行分发需要子View的位置信息,所以我们需要一个成员变量来保存,此处选择RectF来保存一个View所在的区域,然后将所有View的位置信息存放到一个HashMap中,这样就可以处理两个以上的控件间图片拖拽了
public class ConfigurableFrameLayout extends FrameLayout implements OnTriggerDragListener {
...
// 保存所有子View的区域
private HashMap mChildViewRects;
...
@Override
protected void onLayout(boolean changed,int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right,bottom);
/* 因为layout后每个子View的位置才确定,所以在此处初始化子View的位置信息*/
initChildViewRect();
}
/**
* 获取各子View所在区域
*/
private void initChildViewRect() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams)child.getLayoutParams();
// 避免多次创建对象
RectF rect = mChildViewRects.get(child;
if (rect == null) {
rect = new RectF();
}
// 设置子View所在的矩形区域
rect.set(lp.leftMargin,lp.topMargin,
lp.leftMargin + child.getWidt(),
lp.topMargin +child.getHeight());
mChildViewRects.put(child, rect);
}
}
}
对事件进行分发
子View有可能比较小,如果要两根手指都在子View里面才能对图片进行操作会不太方便,而我们要实现只要一根手指在子View内,另一根手指无论在哪都可以对子View的图片进行操作,并且同一时间只能操作一个子View,此时就需要自定义事件分发规则
自定义事件分发规则可以选择重写dispatchTouchEvent()方法,但是需要考虑的细节比较多,所以我们选择拦截所有事件,然后在onTouchEvent()方法中对事件进行分发
//重写自定义Layout的onInterceptTouchEvent()和onTouchEvent()
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
// 拦截所有事件
return true;
}
private View mCurrentChildView; // 当前正在处理触摸事件的子View
@Override
public boolean onTouchEvent(MotionEventevent) {
// 事件是否被子View消费
boolean handled = false;
// 当前事件流若已被分发给某个子View处理,则将后续事件都分发给该子View
if (mCurrentChildView != null) {
handled = dispatchTouchEventToChild(event, mCurrentChildView);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// 获取位于触点的子View
mCurrentChildView = viewInXY(event.getX(), event.getY());
// 判断子View是否可以触发拖拽事件,可以则为其设置触发时的监听事件
if (mCurrentChildView instanceof TriggerDraggable) {
标签:
相关文章
-
无相关信息