ListView subobject clickable confilct
After asking a question and spending 15 days to solve this, i am looking for help and solution here again. In MainActivity i have created Json Downloading Task which is downloading data from http and with CustomListAdapter.class i populate the listview. Everything works. Now, in the listview i have 2 textview's which i want to be clickable, one of them is "Accept", that textview is just in xml it's not populated with Adapter or Json. "Accept" should work like this "Change the text to Accepted and change color" and its working like everything else. BUT when i click on first "Accept"(Position 0) in listview it changes other listview items (Position 4,9). It's like i clicked textviews on Position 4,9. On first image is before clicking the "Accept" and second on is afer clicking.
///
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;
}
You need to understand how listview recycle mechanism works
How ListView's recycling mechanism works
Use a Model Class. Assume you already have the below
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;
}
}
In getView
holder.accept.setText(listData.get(position).getContent());
holder.accept.setTag(position);
holder.accept.setOnClickListener(mClickListener);
Then
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();
}
};
Exaplanation :
You use a model class which has getters and setters.
You setTag
to the button with position. In onClick you get the tag ie position and change the content accordingly. You refresh listview by calling notifyDataSetChanged
on the adapter.
For the benefit of others here's a example
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);
}
}
Model class 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;
}
}
CustomListAdapter
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_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="40dp"
android:text="Button" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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_width="match_parent"
android:layout_height="match_parent"
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_width="match_parent"
android:layout_height="wrap_content"
>
</ListView>
</RelativeLayout>
Snap
Button at row 1 and 5 is clicked so it is changed to Accepted and is Blue.
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;
}
}