anddev84 anddev84 - 3 months ago 14
Android Question

Using ViewStub within ListViews causes onItemClick problems

I have an interesting dilemma. I have a

ListView
which I am using to contain
CardView
s. However, each
CardView
is actually a pair of two: one default (front) card, and then a back card that I am animating as a flip upon the user clicking the card. Since the user may not click on any list item, I am using a
ViewStub
as the back card, and will lazy inflate upon the list item click.

So the first time the user clicks a card to flip to the back, everything looks good and the flip works (
onItemClick()
gets called from clicking anywhere within the card). However, any further attempts to flip over to the front again do not trigger the
onItemClick()
call, UNLESS I click the very top of the view (list item).

I've done lots of debugging (changing focusability and sizing) and figured out that this is being caused only after the
ViewStub
inflation. Making both front and back cards normal
CardView
s allows the
onItemClick()
to fire successfully every time.

So what is the reason why the
ViewStub
is fubaring my
onItemClick()
? Is there a way to fix it, rather than have all of my views inflated automatically?

Here is the code I'm using:

card_container_layout.xml

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

<include layout="@layout/card_front_view"
android:id="@+id/main_card_view_layout" />

<ViewStub android:id="@+id/stub_flipped_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inflatedId="@+id/flipped_card"
android:layout="@layout/card_back_view" />
</FrameLayout>


card_front_view.xml

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_height="wrap_content"
card_view:cardCornerRadius="5dp"
android:id="@+id/card_view"
android:descendantFocusability="blocksDescendants" >

<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/card_view_height">
<ImageView android:id="@+id/img_main_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/my_image"
android:background="#ff10fffd" />

<TextView
android:id="@+id/lbl_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Name"
style="@style/main_card_title_text"
android:layout_alignParentBottom="true"
android:padding="15dp"
android:background="#5b000000"/>

</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:padding="4dp" >

<TextView
android:id="@+id/lbl_item_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
style="@style/main_card_text" />

<ImageView android:id="@+id/img_sync_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_centerVertical="true"/>
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>


card_back_view.xml

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
android:layout_width="match_parent"
card_view:cardCornerRadius="5dp"
android:id="@+id/flipped_card"
android:alpha="0"
android:descendantFocusability="blocksDescendants">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Here are the items:"
android:gravity="center"
style="@style/card_flipped_title_text"
android:background="#ad000000"/>
<GridView android:id="@+id/grid_images"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:numColumns="3">
<!-- make believe there were items here -->
</GridView>
</LinearLayout>
</android.support.v7.widget.CardView>

Answer

Ok, I did more digging into it and it appears that it's not the ViewStub at all causing it, but in fact the GridView. For reasons that I was not able to figure out, the GridView continued to hijack all touch events, despite setting focusable and clickable to false and blocking descendent focusability as seen in the sample XML above.

Therefore the hacky way I've worked around it, is to intercept all touch events from the GridView, and within the onTouchListener(), if I detect a MotionEvent.ACTION_UP, indicating that I have been touching the screen and just released, I will then manually perform the same action as I am doing in the rest of the CardView.

itemGrid.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View grid, MotionEvent event) {
            // hacky way to make sure that we absorb the GridView's focus stealing and still do the right thing
            if (MotionEvent.ACTION_UP == event.getAction()) doExpectedStuff();
            return true; // absorb the touch here
        }
});

Sorry that this isn't the most elegant of solutions, and agree that capturing an up event is not a one-size-fits-all approach for most people, but it helped in my specific situation. Hope that this still may help others.