Issues with Button press states in each ListView r

2019-07-29 06:33发布

问题:

I'm having problems with button presses in ListView rows. The background attribute for each Button refers to a XML selector file; in order to select a different image on button presses. I'm able to get press events from OnClickListener, but the state selector breaks and does not register presses with android:state_pressed="true" and android:state_focused="false".

If I remove android:descendantFocusability="blocksDescendants" from the parent/root Layout XML for the button; then the press state will work, but will fire no matter where you touch on the ListView row, which is annoying.

My problem: cannot manage the press/default states of rows separate from the buttons inside. They interfere with each other.

Code:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/mainListLayout"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content" 
   android:descendantFocusability="blocksDescendants">
..........

Row item button further down:

<Button
        android:id="@+id/deleteButton"
        android:background="@drawable/del_button_selector"
        .....
        .....
        />
..........

And the drawable/del_button_selector:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/deleteico" android:state_focused="true" android:state_pressed="false"/>
    <item android:drawable="@drawable/deletepressed" android:state_focused="true" android:state_pressed="true"/>
    <item android:drawable="@drawable/deleteico" android:state_focused="false" android:state_pressed="true" />

Changing the drawable background in the last selector line there does not work. It will show the pressed image but it does not matter where you click on the row, away from the button.

I can change the background on button click events but I need to switch back to default background upon button release, which is hard to capture(?). If I can capture press/release events in listeners then that would be great for buttons only.

Any help appreciated! Thanks!

回答1:

The easy solution is to set android:clickable="true" to the parent view.

This way it will intercept the touch event and the child views will not be highlighted.

In your concrete case:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/mainListLayout"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" 
      android:clickable="true">


回答2:

Did you find an answer?

I've had this problem also. I think there is a bug in the android framework (and/or we are facing - as usual - a poor framework design). However there is a xml field called android:duplicateParentState it is useless in this case (to tell the truth I used it in another project to duplicate the state of the parent setting it to true).

So my solution was this (I know it is ugly and should be an easier way to do this):

  • define 2 different drawables: one for the pressed and one for the unpressed state
  • parent of the button defines android:descendantFocusability="blocksDescendants"
  • set an onTouchListener on the button

This latter looks like this:

    button.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                v.setBackgroundDrawable(mButtonDownDrawable); // this call is deprecated since Jelly Bean, use setBackground() instead
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_OUTSIDE: // just in case. this is never called
            case MotionEvent.ACTION_CANCEL:
                v.setBackgroundDrawable(mButtonUpDrawable);
                break;
            case MotionEvent.ACTION_MOVE:
                final Rect rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
                if(!rect.contains((int)event.getX(), (int)event.getY())){
                    // User moved outside bounds
                    v.setBackgroundDrawable(mButtonUpDrawable);
                }
                break;
            }
            return false;
        }
    });