Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android multitouch! hack anyone?

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;
                                for(int i = 0; i < viewgroup.getChildCount();i++)
                                {
                                        viewList.addAll(flattenLayout(viewgroup.getChildAt(i),false));
                                }
                        }      
                }
                else if(view instanceof View)
                {
                        viewList.add(view);
                }
                return viewList;
        }
like image 501
JQCorreia Avatar asked May 09 '11 15:05

JQCorreia


2 Answers

The best solution here is to put

android:splitMotionEvents = false 

inside LinearLayout or any Layout your view (Button, TextView, etc) is.

-cheers happy codings

like image 171
ralphgabb Avatar answered Oct 07 '22 05:10

ralphgabb


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.

like image 32
Robin Davies Avatar answered Oct 07 '22 03:10

Robin Davies