I have to let this slip for now as a purely academic issue but i would very much like to see a solution in near time.
Due to the way that Android handles multitouch you can (as i see it) only trap the event in a single view. I've tried an hack for this envolving a container layout that intercepts the events sees what View it belongs by seeing the coords and changing the action itself so that it seems to the component that it's a single touch event. I compose such events and then route it to the Views.
Does anyone have a better idea to do this?
If someone wants the code for what i described above just ask and i post it!
Have fun and good luck :D JQCorreia
public class Container extends LinearLayout
{
LinkedHashMap<Integer,View> pointers = new LinkedHashMap<Integer,View>();
ArrayList<View> views = new ArrayList<View>();
public Container(Context context) {
super(context);
initialize(context);
}
public Container(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
private void initialize(Context context)
{
}
@Override
public void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
views = LayoutUtil.flattenLayout(this,false);
for(View foo : views)
{
Rect rect = new Rect();
foo.getGlobalVisibleRect(rect);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event)
{
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
int action = event.getAction() & MotionEvent.ACTION_MASK;
if(action==MotionEvent.ACTION_DOWN)
{
for(View v: views)
{
Rect r = new Rect();
v.getGlobalVisibleRect(r);
if (event.getX() > r.left && event.getX() < r.right
&& event.getY() > r.top
&& event.getY() < r.bottom) {
pointers.put(event.getPointerId(0),v);
pointers.get(event.getPointerId(0)).onTouchEvent(event);
break;
}
}
}
if(action==MotionEvent.ACTION_POINTER_DOWN)
{
int pid = event.getAction() >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int index = event.findPointerIndex(pid);
for(View v: views)
{
Rect r = new Rect();
v.getGlobalVisibleRect(r);
if (event.getX(index) > r.left
&& event.getX(index) < r.right
&& event.getY(index) > r.top
&& event.getY(index) < r.bottom) {
pointers.put(pid,v);
MotionEvent copy = MotionEvent.obtain(event);
copy.setAction(MotionEvent.ACTION_DOWN);
copy.setLocation(event.getX(index), event.getY(index));
pointers.get(pid).onTouchEvent(copy);
}
}
}
if(action==MotionEvent.ACTION_POINTER_UP)
{
int pid = event.getAction() >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int index = event.findPointerIndex(pid);
if(pointers.get(pid)!=null) // If the touch was outside any view
{
MotionEvent copy = MotionEvent.obtain(event);
copy.setAction(MotionEvent.ACTION_UP);
pointers.get(pid).onTouchEvent(copy);
pointers.remove(pid);
}
}
if(action==MotionEvent.ACTION_MOVE)
{
for(int i = 0; i<event.getPointerCount();i++)
{
int pid = event.getPointerId(i);
MotionEvent copy = MotionEvent.obtain(event);
copy.setLocation(event.getX(i), event.getY(i));
if(pointers.get(pid)==null) continue; // If the touch was outside any view
pointers.get(pid).onTouchEvent(copy);
}
}
if(action==MotionEvent.ACTION_UP)
{
if(pointers.get(event.getPointerId(0))!=null)
{
pointers.get(event.getPointerId(0)).onTouchEvent(event);
pointers.remove(event.getPointerId(0));
}
}
return true;
}
}
// This is the LayoutUtil.flattenLayout method
public static ArrayList<View> flattenLayout(View view, boolean addViewGroups)
{
ArrayList<View> viewList = new ArrayList<View>();
if(view instanceof ViewGroup)
{
if(((ViewGroup)view).getChildCount()==0)
viewList.add(view);
else
{
if(addViewGroups)
{
viewList.add(view);
}
ViewGroup viewgroup = (ViewGroup) view;
fo开发者_JS百科r(int i = 0; i < viewgroup.getChildCount();i++)
{
viewList.addAll(flattenLayout(viewgroup.getChildAt(i),false));
}
}
}
else if(view instanceof View)
{
viewList.add(view);
}
return viewList;
}
The best solution here is to put
android:splitMotionEvents = false
inside LinearLayout or any Layout your view (Button, TextView, etc) is.
-cheers happy codings
You need to override onInterceptTouchEvent as well to capture motion events. When you return true from onInterceptTouchEvent, all subsequent events (whether inside your view bounds or not) are captured in calls to onTouchEvent up until (and including) the point where the last pointer goes up.
Traditionally, you put enough logic in onInterceptTouchEvent to determine that a pointer has gone down, AND that it has moved beyond some threshold before returning true, but that depends on whether you want to support drag in horizontal and/or vertical directions in parent views. If an ACTION_POINTER_DOWN event is sufficient to trigger the capture, then you can return true immediately.
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getPointerCount() > 1) return false;
return super.dispatchTouchEvent(ev);
}
insert this on activity or view
精彩评论