J'ai un ExpandableListView que je veux remplir avec mes vues personnalisées du type NoteView
. NoteView
étend LinearLayout et contient deux boutons et une case à cocher. J'ai presque tout fonctionnant, les NoteView
sont remplis avec des données de support, les listes sont remplies, et les boutons sont cliquables et accomplissent les tâches requises.Comment implémenter une ligne listview personnalisée avec des boutons?
Le problème est que ExpandableListView ne répond plus du tout aux événements click/longclick/keypress (autres que la sélection d'éléments de liste avec trackball/DPAD). J'ai remplacé ma vue personnalisée par un TextView standard et les événements tactiles ont repris leur cours normal. Il est donc presque certain que je fais quelque chose de mal avec ma vue personnalisée ou un paramètre ListView obscur que je néglige.
Voici mon code NoteView et ma mise en page XML. Qu'est-ce que je rate?
//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>
J'ai eu le même problème beaucoup plus tôt dans le développement et avait découvert que focusable = "false" était la solution. Ensuite, j'ai changé la mise en page à ma vue personnalisée au lieu d'une mise en page XML directe et j'ai oublié de le faire à nouveau dans toutes mes expériences. J'avais essayé de bloquer la focalisation des descendants via XML, mais je ne suis jamais allé sur les boutons individuels pour le contrôle de la mise au point. Merci, a parfaitement fonctionné. – CodeFusionMobile