4

I have a Fragment which consists of a Button overlaid on a FrameLayout. A second ListView Fragment is inserted into the FrameLayout. The ListView scrolls correctly except if the touch event starts on top of the Button. The Button's onClick listener seems to intercept the scroll of the beneath ListView. How can I have the Button process click events, but not scroll events.

I have a basic solution working using a GestureDetector on the Button and passing the onScroll event to the ListView's smoothScrollBy function. This doesn't work easily for fling events though.

This question seems similar albeit the reverse situation. I don't think the accepted answer helps me.

Example:

main_fragment.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/sub_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="80dp"
        android:layout_height="70dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="#999"
        android:text="Button" />

</RelativeLayout>

Inflated by MainFragment:

public class MainFragment extends Fragment {

    private static final String TAG = MainFragment.class.getSimpleName();

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.main_fragment, container, false);

        Button button = (Button) view.findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d(TAG, "click");
            }
        });

        FragmentTransaction fragmentTransaction = getActivity()
                .getSupportFragmentManager().beginTransaction();
        ListFragment f = new ListFragment();
        fragmentTransaction.replace(R.id.sub_content, f);
        fragmentTransaction.commit();

        return view;
    }
}

The Button has an onClickListener and a second fragment is inflated in place of the FrameLayout:

fragment_item_list.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

Inflated by ListFragment:

public class ListFragment extends Fragment {

    private static final String TAG = ListFragment.class.getSimpleName();

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_item_list,
                                     container, false);

        ListView listView = (ListView) view.findViewById(R.id.list);

        List<String> list = new ArrayList<String>();
        for (int i=0; i<30; i++) {
            list.add("Item: " + i);
        }
        MyAdapter adapter = new MyAdapter(getActivity(), list);
        listView.setAdapter(adapter);

        return view;
    }

    private class MyAdapter extends BaseAdapter {

        private List<String> items;
        private Context context;

        public MyAdapter(Context context, List<String> items) {
            this.items = items;
            this.context = context;
        }

        @Override
        public int getCount() {
            return items.size();
        }

        @Override
        public String getItem(int i) {
            return items.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View convertView, ViewGroup parent) {
            if (convertView==null) {
                convertView = LayoutInflater.from(context).inflate(
                        R.layout.list_item, parent, false);
            }

            TextView textView = (TextView) convertView.findViewById(
                                R.id.list_item_text);
            textView.setText(getItem(i));

            return convertView;
        }
    }
}
Jon G
  • 1,656
  • 3
  • 16
  • 42

1 Answers1

3

The solution I found was to remove the onClickListener on the button and instead have a SimpleGestureDetector on the Button (see the docs).

class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        clickButton();
        return true;
    }
}

Where the button implements this in an onTouchListener:

mDetector = new GestureDetectorCompat(getActivity(), new MyGestureListener());
button.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        return mDetector.onTouchEvent(motionEvent)
               || dispatchTouchEvent(motionEvent);
    }
});

And the Button passes the touch event to the ListView via an interface (inside dispatchTouchEvent function) and calls:

listView.dispatchTouchEvent(motionEvent)

Useful reading includes this SO question and also this.

Community
  • 1
  • 1
Jon G
  • 1,656
  • 3
  • 16
  • 42