How to implement a custom listview row with buttons?
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 expan开发者_如何学CdedDetailsRight)
{
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>
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,
精彩评论