views:

44

answers:

1

I have an ExpandableListView that I want to populate with my custom views of type NoteView. NoteView extends LinearLayout and contains two buttons and a checkbox. I have almost everything working, the NoteView's are being populated with backing data, the lists are getting filled, and the buttons are clickable and perform the required tasks.

The problem is the ExpandableListView no longer responds to click/longclick/keypress events at all (other than selecting list items with trackball/DPAD).

I replaced my custom view with a standard TextView and the touch events flowed normally again, so it is almost certainly something I am doing wrong with my custom view or some obscure ListView setting I am overlooking.

Here is my NoteView code and XML Layout. What am I missing?

//Custom Note View
public class NoteView extends LinearLayout{

    private CheckBox mCheckBox;
    private TextView mTitleTextView;
    private TextView mDetailsTextView;
    private TextView mDetailsRightTextView;
    private LinearLayout mButtonLayout;
    private Button mDeleteButton;
    private Button mEditButton;

    //data storage
    private long mNoteId = -1;
    private boolean mStretched = false;
    private CharSequence mDetails = "";
    private CharSequence mStretchedDetails = "";
    private CharSequence mDetailsRight = "";
    private CharSequence mStretchedDetailsRight = "";       


    private NoteView.OnNoteButtonClickedListener mEditNoteListener;
    private NoteView.OnNoteButtonClickedListener mDeleteNoteListener;
    private NoteView.OnNoteCheckBoxClickedListener mCheckboxListener;

    public NoteView(Context context) {
        super(context);
        init(context);
    }

    public NoteView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }


    private void init(Context context) {

        this.setPadding(0, 0, 5, 0);
        this.setOrientation(LinearLayout.VERTICAL);

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.noteview_layout, this);//returns the noteview itself, since is parent

        //get views
        mCheckBox = (CheckBox) findViewById(R.id.noteViewCB);
        mTitleTextView = (TextView) findViewById(R.id.noteViewTitle);
        mDetailsRightTextView = (TextView) findViewById(R.id.noteViewDetailsRight);
        mDetailsTextView =  (TextView) findViewById(R.id.noteViewDetails);


        mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if(mCheckboxListener != null)
                {
                    mCheckboxListener.onNoteCheckBoxClicked(mNoteId, isChecked);
                }
            }
        });

        //prepare button layout
        mButtonLayout = (LinearLayout) findViewById(R.id.noteViewButtonLayout);
        mEditButton = (Button) findViewById(R.id.noteViewEditButton);
        mDeleteButton = (Button) findViewById(R.id.noteViewDeleteButton);


        mEditButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if(mEditNoteListener != null)
                {
                    mEditNoteListener.onNoteButtonClicked(mNoteId);
                }
            }
        });

        mDeleteButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if(mDeleteNoteListener != null)
                {
                    mDeleteNoteListener.onNoteButtonClicked(mNoteId);
                }
            }
        });

    }

    public void setOnEditClickedListener(NoteView.OnNoteButtonClickedListener listener)
    {
        mEditNoteListener = listener;
    }

    public void setOnDeleteClickedListener(NoteView.OnNoteButtonClickedListener listener)
    {
        mDeleteNoteListener = listener;
    }


    public void setOnCheckboxClickedListener(NoteView.OnNoteCheckBoxClickedListener listener)
    {
        mCheckboxListener = listener;
    }

    static abstract class OnNoteButtonClickedListener
    {
        public abstract void onNoteButtonClicked(long noteId);
    }


    static abstract class OnNoteCheckBoxClickedListener
    {
        public abstract void onNoteCheckBoxClicked(long noteId, boolean checked);
    }


    public void setNoteId(long noteId)
    {
        mNoteId = noteId;   
    }

    public long getNoteId()
    {
        return mNoteId;
    }

    public void setTitle(CharSequence title)
    {
        mTitleTextView.setText(title);
    }

    public void setChecked(boolean checked)
    {
        mCheckBox.setChecked(checked);
    }

    public boolean isChecked()
    {
        return mCheckBox.isChecked();
    }

    public void setDetails(CharSequence details, CharSequence stretchedDetails)
    {
        if(details == null || details.length() == 0)
        {
            mDetails = "";
        }else
        {
            mDetails = details;
        }

        if(stretchedDetails == null)
        {
            mStretchedDetails = "";
        }
        else
        {
            mStretchedDetails = stretchedDetails;
        }

        refreshStretched();
    }

    public void setDetailsRight(CharSequence detailsRight, CharSequence expandedDetailsRight)
    {
        if(detailsRight == null || detailsRight.length() == 0)
        {
            mDetailsRight = "";
        }else
        {
            mDetailsRight = detailsRight;
        }

        if(expandedDetailsRight == null)
        {
            mStretchedDetailsRight = "";
        }
        else
        {
            mStretchedDetailsRight = expandedDetailsRight;
        }

        refreshStretched();
    }

    public void setStretched(boolean expanded)
    {
        mStretched = expanded;
        refreshStretched();
    }

    public boolean getStretched()
    {
        return mStretched;
    }

    public boolean toggleStretched()
    {
        setStretched(!getStretched());
        return mStretched;
    }

    public void showButtons() {

        if(mButtonLayout.getVisibility() != VISIBLE )
        {
            Animation slideIn = AnimationUtils.loadAnimation(this.getContext(), R.anim.slideonfromright);

            mButtonLayout.setAnimation(slideIn);
            mButtonLayout.setVisibility(VISIBLE);
            mButtonLayout.startAnimation(slideIn);
        }       
    }

    public void hideButtons() {
        if(mButtonLayout != null && mButtonLayout.getVisibility() == VISIBLE )
        {
            Animation slideOut = AnimationUtils.loadAnimation(this.getContext(), R.anim.slideofftoright);
            slideOut.setAnimationListener(new AnimationListener()
            {
                @Override
                public void onAnimationEnd(Animation animation) {
                    mButtonLayout.setVisibility(GONE);
                }
                @Override
                public void onAnimationRepeat(Animation animation) {}
                @Override
                public void onAnimationStart(Animation animation) { }
            });

            mButtonLayout.startAnimation(slideOut);
        }
    }


    public void hideButtons(boolean noAnimation) {
        mButtonLayout.setVisibility(GONE);
    }

    public void refreshStretched() {

        if(mStretched)
        {
            mDetailsRightTextView.setText(mStretchedDetailsRight);
            mDetailsTextView.setText(mStretchedDetails);
        }else
        {
            mDetailsRightTextView.setText(mDetailsRight);
            mDetailsTextView.setText(mDetails);
        }

        if(mDetailsRightTextView.length() == 0)
        {

            mDetailsRightTextView.setVisibility(GONE);
        }else
        {
            mDetailsRightTextView.setVisibility(VISIBLE);
        }

        if(mDetailsTextView.length() == 0)
        {
            mDetailsTextView.setVisibility(GONE);
        }else
        {
            mDetailsTextView.setVisibility(VISIBLE);
        }
    }
}

noteview_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:paddingRight="5dip">
        <CheckBox android:id="@+id/noteViewCB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
        <TextView android:id="@+id/noteViewTitle"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:text="Title"/>
        <LinearLayout
                android:id="@+id/noteViewButtonLayout"
                android:orientation="horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="5dip"
                android:visibility="gone"
                android:layout_gravity="center_vertical">
            <Button android:id="@+id/noteViewEditButton"
                    android:layout_width="80dp"
                    android:layout_height="fill_parent"
                    android:background="@drawable/drawngreenbutton"
                    android:textStyle="bold"
                    android:text="Edit"/>
            <Button android:id="@+id/noteViewDeleteButton"
                    android:layout_width="80dp"
                    android:layout_height="fill_parent"
                    android:background="@drawable/drawnredbutton"
                    android:textStyle="bold"
                    android:text="Delete"/>
        </LinearLayout>
    </LinearLayout>
    <LinearLayout  android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <TextView android:id="@+id/noteViewDetails"
    android:layout_width="0dip"
    android:layout_height="wrap_content"
    android:layout_weight="2"
    android:layout_marginRight="10dip"
    android:visibility="gone"
    android:focusable="false"
    android:bufferType="spannable"/>
    <TextView 
    android:id="@+id/noteViewDetailsRight"
    android:layout_width="0dip"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:visibility="gone"
    android:focusable="false"
    android:bufferType="spannable"
    android:gravity="right"/></LinearLayout>
</merge>
+2  A: 

I've had a similar issue that happends when use a checkbox is in the listview items layout. Please check if adding the attribute: android:focusable="false" to the checkbox definition helps you like:

<CheckBox android:id="@+id/noteViewCB" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content"
        android:focusable="false" />

The issue seems to be that Android doesn't allow you to select list items that have elements on them that are focusable.

Best,

fpanizza
I had the same issue much earlier in development and had found out that focusable="false" was the solution. Then I changed the layout to my custom view instead of a direct XML layout and forgot to do that again in all my experimenting. I had tried blocking descendant focusability via xml, but I never went to the individual buttons for focus control. Thanks, worked perfectly.
CodeFusionMobile