This is a simple listview example
public class Test extends ListActivity {
ArrayList<String> txt;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
txt=new ArrayList<String>();
txt.add("diaplay text 1");
txt.add("diaplay text 2");
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, txt));
}
}
But this can only store string
I want do something like this
<ul>
<li data-meta="hidden text 1">display text 1</li>
<li data-meta="hidden text 2">display text 2</li>
</ul>
Because I want store more information in each list item
So I want store this class into listview
public class Item
{
public String displayText;
public String meta;
}
How can I do this?
You have to implement a custom Adapter for that. First we have to fix your view model, you call it Item:
public class Item
{
public String displayText;
public String meta;
}
Those fields should be private with appropriate getters and setters and constructors. If you want to modify such Items in a List you also need to implement equals() and hashCode(). If you do all that your Item class should look something like this:
public class Item {
private String displayText;
private String meta;
public Item(String displayText, String meta) {
this.displayText = displayText;
this.meta = meta;
}
public Item() {
}
public String getDisplayText() {
return displayText;
}
public void setDisplayText(String displayText) {
this.displayText = displayText;
}
public String getMeta() {
return meta;
}
public void setMeta(String meta) {
this.meta = meta;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
if (displayText != null ? !displayText.equals(item.displayText) : item.displayText != null) return false;
return !(meta != null ? !meta.equals(item.meta) : item.meta != null);
}
#Override
public int hashCode() {
int result = displayText != null ? displayText.hashCode() : 0;
result = 31 * result + (meta != null ? meta.hashCode() : 0);
return result;
}
}
Now we need to create a layout for the Items in the ListView, for example something simple like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rlRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:clickable="true"
android:background="#drawable/list_item_background">
<TextView
style="#style/DefaultTextView"
android:id="#+id/tvDisplayText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true" />
<TextView
style="#style/DefaultTextView"
android:id="#+id/tvMeta"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#id/tvDisplayText"
android:layout_toStartOf="#id/tvDisplayText"
android:layout_centerVertical="true" />
</RelativeLayout>
In this layout we have two TextViews to display both the Strings from each Item. To increase ListView performance you should always implement the view holder pattern. For this purpose we create a ViewHolder class. Its purpose is to hold a reference to the relevant Views in each row of the ListView so we don't have to perform the expensive findViewById() as often:
public class ViewHolder {
public TextView tvDisplayText;
public TextView tvMeta;
}
Note that we don't need getters and setters or anything in this view holder. We will access the public fields directly.
Now we can implement our custom Adapter. This is actually pretty straight forward, I will comment the most important parts.
public class ExampleAdapter extends BaseAdapter {
private final LayoutInflater inflater;
private final List<Item> items;
private ExampleAdapter(Context context, List<Item> items) {
this.inflater = LayoutInflater.from(context);
this.items = items;
}
#Override
public int getCount() {
return this.items.size();
}
#Override
public Item getItem(int position) {
return this.items.get(position);
}
#Override
public long getItemId(int position) {
return this.items.get(position).hashCode();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Item item = getItem(position);
if(convertView == null) {
// If convertView is null we have to inflate a new layout
convertView = this.inflater.inflate(R.layout.example_list_item, parent, false);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.tvDisplayText = (TextView) convertView.findViewById(R.id.tvDisplayText);
viewHolder.tvMeta = (TextView) convertView.findViewById(R.id.tvMeta);
// We set the view holder as tag of the convertView so we can access the view holder later on.
convertView.setTag(viewHolder);
}
// Retrieve the view holder from the convertView
final ViewHolder viewHolder = (ViewHolder) convertView.getTag();
// Bind the values to the views
viewHolder.tvDisplayText.setText(item.getDisplayText());
viewHolder.tvMeta.setText(item.getMeta());
return convertView;
}
}
And you would use this custom Adapter like this:
final List<Item> items = new ArrayList<Item>();
items.add(new Item("a", "b"));
items.add(new Item("c", "d"));
items.add(new Item("e", "f"));
items.add(new Item("g", "h"));
final ExampleAdapter adapter = new ExampleAdapter(context, items);
listView.setAdapter(adapter);
You have implementation BaseAdapter, ArrayApdater or somethings.
In your adapter you pass a list of custom objects.
Here a sample code:
public class CustomListAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Item> items;
public CustomListAdapter(Activity activity, List<Item> items) {
this.activity = activity;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int location) {
return items.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
convertView = inflater.inflate(R.layout.yourlayout, null);
Item item = items.get(position);
//Setter text
return convertView;
}
}
Tutorial:
BaseAdapter
Official Documentation
To store custom data in a ListView adapter, you must implement your own. Take a look at Custom BaseAdapter with ListView.
I don't know clearly understand your question. But i understand that. Hope for help.
strings.xml
<string-array name="li_items">
<item >Home</item>
<item >Find People</item>
<item >Photos</item>
<item >Communities</item>
<item >Pages</item>
</string-array>
In your ListActivity
public class Test extends ListActivity {
ArrayList<String> txt;
private String[] liItem;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
liItem= getResources().getStringArray(R.array.li_items);
txt=new ArrayList<String>();
txt.add(liItem[0]);
txt.add(liItem[1]);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, txt));
}
}
Related
I'm trying to implement button on each row in ListView, but I saw many topics and I don't succeeded to add code to mine. Here is my MainActivity :
public class MainActivity extends AppCompatActivity {
private ArrayAdapter<String> itemsAdapter;
private ArrayList<String> items;
private ImageButton formButton;
private ListView lvMain;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
commonFunction();
}
public void commonFunction() {
lvMain = (ListView) findViewById(R.id.lvMain);
items = new ArrayList<String>();
readItems();
itemsAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
items);
lvMain.setAdapter(itemsAdapter);
formButton = (ImageButton) findViewById(R.id.btnPlus);
formButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setLayoutActivity();
}
});
}
}
Here is my activity_main.xml :
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/lvMain"
android:layout_above="#+id/btnPlus" />
<ImageButton
android:layout_width="match_parent"
android:layout_height="65dp"
android:id="#+id/btnPlus"
android:layout_alignParentBottom="true"
app:srcCompat="#mipmap/ic_plus_foreground" />
Does someone any idea how to do ?
Create custom adapter with custom row layout file and add button on that row file and bind it to List/Recycler view. So it will inflate in all row.
Add below code in row_list.xml file.
<ImageButton
android:layout_width="match_parent"
android:layout_height="65dp"
android:id="#+id/btnPlus"
android:layout_alignParentBottom="true"
app:srcCompat="#mipmap/ic_plus_foreground" />
CustomAdapter.java
public class CustomAdapter extends BaseAdapter {
private ArrayList data;
private static LayoutInflater inflater = null;
/************* CustomAdapter Constructor *****************/
public CustomAdapter(ArrayList d) {
/********** Take passed values **********/
data = d;
/*********** Layout inflater to call external xml layout () ***********/
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/******** What is the size of Passed Arraylist Size ************/
public int getCount() {
if (data.size() <= 0)
return 1;
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
/********* Create a holder Class to contain inflated xml file elements *********/
public static class ViewHolder {
public ImageButton button;
}
/****** Depends upon data size called for each row , Create each ListView row *****/
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ViewHolder holder;
if (convertView == null) {
/****** Inflate tabitem.xml file for each row ( Defined below ) *******/
vi = inflater.inflate(R.layout.row_list, null);
/****** View Holder Object to contain tabitem.xml file elements ******/
holder = new ViewHolder();
holder.button = (ImageView) vi.findViewById(R.id.btnPlus);
/************ Set holder with LayoutInflater ************/
vi.setTag(holder);
} else
holder = (ViewHolder) vi.getTag();
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Button click
}
});
return vi;
}}
Hope it will solve problem.
Happy coding!!
Crate custom adapter and bind it with the ListView.
Inflate custom layout for each row in the adapter.
You can put your button in the custom layout file.
public class CustomAdapter extends ArrayAdapter<String> {
private Context context;
private int singleRowLayoutId;
public CustomAdapter(Context context, int singleRowLayoutId, String []titles, String []desc, int []images ) {
super(context, singleRowLayoutId,titles);
this.context=context;
this.singleRowLayoutId=singleRowLayoutId; //custom row layout for list view item
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View row=convertView;
CustomViewHolder holder=null;
if(row==null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//view object of single row of list view
row = inflater.inflate(singleRowLayoutId, parent, false);
//by using holder, we make sure that findViewById() is not called every time.
holder=new CustomViewHolder(row);
row.setTag(holder);
}
else
{
holder=(CustomViewHolder)row.getTag();
}
return row;
}
private class CustomViewHolder {
private ImageButton mbtn;
CustomViewHolder(View view)
{
mbtn=(ImageButton)view.findViewById(R.id.btn);
mbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//handle button click here
}
});
}
}
I think that this is what you need - create a separate layout for the rows in the list view. Then create a custom adapter, where you are adding this layout as row layout and then set this adapter to your list view. Here are the details with examples:
Android custom Row Item for ListView
I have a list of a POJO in an Android app and I am currently displaying one of the fields in a listview/listitem like so:
List<NotificationItem> notifItems;
// snip, populate
ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.notification_item, notifItems);
ListView listView = (ListView) findViewById(R.id.notification_listview);
listView.setAdapter(adapter);
And it is my understanding that the listivew or adapter uses the toString of the POJO, which is
public String toString() {
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd hh:mm");
return _notificationTitle + " | " + dateFormatter.format(_notificationReceivedDate);
}
R.id.notificationitem is
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/label"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip"
android:textSize="16sp"
android:textStyle="bold"></TextView>
So far so good, but what I want to do is add elements to the notificationitem layout file and then update the Java code to populate the new fields.
How is this done? I don't really understand how the adapter knows/puts the toString value into the one field in the notificationitem.
public class NotificationItem {
//your fields here
}
//-----------------------
public class NotifAdapter extends BaseAdapter {
Context context;
List<NotificationItem> data;
public NotifAdapter(Context context, List<NotificationItem> data) {
this.context = context;
this.data = data;
}
#Override
public int getCount() {
return data.size();
}
#Override
public Object getItem(int position) {
return data.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.your_row_layout,parent, null);
TextView tx = convertView.findViewById(R.id.your_widget_inside_row_layout);
tx.setText(data.yourDataPropery);
return convertView;
}
}
// In your activity
NotifAdapter adapter = new NotifAdapter(this, ArrayList<NotificationItem>);
listView.setAdapter(adapter);
I'm trying to set up a 2-line list view but each string seems to copy itself within the item rather than showing it once. How can I prevent this from happening? i.e. I need the data to appear like this: for Item 1 - America, America Description rather than America, America; for Item 2 - Europe, Europe Description rather than Europe, Europe. See screenshot for evidence of the undesired result.
ListData.java
public class ListData {
public static final int[][] items = {
{R.string.america,R.string.america_description},
{R.string.europe, R.string.europe_description}
};
}
ListViewAdapter
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<String> mData;
private List<String> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<String> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String strItem = mFilteredData.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.mTitle = (TextView) convertView.findViewById(R.id.item_title);
holder.mDescription = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mTitle.setText(strItem);
holder.mDescription.setText(strItem);
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView mTitle;
private TextView mDescription;
}
}
item_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingRight="?android:attr/scrollbarSize"
android:baselineAligned="false">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView android:id="#+id/item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:ellipsize="marquee"
android:textColor="?android:attr/textColorPrimary" />
<TextView android:id="#+id/item_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/item_title"
android:layout_alignLeft="#id/item_title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" />
</RelativeLayout>
</LinearLayout>
ItemListadapter.java
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<String> mData;
private List<String> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<String> data, Context context) {
mData = data;
mFilteredData = data;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mFilteredData.size();
}
#Override
public String getItem(int position) {
return mFilteredData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String strItem = mFilteredData.get(position);
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.mTitle = (TextView) convertView.findViewById(R.id.item_title);
holder.mDescription = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.mTitle.setText(strItem);
holder.mDescription.setText(strItem);
return convertView;
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView mTitle;
private TextView mDescription;
}
/**
* Filter for filtering list items
*/
private class ItemFilter extends Filter {
/**
* Invoked on a background thread. This is where all the filter logic should go
* #param constraint the constraint to filter on
* #return the resulting list after applying the constraint
*/
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<String> resultList = new ArrayList<>();
for (String str : mData) {
if (str.toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* #param constraint the constraint used for the result
* #param results the results to display
*/
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<String>)results.values;
notifyDataSetChanged();
}
}
}
}
The name is shown two times in ListView because you are passing in both TextView the same item:
String strItem = mFilteredData.get(position);
To avoid this and use two separate fields you need to pass to your adapter a model with two field:
public class CustomListItem {
private String title;
private String description;
//get method
public CustomListItem(String title, String description){
this.title = title;
this.description = description;
}
}
Then you have to add them to create a new List<CustomListItem> in your activity/fragment that use the adapter and pass to the adapter this list:
public class ItemListAdapter extends ArrayAdapter<CustomListItem>{
private Context context;
private List<CustomListItem> data;
public ItemListAdapter(Context context, List<CustomListItem> data){
this.context = context;
this.data = data;
}
}
I am having some trouble learning the ins and outs of the SQLite world. I have some code that is allowing me to enter data into a DB. But what i want to do is return this data into a listview. At the moment all I could figure out to do was to have each row printed in a toast after a new entry is added. Can someone please show me how to alter my code to print it in a listview? Or to even look at my code and see that i am going about it in the right way. Thanks
This is the code i am using which calls a display record function
//---get all Records---
com.example.rory.dbtest.DBAdapter db = new com.example.rory.dbtest.DBAdapter(this);
db.open();
Cursor c = db.getAllRecords();
if (c.moveToFirst())
{
do {
DisplayRecord(c);
} while (c.moveToNext());
}
db.close();
This is the display record function
public void DisplayRecord(Cursor c)
{
Toast.makeText(this,
"id: " + c.getString(0) + "\n" +
"Item: " + c.getString(1) + "\n" +
"Litres: " + c.getString(2),
Toast.LENGTH_SHORT).show();
}
I know i need to change the second function but i dont know how to do that to make it print into a listview
this is the code of getting data from database and insert into Arraylist and insert into arrayAdapter and than display it in listview .
i just done some editing in your existing code.
com.example.rory.dbtest.DBAdapter db = new com.example.rory.dbtest.DBAdapter(this);
db.open();
ArrayList<String> data_list=new ArrayList<String>();
ListView lv=(ListView)findViewById(R.id.listView1);
Cursor c = db.getAllRecords();
if (c.moveToFirst())
{
do {
data_list.add(c.getString(0));
DisplayRecord(c);
} while (c.moveToNext());
}
ArrayAdapter<String> aa=new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, data_list);
lv.setAdapter(aa);
lv - is the object of ListView.
Create a ListView. Then provide cursorAdapter to the ListView as it's adapter to bind the data in the database to the ListView.
There are examples in the samples folder of the SDK you downloaded in the project called ApiDemos.
You need to have, 1) listview, 2) Object class, 3) Custom Adapter
Here I have just tried to implement as per your requirement.
Since I dont have db I did not try to run. The point to is to five you idea.
Because listview is widget that we use more frequently in android. This is the best approach as per my knowledge.
Layouts that required,
activity_list.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=".ListActivity" >
<ListView
android:id="#+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
</RelativeLayout>
row_item.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"
android:padding="10dp" >
<TextView
android:id="#+id/Item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Item" />
<TextView
android:id="#+id/Litres"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="litres" />
</LinearLayout>
ListActivity.java :
public class ListActivity extends Activity {
ArrayList<RowData> rowDataArrayList = new ArrayList<RowData>();
ListView list;
ListAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
list = (ListView) findViewById(R.id.list);
listAdapter = new ListAdapter(ListActivity.this, rowDataArrayList);
list.setAdapter(listAdapter);
getDataFromDB();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.list, menu);
return true;
}
private void getDataFromDB() {
rowDataArrayList.clear();
com.example.rory.dbtest.DBAdapter db = new com.example.rory.dbtest.DBAdapter(
this);
db.open();
Cursor c = db.getAllRecords();
if (c.moveToFirst()) {
do {
/**
* Set your data in class
*/
RowData rowData = new RowData();
rowData.setId(c.getString(0));
rowData.setItem(c.getString(1));
rowData.setLitres(c.getString(2));
rowDataArrayList.add(rowData);
} while (c.moveToNext());
}
db.close();
/**
* To reflect new data set change in listview
*/
listAdapter.notifyDataSetChanged();
}
}
RowData.java : Model[pojo] class to save data and to bind in custom adapter.
public class RowData {
String Id;
String Item;
String Litres;
public String getId() {
return Id;
}
public void setId(String id) {
Id = id;
}
public String getItem() {
return Item;
}
public void setItem(String item) {
Item = item;
}
public String getLitres() {
return Litres;
}
public void setLitres(String litres) {
this.Litres = litres;
}
}
ListAdapter.java : custom adapter to bind in listview
public class ListAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<RowData> rowDataArrayList = new ArrayList<RowData>();
public ListAdapter(Context context, ArrayList<RowData> rowData) {
mContext = context;
rowDataArrayList = rowData;
}
#Override
public int getCount() {
return rowDataArrayList == null ? 0 : rowDataArrayList.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
Holder holder = new Holder();
view = View.inflate(mContext, R.layout.row_item, null);
holder.tvtItem = (TextView) view.findViewById(R.id.Item);
holder.tvtLitres = (TextView) view.findViewById(R.id.Litres);
view.setTag(holder);
} else {
view = convertView;
}
Holder holder = (Holder) view.getTag();
holder.tvtItem.setText(rowDataArrayList.get(position).getItem());
holder.tvtLitres.setText(rowDataArrayList.get(position).getLitres());
return view;
}
class Holder {
TextView tvtItem;
TextView tvtLitres;
}
}
public class ListAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<RowData> rowDataArrayList = new ArrayList<RowData>();
public ListAdapter(Context context, ArrayList<RowData> rowData) {
mContext = context;
rowDataArrayList = rowData;
}
#Override
public int getCount() {
return rowDataArrayList == null ? 0 : rowDataArrayList.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
Holder holder = new Holder();
view = View.inflate(mContext, R.layout.row_item, null);
holder.tvtItem = (TextView) view.findViewById(R.id.Item);
holder.tvtLitres = (TextView) view.findViewById(R.id.Litres);
view.setTag(holder);
} else {
view = convertView;
}
Holder holder = (Holder) view.getTag();
holder.tvtItem.setText(rowDataArrayList.get(position).getItem());
holder.tvtLitres.setText(rowDataArrayList.get(position).getLitres());
return view;
}
class Holder {
TextView tvtItem;
TextView tvtLitres;
}
}
I'm trying to make a task manager, and I only have one problem. I have a listview that gets inflated. All the elements in the listview are correct. The problem is that when I select an item, the listview will select another item away. I've heard listviews repopulate the list as it scrolls down to save memory. I think this may be some sort of problem. Here is a picture of the problem.
If i had more apps loaded, then it would continue to select multiple at once.
Here is the code of my adapter and activity and XML associated
public class TaskAdapter extends BaseAdapter{
private Context mContext;
private List<TaskInfo> mListAppInfo;
private PackageManager mPack;
public TaskAdapter(Context c, List<TaskInfo> list, PackageManager pack) {
mContext = c;
mListAppInfo = list;
mPack = pack;
}
#Override
public int getCount() {
return mListAppInfo.size();
}
#Override
public Object getItem(int position) {
return mListAppInfo.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
TaskInfo entry = mListAppInfo.get(position);
if (convertView == null)
{
LayoutInflater inflater = LayoutInflater.from(mContext);
//System.out.println("Setting LayoutInflater in TaskAdapter " +mContext +" " +R.layout.taskinfo +" " +R.id.tmbox);
convertView = inflater.inflate(R.layout.taskinfo,null);
}
ImageView ivIcon = (ImageView)convertView.findViewById(R.id.tmImage);
ivIcon.setImageDrawable(entry.getIcon());
TextView tvName = (TextView)convertView.findViewById(R.id.tmbox);
tvName.setText(entry.getName());
convertView.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
final CheckBox checkBox = (CheckBox)v.findViewById(R.id.tmbox);
if(v.isSelected())
{
System.out.println("Listview not selected ");
//CK.get(arg2).setChecked(false);
checkBox.setChecked(false);
v.setSelected(false);
}
else
{
System.out.println("Listview selected ");
//CK.get(arg2).setChecked(true);
checkBox.setChecked(true);
v.setSelected(true);
}
}
});
return convertView;
public class TaskManager extends Activity implements Runnable
{
private ProgressDialog pd;
private TextView ram;
private String s;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.taskpage);
setTitleColor(Color.YELLOW);
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run()
{
//System.out.println("In Taskmanager Run() Thread");
final PackageManager pm = getPackageManager();
final ListView box = (ListView) findViewById(R.id.cBoxSpace);
final List<TaskInfo> CK = populate(box, pm);
runOnUiThread(new Runnable()
{
#Override
public void run()
{
ram.setText(s);
box.setAdapter(new TaskAdapter(TaskManager.this, CK, pm));
//System.out.println("In Taskmanager runnable Run()");
endChecked(CK);
}
});
handler.sendEmptyMessage(0);
}
Taskinfo.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal">
<ImageView
android:id="#+id/tmImage"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="centerCrop"
android:adjustViewBounds="false"
android:focusable="false" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tmbox"
android:lines="2"/>
</LinearLayout>
Taskpage.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="#+id/cBoxSpace"
android:layout_width="wrap_content"
android:layout_height="400dp"
android:orientation="vertical"/>
<TextView
android:id="#+id/RAM"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
<Button
android:id="#+id/endButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="End Selected Tasks" />
</LinearLayout>
Any ideas for what reason mutliple items are selected with a single click would be GREATLY appreciated. I've been messing around with different implementations and listeners and listadapters but to no avail.
I think the point is you only save checking state in the view(v.setSelected).
And you reuse these view, so its checkbox is always not change its state.
You can create a state array to save every checking state of every TaskInfo, and check this array when you create a view.
for example
// default is false
ArrayList<Boolean> checkingStates = new ArrayList<Boolean>(mListAppInfo.size());
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
TaskInfo entry = mListAppInfo.get(position);
if (convertView == null)
{
LayoutInflater inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(R.layout.taskinfo,null);
}
ImageView ivIcon = (ImageView)convertView.findViewById(R.id.tmImage);
ivIcon.setImageDrawable(entry.getIcon());
TextView tvName = (TextView)convertView.findViewById(R.id.tmbox);
tvName.setText(entry.getName());
final CheckBox checkBox = (CheckBox)v.findViewById(R.id.tmbox);
checkBox.setChecked(checkingStates.get(position));
convertView.setSelected(checkingStates.get(position));
convertView.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
if(v.isSelected())
{
System.out.println("Listview not selected ");
//CK.get(arg2).setChecked(false);
checkBox.setChecked(false);
v.setSelected(false);
checkingStates.get(position) = false;
}
else
{
System.out.println("Listview selected ");
//CK.get(arg2).setChecked(true);
checkBox.setChecked(true);
v.setSelected(true);
checkingStates.get(position) = true;
}
}
});
return convertView;
}
I'm not 100% sure what you are trying to do, but part of your problem might be related to the condition in your onClick method:
if(v.isSelected())
I think you want that to read
if(v.isChecked())
isSelected is inherited from View, and it means something different from isChecked
Also, the whether the CheckBox is checked or not is independent from your data model since it is a recycled view. Your CheckBox should be checked based on entry (I'm assuming your TextInfo class has an isChecked() method that returns a boolean:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
TaskInfo entry = mListAppInfo.get(position);
if (convertView == null)
{
LayoutInflater inflater = LayoutInflater.from(mContext);
//System.out.println("Setting LayoutInflater in TaskAdapter " +mContext +" " +R.layout.taskinfo +" " +R.id.tmbox);
convertView = inflater.inflate(R.layout.taskinfo,null);
}
ImageView ivIcon = (ImageView)convertView.findViewById(R.id.tmImage);
ivIcon.setImageDrawable(entry.getIcon());
TextView tvName = (TextView)convertView.findViewById(R.id.tmbox);
tvName.setText(entry.getName());
CheckBox checkBox = (CheckBox)v.findViewById(R.id.tmbox);
checkBox.setChecked(entry.isChecked());
}
I don't think you need the View.OnClickListener you are attaching to convertView. You should handle that in the OnItemClickListener attached to the ListView. Assuming your ListView is called listView and TaskInfo instances have setChecked and isChecked methods:
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position, long id) {
entry = mListAppInfo.get(position);
entry.setChecked(!entry.isChecked());
}
});
First of all don't set the list checked or unchecked on view position.
because view position means only visible items position in your listview but you would like to set checked or uncheked status on a particular list item.
That's why this problem arises in your code.
You have the need to set the items checked and unchecked on your custom arraylist getter setter like the code i have attached below:
package com.app.adapter;
public class CategoryDynamicAdapter {
public static ArrayList<CategoryBean> categoryList = new ArrayList<CategoryBean>();
Context context;
Typeface typeface;
public static String videoUrl = "" ;
Handler handler;
Runnable runnable;
// constructor
public CategoryDynamicAdapter(Activity a, Context context, Bitmap [] imagelist,ArrayList<CategoryBean> list) {
this.context = context;
this.categoryList = list;
this.a = a;
}
// Baseadapter to the set the data response from web service into listview.
public BaseAdapter mEventAdapter = new BaseAdapter() {
#Override
public int getCount() {
return categoryList.size();
}
#Override
public Object getItem(int position) {
return categoryList.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
class ViewHolder {
TextView title,category,uploadedBy;
ImageView image;
RatingBar video_rating;
Button report_video ,Flag_video;
}
public View getView(final int position, View convertView, final ViewGroup parent) {
ViewHolder vh = null ;
if(convertView == null) {
vh = new ViewHolder();
convertView = LayoutInflater.from(context).inflate (R .layout.custom_category_list_layout,null,false);
vh.title = (TextView) convertView .findViewById (R.id.title);
vh.image = (ImageView) convertView.findViewById(R.id.Imagefield);
convertView.setTag(vh);
}
else
{
vh=(ViewHolder) convertView.getTag();
}
try
{
final CategoryBean Cb = categoryList.get(position);
//pay attention to code below this line i have shown here how to select a listview using arraylist getter setter objects
String checkedStatus = Cb.getCheckedStringStaus();
if(checkdStatus.equal("0")
{
System.out.println("Listview not selected ");
//CK.get(arg2).setChecked(false);
checkBox.setChecked(false);
v.setSelected(false);
}
else ////checkdStatus.equal("1")
{
System.out.println("Listview selected ");
//CK.get(arg2).setChecked(true);
checkBox.setChecked(true);
v.setSelected(true);
}
catch (Exception e)
{
e.printStackTrace();
}