草庐IT

android - 通过 ListView 检查动态生成的复选框时出现问题

coder 2023-06-07 原文

我知道其他成员已经提出了这个问题,并且一些成员也给出了解决方案,但问题是我没有找到适合我的应用程序的任何解决方案。 我正在创建一个应用程序,其中我有一个屏幕,它将显示带有列表项的动态 ListView ,一个复选框和三个 TextView (一个用于候选人姓名,另外两个用于时钟输入和时钟输出时间,将在选择日期和时间后显示日期时间选择器)。现在我的问题是,当我检查第一个复选框(我有 15 个带有复选框的候选人名称)时,第 10 个复选框会自动检查自己,这也发生在第 2 和第 11、第 3 和第 12 等等(反之亦然) true).这里我提供了我的适配器类和列表项 xml。

import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;

import com.android.feedback.ListViewCheckBox;

public class DemoAdapter extends ArrayAdapter<String>{

    private final List<String> list;
    private final Activity context;
    LayoutInflater inflater;
    TextView CItv,COtv;
    static ViewHolder holder;
    View view;

    public DemoAdapter(Activity context, List<String> list) {
        super(context, R.layout.test_listitems,list);
        // TODO Auto-generated constructor stub

        this.context = context;
        this.list = list;
    }

    static class ViewHolder {
        protected TextView text,CItv,COtv;
        protected CheckBox checkbox;
    }


    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
          view = null;
        //  final ArrayList<Integer> checkedItems = new ArrayList<Integer>(); 
        if (convertView == null) {

            inflater = context.getLayoutInflater();
            view = inflater.inflate(R.layout.test_listitems, null);
            final ViewHolder viewHolder = new ViewHolder();
            viewHolder.CItv = (TextView)view.findViewById(R.id.CITextView);
            viewHolder.COtv = (TextView)view.findViewById(R.id.COTextView);
            viewHolder.text = (TextView) view.findViewById(R.id.empTextView);
            viewHolder.checkbox = (CheckBox) view.findViewById(R.id.empCheckBox);

            viewHolder.checkbox
                    .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                        @Override
                        public void onCheckedChanged(CompoundButton buttonView,
                                boolean isChecked) {


                            if(isChecked){  
                                Object o = getItemId(position+1);
                                String keyword = o.toString();
                                Toast.makeText(getContext(), "You selected: " + keyword, 2000).show();

                            Toast.makeText(getContext(),ListViewCheckBox.DT_selected, 2000).show();
                                //  holder.CItv.setText(ListViewCheckBox.DT_selected);
                                //  holder.COtv.setText(ListViewCheckBox.outDT_selected);
                                }

                            else{
                                Object o = getItemId(position+1);
                                String keyword = o.toString();
                                //Toast.makeText(getContext(), "You unselected: " + keyword, 2000).show();
                                holder.CItv.refreshDrawableState();
                                holder.COtv.refreshDrawableState();

                            }



                        }
                    });

            view.setTag(viewHolder);
            viewHolder.checkbox.setTag(list.get(position));
            viewHolder.checkbox.setId(position);
        } else {
            view = convertView;
            ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position));
        }
        holder = (ViewHolder) view.getTag();
        holder.text.setText(list.get(position));



        return view;

        }

    }

和 XML。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">


    <TableLayout android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:stretchColumns="1,2,3">

    <TableRow >


     <CheckBox    android:text=" " android:id="@+id/empCheckBox"
                  style="@style/Check" android:textColor="#000000"
                  android:textSize="12dp" 
                  android:layout_weight="1"/>

     <TextView    android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:id="@+id/empTextView"
                 style="@style/CICOTextView"
                 android:layout_weight="2"/>

    <TextView    android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:id="@+id/CITextView"
                 style="@style/CICOTextView"
                 android:text=""
                 android:layout_weight="3"/>    

    <TextView    android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:id="@+id/COTextView"
                 style="@style/CICOTextView"
                 android:text=""
                 android:layout_weight="4"/>     

    </TableRow>
    </TableLayout>
</LinearLayout>

请帮我解决这个问题。(ListViewCheckBox 是一个生成列表并将日期和时间值存储在变量 DT_selected 和 outDT_selected 中的类)。

最佳答案

我编辑了我的答案,因此常见信息位于顶部。您将在底部找到此问题的实际答案...


这是回收的实际想法和过程,因此您可能会弄清楚 getView 的实现和想法有什么问题(也许其他人也会发现这个问题和答案)。请参阅下面的代码示例,只需忽略类型部分,因为这是附加信息。

  • 第 1 阶段:为回收利用创建项目(convertViewnull):
    这意味着您创建了所有项目共享的布局和公共(public)状态。如果您有监听器,您可以在此处添加它们并设计它们,以便它们可以在以后对位置更改(当它被重用时)使用react。因此,例如通过将位置设置为相应 View 上的标记,以便监听器可以捕获此信息并知道它当前正在操作哪个项目。您不能使用 View 来存储数据。因此,当监听器更改列表项的状态时,您应该保留这些数据(在数据数组、SQLite 数据库等中)并在阶段 2 中使用它。

  • 第 2 阶段:为给定位置设置项目状态:
    您设置项目的视觉状态。必须在此处设置可能为项目单独更改的所有内容(文本、复选框状态、颜色等)。不仅当前项目发生了更改,而且可能已被另一个项目更改。这样可以确保 View 不会在 无效 状态下使用,因为它之前被另一个列表项重用过。


已删除/编辑了已接受的答案,但建议实现 getItemViewTypegetViewTypeCount 以便每个列表项都有自己的 View 类型。 The edited answer现在展示如何按照此处描述的方式解决问题。

重新实现 getItemViewTypegetViewTypeCount 有效,但您显然误解了它的用途(比较下面的示例和/或 this answer)。

这两种方法用于使用两个(或更多)彼此完全不同的列表项(例如,一个公共(public)列表项和一个仅包含标题的分隔符),而不是避免回收可以重用的 View .

如果您仍然使用它们来解决您的问题,那么您可能不理解我之前解释的过程。所以例如您有 1000 个项目并且您执行 View 类型hack,然后您将创建 1000 个 View (层次结构)而不是可能 10 个可以重复使用的轻松。如果您只有 20 个左右的项目,那应该没那么重要,但是如果您将这种技术用于大型列表,您只是在浪费(宝贵的)内存!

这是一个例子:

void getItemViewType(int position) {
    return isItemAtPositionSeperator(position) ? 1 : /* normal item */ 0;
}

void int getViewTypeCount() {
    return 2; // normal item and separator
}

void View getView(int position, View convertView, ViewGroup parent) {
    int type = getItemViewType(position);

    // phase 1: see my explanation 
    if (convertView == null) {
        if (type == 0) {
            // setup your common item view - inflate it and set to convertView
        } else {
            // setup separator view - inflate it and set to convertView
        }
    }

    // phase 2: see my explanation 
    if (type == 0) {
        // set the state of the common item view based on the position
        // rely on the fact that convertView contains the view hierarchy
        // you created in convertView == null && type == 0
    } else {
        // set state of the separator based on the position
        // rely on the fact that convertView contains the view hierarchy
        // you created in convertView == null && type != 0 (else part)
    }

    return convertView;
}

问题的实际答案...

我知道问题出在哪里,但现在想不出一个优雅的解决方案......

您的问题是您在创建 View 时使用 viewHolder.checkbox.setOnCheckedChangeListener 设置了一次单击监听器。因此,当您滚动并且单击行为适用于错误的列表项时,它会被回收/重用于项目。

尽量不要使用外部最终位置对位置进行硬编码。尝试在 return 之前设置 viewHolder.checkbox.setTag(position) 然后使用 (Integer) buttonView.getTag() 而不是 position+ 1。所以你的回收 View 将保持实际位置。

当您单击复选框时,您应该将状态保留在其他地方。不要依赖 UI 状态(因为它会被回收)。所以在 return 之前调用 viewHolder.checkbox.setChecked(persistedState)

我希望这是有道理的,你明白了...... ;-)

关于android - 通过 ListView 检查动态生成的复选框时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7738527/

有关android - 通过 ListView 检查动态生成的复选框时出现问题的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  3. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  4. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  5. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  6. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  7. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  8. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  9. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  10. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

随机推荐