ListView 子对象可点击冲突

Posted

技术标签:

【中文标题】ListView 子对象可点击冲突【英文标题】:ListView subobject clickable confilct 【发布时间】:2014-01-03 20:32:15 【问题描述】:

在提出问题并花费 15 天解决此问题后,我再次在这里寻求帮助和解决方案。 在 MainActivity 我创建了 Json 下载任务,它从 http 下载数据并使用 CustomListAdapter.class 填充列表视图。 一切正常。 现在,在列表视图中,我有 2 个文本视图,我希望它们可以点击,其中之一是“接受”,该文本视图只是在 xml 中,它没有填充适配器或 Json。 “接受”应该像这样“将文本更改为接受并更改颜色”,它的工作方式与其他所有内容一样。但是当我在列表视图中单击第一个“接受”(位置 0)时 它会更改其他列表视图项目(位置 4,9)。就像我在位置 4,9 上单击了 textviews。 第一张图片是在点击“接受”之前,第二张是点击之后。

///

 public class MainActivity extends Activity 

protected static final String TAG = null;
public ArrayList<FeedItem> feedList;
public ListView feedListView;
private ProgressBar progressbar;
 private CustomListAdapter adap;
 private LayoutInflater mInflater;


@Override 
public void onCreate(Bundle savedInstanceState)

      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main); 
      feedListView= (ListView) findViewById(R.id.custom_list);

      mInflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
      String url = "...";
      new DownloadFilesTask().execute(url);

      getActionBar().setIcon(R.drawable.angel);
      progressbar = (ProgressBar)findViewById(R.id.progressBar);


       public void updateList() 
    adap = new CustomListAdapter(this, feedList);

           feedListView.setAdapter(adap);

            


      public class DownloadFilesTask extends AsyncTask<String, Integer, Void> 


      ///....  

CustomListAdapter.class

    public class CustomListAdapter extends BaseAdapter  
 

private ArrayList<FeedItem> listData;
private LayoutInflater layoutInflater;
private Context mContext;
private ArrayList<String> data;
protected ListView feedListView;
ArrayList<HashMap<String,String>> list;

public CustomListAdapter(Context context, ArrayList<FeedItem> listData)

    this.listData = listData;
    layoutInflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mContext = context;
    data = new ArrayList<String>();
    for (int i = 0; i < 10; i++) 
        data.add("Sample Text " + String.valueOf(i));
    



@Override
public int getCount()

    return listData.size();


@Override
public Object getItem(int position)

    return listData.get(position);


@Override
public long getItemId(int position)

    return position;



public View getView( int position, View convertView, ViewGroup parent)

 final ViewHolder holder;
 View row=convertView;
    if ((row == null) || (row.getTag()==null)) 

     convertView = layoutInflater.inflate(R.layout.list_row_layout, null);
     holder = new ViewHolder();
     holder.headlineView = (TextView)convertView.findViewById(R.id.name);
     holder.reportedDateView = (TextView) convertView.findViewById(R.id.confid);
     holder.accept= (TextView) convertView.findViewById(R.id.acceptTV);

     convertView.setTag(holder);



    
    else
    
        holder = (ViewHolder) convertView.getTag();

    

   final FeedItem newsItem = (FeedItem) listData.get(position);
    holder.accept.setFocusable(true);

    holder.accept.setClickable(true);
    holder.headlineView.setText(html.fromHtml(newsItem.getTitle()));
    holder.reportedDateView.setText(Html.fromHtml(newsItem.getContent()));

    holder.accept.setOnClickListener(new View.OnClickListener() 

        @Override
        public void onClick(View arg0) 


                    holder.accept.setText(Html.fromHtml(newsItem.getContent()));
        
    );



    return convertView;





static class ViewHolder


    TextView accept;
    TextView headlineView;
    TextView reportedDateView;
    ImageView imageView;
    FeedItem newsItem;


【问题讨论】:

移除支架然后使用 只适用于“接受”文本视图还是适用于所有? @Rohit 为什么 ViewHolder 有问题? 因为我也面临这个问题,所以通过移除视图支架解决了 @Rohit 我认为这不是问题所在。也建议使用视图持有者模式 【参考方案1】:

你需要了解listview回收机制是如何工作的

How ListView's recycling mechanism works

使用模型类。假设您已经拥有以下内容

public class FeedItem 

String title,content;

public String getTitle() 
    return title;


public void setTitle(String title) 
    this.title = title;


public String getContent() 
    return content;


public void setContent(String content) 
    this.content = content;



getView

holder.accept.setText(listData.get(position).getContent()); 
holder.accept.setTag(position);
holder.accept.setOnClickListener(mClickListener);

然后

private OnClickListener mClickListener = new OnClickListener() 
public void onClick(View v) 
    int pos = (Integer) v.getTag();
    FeedItem newsItem = (FeedItem) listData.get(pos);
    newsItem.setContent("Accepted");
    CustomListadapter.this.notifyDataSetChanged();

;

解释:

您使用具有 getter 和 setter 的模型类。

setTag 到带有位置的按钮。在 onClick 中,您将获得标签,即位置并相应地更改内容。您可以通过在适配器上调用 notifyDataSetChanged 来刷新列表视图。

为了他人的利益,这里是一个例子

public class MainActivity extends Activity 

   ArrayList<Holder> list = new ArrayList<Holder>();
   ListView lv;
   CustomListAdapter cus;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.listView1);
        for(int i=0;i<10;i++)
        
            Holder h = new Holder();
            h.setTitle("Title"+i);
            h.setContent("Content"+i);
            h.setColor(Color.BLACK);
            list.add(h);
        
        cus = new CustomListAdapter(this,list);
        lv.setAdapter(cus);
    

模型类Holder

public class Holder 

    String title,content;
    int color;

    public int getColor() 
return color;

    public void setColor(int color) 
this.color = color;
    

    public String getTitle() 
        return title;
    

    public void setTitle(String title) 
        this.title = title;
    

    public String getContent() 
        return content;
    

    public void setContent(String content) 
        this.content = content;
    


自定义列表适配器

public class CustomListAdapter extends BaseAdapter

    LayoutInflater inflater;
    ArrayList<Holder> list;
    public CustomListAdapter(MainActivity mainActivity, ArrayList<Holder> list) 
        inflater = LayoutInflater.from(mainActivity);
        this.list =list;
    
    @Override
    public int getCount() 
        // TODO Auto-generated method stub
        return list.size();
    
    @Override
    public Object getItem(int position) 
        // TODO Auto-generated method stub
        return position;
    
    @Override
    public long getItemId(int position) 
        // TODO Auto-generated method stub
        return position;
    
    public View getView(int position, View convertView, ViewGroup parent)  
        ViewHolder holder; 
        if (convertView == null)  
            convertView = inflater.inflate(R.layout.list_item, 
                    parent, false);
            holder = new ViewHolder(); 
            holder.tv = (TextView) convertView.findViewById(R.id.textView1); 
            holder.b = (Button) convertView.findViewById(R.id.button1);
           convertView.setTag(holder); 
        else  
           holder = (ViewHolder) convertView.getTag(); 
        
       Holder h = list.get(position);
       holder.tv.setText(h.getTitle());
       holder.b.setText(h.getContent());
       holder.b.setTextColor(h.getColor());
       holder.b.setOnClickListener(mClickListener); 
       holder.b.setTag(position);
       return convertView; 

     private OnClickListener mClickListener = new OnClickListener() 

            public void onClick(View v) 
                int pos = (Integer) v.getTag();
                Holder h = (Holder) list.get(pos);
                h.setContent("Accepted");
                    h.setColor(Color.BLUE);
                CustomListAdapter.this.notifyDataSetChanged();

            

            ;
    static class ViewHolder
    
        TextView tv;
        Button b;
    

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_ >

    <Button
        android:id="@+id/button1"
        android:layout_
        android:layout_
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="40dp"
        android:text="Button" />

    <TextView
        android:id="@+id/textView1"
        android:layout_
        android:layout_
        android:layout_alignBottom="@+id/button1"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="22dp"
        android:text="TextView" />

</RelativeLayout>

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
    <ListView
        android:id="@+id/listView1"
        android:layout_
        android:layout_
        >
    </ListView>

</RelativeLayout>

捕捉

第 1 行和第 5 行的按钮被点击,所以它变为 Accepted 并且是蓝色的。

【讨论】:

THAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA你!!!!!!!!!!!!!!!!!!!!!!!!!!!! 15 天后……你就是天使! 感谢@Raghunandan,我的应用程序也发生了变化,并且 +1 来自我【参考方案2】:
getView(...)
if ((row == null) || (row.getTag()==null)) 
 // some code
else
 // some code

holder.accept.setTag(position);

// some more code

if(newsItem.isSelected())
holder.accept.setText("accepted");
else
holder.accept.setText("accept");


//handling click
holder.accept.setOnClickListener(new View.OnClickListener() 

    @Override
    public void onClick(View arg0) 

                int position = (Integer)arg0.getTag();
                // change backing dataset here instead.
                FeedItem m = listData.get(position);
                // declare a boolean 'selected' in FeedItem 
                // toggle the previous selection
                m.setSelected(! m.isSelected());
                // call notifydatasetChanged
                CustomListAdapter.this.notifyDataSetChanged();

    
);

// some more code



class FeedItem
// some data member

boolean selected = false;
public boolean isSelected()
return selected;


public void setSelected(boolean status)
selected = status;


【讨论】:

这是正确的。 FeetItem 是模型类。但你必须改变数据 @Raghunandan 在这里,我正在更改选择状态并且设置为真。或者,我认为切换状态会更好。 操作是为按钮设置文本,而不是切换更改按钮 settext。任何方式这也适用于更改文本 请同时添加模型类 @Rohit 这个模型类是由 OP 定义的。这是一个简单的 java 类,它会根据用户的用例有一些成员。在代码中添加模型类的简单版本。【参考方案3】:

您的问题肯定来自您在重用视图时没有重置视图状态这一事实。 ListView 重用视图以实现更好的内存管理。因此,根据项目的选定状态调整您的getView() 以调整文本和颜色。我没有看到你在代码中实际保存它的位置,所以我不能给你一个工作示例。

类似

    if (newsItem.selected)
      holder.accept.setText("accepted");
    else
      holder.accept.setText("accept");

【讨论】:

以上是关于ListView 子对象可点击冲突的主要内容,如果未能解决你的问题,请参考以下文章

Scrollerview与listview或者gridview发生冲突

关于ListView中item与子控件抢夺焦点的解决方法

ListView中加入Button后,Button的点击事件和ListView的点击事件冲突

Android学习笔记之ListView与Item的焦点冲突处理

Android学习笔记之ListView与Item的焦点冲突处理

item的Ontouch和listview的setOnItemClick冲突求解。