0

I have a ListView with rows that contain ImageButtons (shown as 'O' below):

[<-------TextView-------> O O O]
[<-------TextView-------> O O O]

I am finding that the first row has intermittent behaviour. At first it appeared that the image button onclicklisteners were not being called for just the top row. What I have found is that the onclicklisteners for the buttons do get called, but the clicks seem to be queued/buffered until I click on the row itself.

I will happily post some code if nobody knows the reason for this but my code is quite large and would need pruning before posting here. I am using a cursor adapter and custom viewbinder but the click listeners are assigned within the onCreate() of my activity.

The click listeners themselves I assign to the image button objects within the setViewValue() method of my CustomViewBinder class.

I haven't used a ViewHolder (ConvertView etc) - is this my problem? It does look as though the click listeners are being assigned 3 times for every row even though I only do so if the columnIndex is equal to 1.

Here is the row item XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_header"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="@drawable/row_selector"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_header"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0"
        android:layout_gravity="center_vertical"
        android:paddingLeft="4dp"
        android:background="@color/listSection"
        android:text=""
        android:textColor="@android:color/white"
        android:textStyle="bold"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal" >

    <!--
      ~ Use android:layout_weight so that this view will take up all the space
      ~ remaining when no other view has specifed its weight.
     -->
    <TextView
        android:id="@+id/lv_tune"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:layout_marginLeft="10dp"
        android:text="Tune"
        android:textSize="20sp"
        android:textColor="@color/lvForeground"/>

    <!-- 
      ~ android:gravity is for the gravity in the View itself, while
      ~ android:layout_gravity is for the gravity of the View in it's
      ~ ViewGroup(container), in this case it may not be what you intended to
      ~ do, so I removed it for you.
     -->

    <ImageView
        android:id="@+id/ibAddPlaylist"
        android:layout_width="40dp"
        android:layout_height="34dp"
        android:layout_gravity="center"
        android:visibility="visible"
        android:src="@drawable/clipboard30x30grey" />

    <ImageView
        android:id="@+id/ibLikeHate"
        android:layout_width="40dp"
        android:layout_height="34dp"
        android:layout_gravity="center"
        android:visibility="visible"
        android:src="@drawable/heart1_30x30grey" />

    <ImageView
        android:id="@+id/ivPlay"
        android:layout_width="40dp"
        android:layout_height="34dp"
        android:layout_gravity="center"
        android:visibility="invisible"
        android:src="@drawable/play_30x30" />

  </LinearLayout>
</LinearLayout>

And here is the getView() method:

       @Override
       public View getView( int position, View convertView, ViewGroup parent )
       {
         final View view = super.getView( position, convertView, parent );
         final TextView text = (TextView) view.findViewById( R.id.lv_tune );
         final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) text.getLayoutParams();

         if( params != null )
         {
           int hei = params.height;

           if( IsStartOfAlphaSection( position ) )
           {
             // PDS: Looking at the layout for the TextView here..
             if( hei == LayoutParams.WRAP_CONTENT )
             {
               // PDS: But modifying the layout of the parent view..
               // view.setBackgroundColor( 0xFFDDDDDD );

               // PDS: Parent does not have LinearLayout - uses
               // AbsListView.LayoutParams
//*** BELOW LINE IS THE PROBLEM ..
               view.setLayoutParams( new AbsListView.LayoutParams( AbsListView.LayoutParams.MATCH_PARENT, g_NormalRowHeight + g_SectionHeaderHeight ) );
               view.requestLayout();
//***
             }
           }
           else
           {
             // PDS: Normal line

             // PDS: Looking at the layout for the TextView here..
             if( hei == LinearLayout.LayoutParams.WRAP_CONTENT )
             {
               // PDS: But modifying the layout of the parent view..
               // view.setBackgroundColor( Color.WHITE );
               view.setLayoutParams( new AbsListView.LayoutParams( AbsListView.LayoutParams.MATCH_PARENT, g_NormalRowHeight ) );
               view.requestLayout();
             }
           }
         }

         return view;
       }

I have highlighted the line(s) that are causing my problem. What I do is make the row height bigger for items that are group/section headers. This changes the layout that is used and also seems to mess up the click handlers for the imageviews on the first row. It also causes touching of the first row to continuously highlight the first row. Click on any other row and the higlight disappears.

UPDATE: Taking mmlooloo's suggestions into account, the below code seems to be working for me. Looks pretty ugly though (so did the original code that I posted too! ;-) )

public View getView( int position, View convertView, ViewGroup parent )
{
     final View view;

     LayoutInflater li = (LayoutInflater) mContext.getSystemService( Activity.LAYOUT_INFLATER_SERVICE );

     if( IsStartOfAlphaSection( position ) )
     {
       view = li.inflate( R.layout.like_hate_row_header,  parent, false );
     }
     else
     {
       view = li.inflate( R.layout.like_hate_row,  parent, false );
     }

     // NOT calling super.getView() stops ViewBinder::setViewValue() from being called
     super.getView( position, view, parent );     
}   
SparkyNZ
  • 6,266
  • 7
  • 39
  • 80
  • `ListView` intercepts events (for scrolling for example). Facing the same problem i was advised to use a `LinearLayout` instead. – Antoine Marques Aug 07 '14 at 09:46
  • @AntoineMarques: If I get rid of ListView, how would I implement scrolling? That sounds like a rather drastic change. – SparkyNZ Aug 07 '14 at 09:53
  • @SparkyNZ please show me the code of your getView and list item layout – mmlooloo Aug 07 '14 at 09:55
  • Just put the `LinearLayout` inside a `ScrollView`. Indeed it's annoying, because besides one cannot directly use `ListView`'s adapter mechanism. Maybe there's a better solution using `ListView` and specifying your items are clickable. – Antoine Marques Aug 07 '14 at 10:00
  • @mmlooloo: I've added the requested bits. My list view is a grouped/sectioned listview. I increase the size of the row header view if it is the first row in a section. – SparkyNZ Aug 07 '14 at 10:02

1 Answers1

0

I think your getView has some issues, first you do not inflate any layout and you are using the constructor of the getView thats bad idea, you must inflate convertView or using the one passes to you, But in your case that you want to use different view for different position i think you should always inflate convertView. and I recommend you to see this question and answer and the comments of below of my answer to get better sense about convertView.

android list array adapter.notifydatasetchanged not updating view correctly

So if i want to solve your problem i do it in this way: first create two XML files, one represents my none header item and another represents my header items. in the getView i check the position and if it must be header i inflate it by header xml else i inflate it by none header xml, so it will become something like this:

public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = 
                (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View view;
        if(position == header){
              view = inflater.inflate(R.layout.header, parent, false);
              // here you must call finViewById of specific objects or imageButtons 
              //which appear in the header and assign them click listeners  
        }else{
             view = inflater.inflate(R.layout.none_header, parent, false);
              // here you must call finViewById of specific objects or imageButtons 
              //which appear in the NONE header and assign them click listeners 
        }

        return view;

}

and also setting focusable to false for each imageButton;

mmlooloo
  • 18,937
  • 5
  • 45
  • 64
  • I'll give it a try tomorrow. The focusable line in the XML alone didn't help so I'll try moving the click listener assignments to where you suggest. I am concerned that there appear to be multiple assignments taking place. – SparkyNZ Aug 07 '14 at 10:14
  • It made no difference :( Even if the click listener is being assigned multiple times that shouldn't be a problem anyway - its only assigning the same reference. I'm not creating the imageviews each time anyway - I'm just doing a findById() so its not as if I'm instantiating multiple ImageViews. The fact that the clicks are being "buffered" indicates that the onclicklisteners *are* being called.. just not immediately - that I don't understand. Almost looks as if they are on the wrong thread??? – SparkyNZ Aug 08 '14 at 01:42
  • I would like to use TraceView or the debugger to understand what is actually happening but I feel out of my depth at the moment. – SparkyNZ Aug 08 '14 at 01:58
  • look at this : http://stackoverflow.com/questions/25091281/delete-a-row-from-a-listview-android/25108045#25108045 it is some kind of your problem, isn't it? I think the functionality that you want is the same in your project and that, responsive click in a row by clickable widget – mmlooloo Aug 08 '14 at 02:03
  • and another thing: you call if( mSectioned == false ) return super.getView( position, convertView, parent ); and in super constructor are you assigned the click listener for your imagebuttons? – mmlooloo Aug 08 '14 at 02:09
  • For the screen in question, mSectioned is true so it doesn't call the super constructor. At least I don't expect it to.. maybe I should double-check just in case.. – SparkyNZ Aug 08 '14 at 02:12
  • Definitely not calling the super constructor in that condition. Why does this seem to work perfectly for all rows except the very first? I have tried adding OnFocusChangedListener and OnTouchListeners() to the imageview but nothing.. Is the very first row special - maybe its supposed to be a heading row or something when used with cursor adapters etc?? – SparkyNZ Aug 08 '14 at 02:17
  • I could cheat and add a dummy first row :-) That won't help me understanding of the cause of the problem though.. – SparkyNZ Aug 08 '14 at 02:18
  • I have indentified whats causing the problem - just don't know why or what the correct approach should be. I have marked the line in the question. Basically I'm trying to change the height of the rows that contain the "tv_header" which is a section header such as "A" for items starting with the letter "A". The way in which I am changing the height by altering the LayoutParams seems to be causing the problem. – SparkyNZ Aug 10 '14 at 05:27
  • I updated my answer please check it and if you have any issues tell me, now i am going somewhere but i will check it tonight, hope help you. – mmlooloo Aug 10 '14 at 06:05
  • Thanks. I understand what you are suggesting but if I use different XML layouts for header and non-header items, then how do I fit this into the following line? cAdapter = new TVSimpleCursorAdapter( this, R.layout.like_hate_row, null, columns, to, 0 ); SimpleCursorAdapter takes one layout file and tries to configure a row for one line in the database cursor returned. This is why I have been dynamically trying to alter the row height. – SparkyNZ Aug 10 '14 at 06:23
  • If I don't call SimpleCursorLoader::getView(), the CustomViewBinder::setViewValue() method doesn't get called and then I can't set the text in my header (which is derived from what is returned in the Cursor). – SparkyNZ Aug 10 '14 at 06:43
  • I will update the original question with some notes on what seems to work with your suggestions. – SparkyNZ Aug 10 '14 at 07:09
  • Sorry about my last answer , finally i hope find your answer: http://stackoverflow.com/questions/8479833/cursoradapter-with-different-row-layouts and http://stackoverflow.com/questions/13942238/android-does-simplecursoradapter-allow-multiple-layouts-like-baseadapter – mmlooloo Aug 10 '14 at 07:29
  • Don't apologise - it really helped. I'm going to accept your answer - couldn't have fixed it without your help. – SparkyNZ Aug 10 '14 at 08:18