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中加入Button后,Button的点击事件和ListView的点击事件冲突
Android学习笔记之ListView与Item的焦点冲突处理