【问题标题】:Android ListView with RadioButton in singleChoice mode and a custom row layoutAndroid ListView 与单选模式下的 RadioButton 和自定义行布局
【发布时间】:2011-05-14 03:07:43
【问题描述】:

我有一个ListView,它处于单选模式。我想要的只是在侧面显示一个 RadioButton,当单击它时突出显示它已被选中,当单击另一个按钮时,它会返回未选中状态并选择新的按钮。为什么这么难?这不应该这么复杂。我花了 DAYS 天来寻找合适的答案,但我一无所获,所以我希望清楚而简洁地问。

我的列表视图布局(R.layout.view_orders)

<?xml version="1.0" encoding="utf-8"?>
<ListView 
        android:choiceMode="singleChoice"
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:divider="@drawable/list_divider"
        android:dividerHeight="1px"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:cacheColorHint="#00000000">
</ListView>

我的自定义行(R.layout.orders_row)

<?xml version="1.0" encoding="utf-8"?>
    
<RelativeLayout
    xmlns:app="http://schemas.android.com/apk/res/com.xxx.xxxxxx"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="6dip">
    
    <com.xxx.xxxxxx.VerticalLabelView
        app:text="SHORT"
        app:textColor="#666"
        app:textSize="14sp"
        android:id="@+id/state"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true" />
    
    <TextView
        android:id="@+id/quantity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/state" 
        android:layout_centerVertical="true"
        android:gravity="center"
        android:textSize="40sp"
        android:layout_margin="2dip"
        android:minWidth="30dip"
        android:textColor="#555" />
        
        
    <RelativeLayout
        android:layout_toRightOf="@id/quantity"
        android:layout_centerVertical="true"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        
        <TextView
            android:id="@+id/instrument"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:textColor="#333"
            android:layout_marginLeft="2dip"
            android:layout_marginRight="2dip"
            />
            
            
        <TextView
            android:id="@+id/deets"
            android:layout_below="@id/instrument"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:textColor="#888"
            android:layout_marginLeft="2dip"
            android:layout_marginRight="2dip"
            />
        
    </RelativeLayout>
    
        <RadioButton
            android:id="@+id/selector"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            />

</RelativeLayout>

我的 onCreate() 方法:

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.view_orders);
    client = new Client(handler);
    ola = new OrdersAdapter(this, R.layout.orders_row, Orders);
    setListAdapter(ola);
    final RelativeLayout loading = (RelativeLayout) findViewById(R.id.loading);
    panel = (PositionsPanel) findViewById(R.id.panel);
    Utility.showProgressBar(loading);
    client.Connect("orders");
}

现在底层的一切都按预期工作了,你点击一个单选按钮,通过它的标签,我可以从列表中适当地选择那个项目,并按照我想要的方式操作它。但是,当单击第一个单选按钮时,将选择最后一个单选按钮。再次单击相同的单选按钮,它现在也被选中。再次单击它,没有任何反应,最后一个和第一个都被选中。现在我单击列表中的任何其他人,它会像预期的那样被选中。单击任何一个选定的单选按钮,没有任何反应,单选按​​钮保持选中状态。

我尝试在 onCreate() 中使用以下内容:

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.view_orders);
    client = new Client(handler);
    ola = new OrdersAdapter(this, android.R.layout.simple_list_item_single_choice, Orders);
    setListAdapter(ola);
    final RelativeLayout loading = (RelativeLayout) findViewById(R.id.loading);
    panel = (PositionsPanel) findViewById(R.id.panel);
    Utility.showProgressBar(loading);
    client.Connect("orders");
}

这根本不显示单选按钮。 太棒了

现在也许(阅读:最有可能),我只是很密集,无法弄清楚这一点,但我看到这个问题被问了很多,但没有真正的答案。大量参考其他教程或 Commonsware 人的书。但是,现在 cmets 已经过时了,而且他的存储库已经发生了很大变化,以至于这些不再是正确的答案。

那么,有没有人知道如何从中获得预期的功能?或者如果失败了,只需将 Gmail 应用程序的源代码传给我即可。 :)

【问题讨论】:

    标签: android listview radio-button listviewitem


    【解决方案1】:

    请记住,ListView 行中的项目是 RECYCLED。这很可能解释了为什么一行上的操作会影响另一行。在马克的书中四处挖掘,你会发现这方面的报道。

    如果您在列表中使用适配器,则可以在适配器上使用 getView() 在每行创建/回收时为其添加点击处理程序,并确保在行项目时正确管理状态创建和回收。

    我的方法是将状态存储在我自己的数据结构中,然后在用户上下滚动 ListView 时使用 getView() 将其镜像到 UI 中。可能有更好的解决方案,但这对我有用。

    【讨论】:

    • 所以这正是正在发生的事情。我不知道为什么其他任何答案都不仅仅陈述了这个简单的事实。在意识到我正在使用 convertView 并回收它之后,这真的很明显。一两分钟的谷歌搜索,我知道如何解决这个问题。我查看了 Mark 的书第 9 章,“Getting Fancy With Lists”,他的 RateList 示例在那里有详细说明,解释得很清楚。遇到此问题的其他人请查看 Mark Murphy 的“开始 Android”一章。
    • Android 中的这些怪癖确实使它成为一个陡峭的学习曲线,但一旦了解我们确实会获得回收的性能优势。
    • 所以这最终没有奏效,我经历了它,虽然回收绝对是我所看到的背后的原因,马克的书没有详细说明如何解决我遇到的具体问题有。
    • 我不太确定。按钮肯定“知道”它们在哪里,但它们不存储正确的状态。我只是将其更改为删除按钮并完成了它。浪费了太多时间。不过感谢您的帮助,回收绝对是这里的根本问题。
    • 我将状态保存在一个单独的对象中,并在适配器的 getView() 方法中从存储中提取状态,并将其应用于复选框(可能已被回收)。为简单起见,您可以通过每次输入 getView() 时重新创建一个新视图来去除回收,这可能有助于调试。如果你不把它拿回来发布,性能可能会很差。 ListView 在最好的时候非常慢。我建议您明确说明问题所在,然后您将被引导找到解决方案。
    【解决方案2】:

    简单的解决方案:

    在 xml 文件的 RadioButton 布局中添加这一行:

    <RadioButton 
       ...
       android:onClick="onClickRadioButton"
       ...
    />
    

    然后在使用ListView的activity类中,添加如下内容:

       private RadioButton listRadioButton = null;
       int listIndex = -1;
    
       public void onClickRadioButton(View v) {
            View vMain = ((View) v.getParent());
            // getParent() must be added 'n' times, 
            // where 'n' is the number of RadioButtons' nested parents
            // in your case is one.
    
            // uncheck previous checked button. 
            if (listRadioButton != null) listRadioButton.setChecked(false);
            // assign to the variable the new one
            listRadioButton = (RadioButton) v;
            // find if the new one is checked or not, and set "listIndex"
            if (listRadioButton.isChecked()) {
                listIndex = ((ViewGroup) vMain.getParent()).indexOfChild(vMain); 
            } else {
                listRadioButton = null;
                listIndex = -1;
            }
        }
    

    使用这个简单的代码只检查一个 RadioButton。 如果您触摸已选中的那个,则它会返回未选中模式。

    "listIndex" 正在跟踪选中的项目,如果没有,则为 -1。

    如果您想始终选中一项,则 onClickRadioButton 中的代码应为:

       public void onClickRadioButton(View v) {
            View vMain = ((View) v.getParent());
            int newIndex = ((ViewGroup) vMain.getParent()).indexOfChild(vMain);
            if (listIndex == newIndex) return;
    
            if (listRadioButton != null) {
                    listRadioButton.setChecked(false);
            }
            listRadioButton = (RadioButton) v;
            listIndex = newIndex; 
        }
    

    【讨论】:

    • 嗨,这在列表适合一页时有效,但是当列表长于一页时,listIndex 会因为它从第一个可见列表项而不是从实际开始计数而变得混乱的名单。如何解决这个问题?
    • 有没有人用长列表解决了这个问题?
    【解决方案3】:

    我的名字是古拉布辛哈。我解决了这个问题。我写了这段代码来解决它。我希望这对大家有帮助。谢谢。我需要 3 个小时。

    public void add_option_to_list(View v){
    
    
        LinearLayout ll = (LinearLayout)v.getParent();
        LinearLayout ll_helper = null; 
        LinearLayout ll2 =  (LinearLayout)ll.getParent();
        LinearLayout ll3 =  (LinearLayout)ll2.getParent();
    
        ListView ll4 =  (ListView)ll3.getParent();
        //RadioButton rb1= (RadioButton)ll.getChildAt(1);
        Toast.makeText(getApplicationContext(), ", "+ll4.getChildCount(), Toast.LENGTH_LONG).show();    
        //ll.findViewById(R.id.radio_option_button)
        for(int k=0;k<ll4.getChildCount();k++){
    
            ll3 = (LinearLayout)ll4.getChildAt(k);
            ll2 = (LinearLayout)ll3.getChildAt(0);
            ll_helper = (LinearLayout)ll2.getChildAt(0);
            RadioButton rb1 = (RadioButton)ll_helper.getChildAt(1);
            if(rb1.isChecked())
            rb1.setChecked(false);
    
    
        }
        RadioButton rb1 = (RadioButton)ll.getChildAt(1);
        rb1.setChecked(true);
        //Toast.makeText(getApplicationContext(), ""+((TextView)ll.getChildAt(4)).getText().toString(), Toast.LENGTH_LONG).show();
    
    
        }
    

    XML:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <LinearLayout
    
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
    
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal" >
    
                <TextView
                    android:id="@+id/id"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Category Name"
                    android:textAppearance="?android:attr/textAppearanceMedium"
                    android:visibility="gone" />
    
                <RadioButton
                    android:id="@+id/radio_option_button"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:onClick="add_option_to_list"
                    android:text="" />
    
                <TextView
                    android:id="@+id/option_name"
                    android:layout_width="wrap_content"
                    android:layout_height="fill_parent"
                    android:layout_marginLeft="3dp"
                    android:layout_weight="5.35"
                    android:text="Category Name"
                    android:textAppearance="?android:attr/textAppearanceMedium" />
    
    
    
                <TextView
                    android:id="@+id/option_price"
                    android:layout_width="76dp"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="10dp"
                    android:gravity="right"
                    android:text="$0.00"
                    android:textAppearance="?android:attr/textAppearanceMedium" />
    
                <TextView
                    android:id="@+id/option_unit_price"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="5dp"
                    android:gravity="right"
                    android:text="$0.00"
                    android:textAppearance="?android:attr/textAppearanceMedium"
                    android:visibility="gone" />
            </LinearLayout>
    
        </LinearLayout>
    
    </LinearLayout>
    

    【讨论】:

    • 欢迎来到 Stack Overflow。我编辑了您的答案以使其易于阅读。顺便说一句,您不需要在答案中写下您的名字;你的名字总是显示在答案的末尾。
    【解决方案4】:

    我刚刚遇到了类似的问题。这似乎与视图回收有关。但是我到处都添加了 Log,并注意到回收处理很好。 问题在于 RadioButtons 处理。我正在使用:

    if(answer == DBAdapter.YES){
       rbYes.setChecked(true); 
       rbNo.setChecked(false);                    
    }
    else if(answer == DBAdapter.NO){
       rbYes.setChecked(false); 
       rbNo.setChecked(true);
    }
    else{
      rbYes.setChecked(false); 
      rbNo.setChecked(false);
    }                
    

    正确的做法是:

    if(answer == DBAdapter.YES){
       rbYes.setChecked(true); 
    }
    else if(answer == DBAdapter.NO){
       rbNo.setChecked(true);
    }
    else{
       RadioGroup rg = (RadioGroup)(holder.answerView);
       rg.clearCheck();
    }                
    

    【讨论】:

      【解决方案5】:

      您可以实现更简单,请按照此操作。在 MarshMallow 中尝试过

      1)有一个CheckedTextView

      <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@android:id/text1"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:checkMark="?android:attr/listChoiceIndicatorSingle"
          android:padding="10dp"
          android:textAppearance="?android:attr/textAppearanceMedium">
      

      • ListChoiceIndicatorSingle 将显示RadioButton

      • ListChoiceIndicatorMultiple 将显示CheckBox

      不要忘记将choiceMode:Single 放入您的ListView

      【讨论】:

        【解决方案6】:

        适配器内部

         viewHolder.radioBtn.setOnClickListener(new OnClickListener() {
        
                       @Override
                       public void onClick(View v) {
                           Log.e("called", "called");
                           if(position != mSelectedPosition && mSelectedRB != null){
                               mSelectedRB.setChecked(false);
                           }
        
        
        
                           mSelectedPosition = position;
                           mSelectedRB = (RadioButton)v;
                       }
                   });
        
                   viewHolder.radioBtn.setText(mList[position]);  
                   if(mSelectedPosition != position){
                       viewHolder.radioBtn.setChecked(false);
        
                   }else{
                       viewHolder.radioBtn.setChecked(true);
        
                       if(mSelectedRB != null && viewHolder.radioBtn != mSelectedRB){
                           mSelectedRB = viewHolder.radioBtn;
                       }
                   }
        

        为单选按钮添加样式

        <?xml version="1.0" encoding="utf-8"?>
         <selector xmlns:android="http://schemas.android.com/apk/res/android">
        
            <item android:drawable="@drawable/tick" android:state_checked="true"/>
            <item android:drawable="@drawable/untick" android:state_checked="false"/>
        
         </selector>
        

        在xml中使用单选按钮样式

        <?xml version="1.0" encoding="utf-8"?>
        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        
            <RadioButton
                android:id="@+id/radioBtn"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:button="@null"
                android:checked="true"
                android:drawablePadding="30dp"
                android:drawableRight="@drawable/checkmark"
                android:text="first" />
        
        </RelativeLayout>
        

        【讨论】:

          【解决方案7】:

          在您的适配器上:

          public void setChecked(View v, int position) {
          
              ViewGroup parent = ((ViewGroup) v.getParent()); // linear layout
          
              for (int x = 0; x < parent.getChildCount() ; x++) {
          
                  View cv = parent.getChildAt(x);
                  ((RadioButton)cv.findViewById(R.id.checkbox)).setChecked(false); // your checkbox/radiobtt id
              }
          
              RadioButton radio = (RadioButton)v.findViewById(R.id.checkbox);
              radio.setChecked(true);
          }
          

          关于你的活动/片段:

             listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                  @Override
                  public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                      ((ModelNameAdapter)adapter).setChecked(view, position);
          
                  }
              });
          

          【讨论】:

          • 位置从未在您设置的检查方法中使用。
          • 为了使这个工作,你不能回收你的视图。不要这样做:如果 convertView == null { convertView = inflate},总是充气一个新的。
          猜你喜欢
          • 2011-06-18
          • 2016-09-06
          • 1970-01-01
          • 2014-05-03
          • 2013-05-07
          • 1970-01-01
          • 2012-07-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多