Android ListView with RadioButton/CheckBox in singleChoice mode and a custom row layout

前端 未结 2 618
眼角桃花
眼角桃花 2020-12-06 04:02

I have a ListView, which is in singleChoice mode. All I want is to display a RadioButton to the side, that when clicked highlights to say it is selected, and when a differen

相关标签:
2条回答
  • 2020-12-06 04:21

    EDIT

    Worth mentioning is that I had a custom layout on my list items: There is an icon, a title, a description and then the checkbox or radio button (depending on if it's a single or multiple choice list). My example solution is, hence described by no less than three different parts:

    1. The custom listitem
    2. The sweet tender love: the CheckableLinearLayout implementation
    3. An example ListView configuration

    4. And a bonus:

    5. Example Adapter::getView() implementation.

    So let's get to the magic then, shall we?

    listitem.xml

    <com.dbm.CheckableLinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
    
        <ImageView
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:id="@+id/myIcon" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="@android:style/TextAppearance.Medium"
                android:textStyle="bold"
                android:ellipsize="end"
                android:id="@+id/myTitle" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="@android:style/TextAppearance.Small"
                android:textStyle="italic"
                android:ellipsize="end"
                android:id="@+id/myDescr" />
        </LinearLayout>
    
        <CheckedTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@android:id/text1" />
    
    </com.dbm.CheckableLinearLayout>
    

    CheckableLinearLayout.java

    public class CheckableLinearLayout extends LinearLayout implements Checkable {
    
        private CheckedTextView mCheckedTextView;
        private final Drawable mCheckDrawable;
        private final Drawable mRadioDrawable;
        private boolean mIsChecked;
    
    
           /**
            * Constructor.
            *
            * @param context The context to operate in.
            * @param attrs The attributes defined in XML for this element.
            */
        public CheckableLinearLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            TypedArray typedArray = null;
    
            // Cache the check box drawable.
            typedArray = context.getTheme().obtainStyledAttributes(new int[] {android.R.attr.listChoiceIndicatorMultiple});
    
            if ((typedArray != null) && (typedArray.length() > 0)) {
                mCheckDrawable = typedArray.getDrawable(0);
            }
            else {
                // Fallback if the target theme doesn't define a check box drawable.
                // Perhaps an application specific drawable should be used instead of null.
                mCheckDrawable = null;
            }
    
            // Careful with resources like this, we don't need any memory leaks.
            typedArray.recycle();
    
            // Cache the radio button drawable.
            typedArray = context.getTheme().obtainStyledAttributes(new int[] {android.R.attr.listChoiceIndicatorSingle});
    
            if ((typedArray != null) && (typedArray.length() > 0)) {
                mRadioDrawable = typedArray.getDrawable(0);
            }
            else {
                // Fallback if the target theme doesn't define a radio button drawable.
                // Perhaps an application specific drawable should be used instead of null
                mRadioDrawable = null;
            }
    
            // Careful with resources like this, we don't need any memory leaks.
            typedArray.recycle();
    
            mIsChecked = false;
        }
    
    
        /*
         * (non-Javadoc)
         * @see android.widget.Checkable#isChecked()
         */
        public boolean isChecked() {
            return mIsChecked;
        }
    
    
        /*
         * (non-Javadoc)
         * @see android.view.View#onAttachedToWindow()
         */
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
    
            // Check if there is a valid GUI element that can visualize the current check-state.
            if (mCheckedTextView != null) {
                ViewParent p = getParent();
    
                // Check if the parent of this list item is a ListView
                if (p instanceof ListView) {
                    int choiceMode = ((ListView) p).getChoiceMode();
    
                    // Decide which check-state notation to visualize (check box, radio button or none).
                    switch (choiceMode) {
                        case ListView.CHOICE_MODE_MULTIPLE:
                            mCheckedTextView.setCheckMarkDrawable(mCheckDrawable);
                            break;
    
                        case ListView.CHOICE_MODE_SINGLE:
                            mCheckedTextView.setCheckMarkDrawable(mRadioDrawable);
                            break;
    
                        default:
                            mCheckedTextView.setCheckMarkDrawable(null);
                            break;
                    }
                }
            }
        }
    
    
        /*
         * (non-Javadoc)
         * @see android.view.View#onFinishInflate()
         */
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            mCheckedTextView = (CheckedTextView) findViewById(android.R.id.text1);
        }
    
    
        /*
         * (non-Javadoc)
         * @see android.widget.Checkable#setChecked(boolean)
         */
        public void setChecked(boolean checked) {
            mIsChecked = checked;
    
            if (mCheckedTextView != null) {
                mCheckedTextView.setChecked(mIsChecked);
            }
        }
    
    
        /*
         * (non-Javadoc)
         * @see android.widget.Checkable#toggle()
         */
        public void toggle() {
            setChecked(!mIsChecked);
        }
    
    }
    

    exampleListView.xml

    NOTE! that you will automatically get check boxes if you set the android:choiceMode attribute to "multipleChoice" and radio buttons if you set it to "singleChoice", provided you use the above implementation.

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ListView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:choiceMode="singleChoice"
            android:id="@+id/myList" />
    
    </LinearLayout>
    

    Bonus: MyCustomAdapter::getView()

    This one relies on a Cursor. You will, of course, implement it as you see fit for your needs.

    private final class ViewHolder {
        public ImageView iconView;
        public TextView titleView;
        public TextView descriptionView;
    }
    
    
    /*
     * (non-Javadoc)
     * @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
     */
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = null;
    
        // Only do something if the requested position exists within the Cursor.
        if (mCursor.moveToPosition(position)) {
            ViewHolder viewHolder;
            view = convertView;
    
            if (view == null) {
                // Create and initialize a new view if not created already for this position.
                view = mLayoutInflater.inflate(R.layout.listitem, null);
    
                // Don't "find view by id" each and every time, but rather save a reference
                // to them and associate the references with the list item itself by storing 
                // them in the list items "tag" attribute. When the view is re-used later on, 
                // you already have a reference to its views and don't need to find them 
                // again, which is a time-consuming operation.
                viewHolder = new ViewHolder();
                viewHolder.iconView = (ImageView) view.findViewById(R.id.myIcon);
                viewHolder.titleView = (TextView) view.findViewById(R.id.myTitle);
                viewHolder.descriptionView = (TextView) view.findViewById(R.id.myDescr);
    
                view.setTag(viewHolder);
            }
            else {
                // Get the references to the views for this, already existing list item.
                viewHolder = (ViewHolder) view.getTag();
            }
    
            // Create a bitmap from the byte array in the database.
            byte[] buffer = mCursor.getBlob(mIconColumnIndex);
            Bitmap icon = null;
    
            // Try to decode the byte array if it exists.
            if (buffer != null) {
                icon = BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
            }
    
            // Update the views with new data.
            viewHolder.iconView.setImageBitmap(icon);
    
            String title = mCursor.getString(mTitleColumnIndex);
            viewHolder.titleView.setText(title);
    
            String description = mCursor.getString(mDescriptionColumnIndex);
            viewHolder.descriptionView.setText(description);
        }
    
        // Return a view showing the correct data for the item at 'position'.
        return view;
    }
    

    ORIGINAL ANSWER:

    I can suggest this link:

    http://tokudu.com/2010/android-checkable-linear-layout/

    I myself have had great pleasure in it when I was in your exact position :-) If anything still is unclear, please feel free to specify your question and I will gladly try to help or assist with further code examples (just as mention earlier: I've been in your position just a few days ago).

    0 讨论(0)
  • 2020-12-06 04:21

    I used dbm's answer and it worked fine for me except one thing:

    You should initiate CheckedTextView in onAttachedToWindow() method, not in onFinishInflate(). This is because onAttachedToWindow() called before onFinishInflate() and using the solution above you will never see any drawable in CheckedTextView—it is null when onAttachedToWindow() called.

    0 讨论(0)
提交回复
热议问题