In my LinearLayout, there's a variable number of CheckBoxes. In a question I had a month ago someone said it´s better to add checkboxes dynamicly instead of make them not visible.
Integer[] count = new Integer[]{1,2,3,4,5,6,7,8,9,10};
size = mFrageList.get(position).getAuswahlList().size();
for (int i = 0; i < size; i++) {
cBox = new CheckBox(this);
cBox.setText(mFrageList.get(position).getAuswahlList().get(i));
cBox.setId(count[i]);
cBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
antwortencode[position] += "" + buttonView.getId();
frageBeantworten.setText("Antwort :"+antwortencode[position]+" abgeben");
} else {
String id = Integer.toString(buttonView.getId());
antwortencode[position] = antwortencode[position].replaceAll(id,"");
if(!antwortencode[position].isEmpty() || antwortencode[position]!= "") {
frageBeantworten.setText("Antwort :" + antwortencode[position] + " abgeben");
} else {
frageBeantworten.setText("Keine Checkbox(en) gewählt");
}
}
}
});
antworten.addView(cBox);
Currently, I'm able to save a string with the checked checkboxes, if I un-check a checkbox, it deletes it's value out of the string.
If I update the activity, the string is saved, and the checkboxes get a new List from the mFrageList.get(position)getAuswahlList(); and fill a new string in the "antwortencode" List with their values.
If I go back to the last position, I have the string which was generated but the checkboxes aren't checked anymore. But they have the Strings from the old position. that means everything is saved except the state of the checkboxes. I cant set a cBox.setChecked(isChecked) or buttonView.setChecked(isChecked) or buttonView.setChecked(buttonView.isChecked()) or something which is nearly the same in syntax.
I don't know what I can do besides declaring 10 Checkboxes in a xml file to talk to them one by one and set the VISIBLE.false if the auswahlList.get(position).isEmpty().
IMPORTANT: My XML is a Scrollable Activity because the size of the content overextended the screen. Thats why i didn´t and can´t use a Listview. So i need a solution that uses a LinearLayout
The truth is, you should actually use a ListView. As long as you reuse a layout multiple times - do it.
There are 2 options:
ListView as root - add other contents of your layout as different types of view
ListView inside a scrollable layout - there are many lightweight implementations of ListView that allow it to wrap content, e.g. https://github.com/paolorotolo/ExpandableHeightListView
The other thing is how to maintain the state of Checkboxes - use model classes. It's extremely easy with a ListView as it forces you to use an Adapter which provides methods to iterate over all positions.
Example of an adapter:
public class CheckableItemAdapter extends BaseAdapter {
private List<Pair<Integer, Boolean>> items = new ArrayList<>();
public void setItems(List<Pair<Integer, Boolean>> items) {
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder holder;
if (convertView == null) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_checkable, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
} else {
view = convertView;
holder = (ViewHolder) view.getTag();
}
Pair<Integer, Boolean> item = items.get(position);
holder.itemCheck.setChecked(item.second);
return view;
}
static class ViewHolder {
CheckBox itemCheck;
public ViewHolder(View itemView) {
itemCheck = (CheckBox) itemView.findViewById(R.id.check);
}
}
}
I´ve managed to solve my problem alone, and now i want to share it, even if it isn´t the best example of programming.
Integer[] count = new Integer[]{1,2,3,4,5,6,7,8,9,10}; //maximum of 10 Checkboxes
size = mFrageList.get(position).getAuswahlList().size();
for (int i = 0; i < size; i++) {
cBox = new CheckBox(this);
cBox.setText(mFrageList.get(position).getAuswahlList().get(i));
cBox.setId(count[i]);
try{ //this is where the magic happens
if(antwortencode[position] != ""){ //cause i won´t want null in my db i´ve set "" as standard string in my activity for the List<String>
String code = antwortencode[position];
char[] c = code.toCharArray();
for(int j=0;j<=c.length;j++){
int x = c[j] -'0'; // 'char 1' - 'char 0' = Integer 1 , lol
if(cBox.getId()== x){ //compare them
cBox.toggle(); //if it fits, toggle
}
}
}
} catch (Exception e){
e.printStackTrace();
} //and here it ends
cBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
antwortencode[position] += "" + buttonView.getId();
frageBeantworten.setText("Antwort :"+antwortencode[position]+" abgeben");
} else {
String id = Integer.toString(buttonView.getId());
antwortencode[position] = antwortencode[position].replaceAll(id,"");
if(!antwortencode[position].isEmpty() || antwortencode[position]!= "") {
frageBeantworten.setText("Antwort :" + antwortencode[position] + " abgeben");
} else {
frageBeantworten.setText("Keine Checkbox(en) gewählt");
}
}
}
});
antworten.addView(cBox);
Ty for the answers and for the correction of my question.
Nostramärus
Related
First of all sorry for the language. I have a RecyclerView with items. I have a checkbox in each item. I have onCheckedChangeListener inside RecyclerAdapter.
When I check 4 checkboxes I need to disable the remaining. So how can I get an access for them? I can do it when on create Recycler by checking how many items are selected. But can't find how to get access for every checkbox in onCheckedChange method. Screenshot example
public class EditAtributesAdapter extends RecyclerView.Adapter<EditAtributesAdapter.EditAtributesViewHolder> {
private ArrayList<AtributeEditItem> mEditAtributeList;
DBHelper dbHelper;
public static class EditAtributesViewHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public CheckBox mCheckBox;
public EditAtributesViewHolder(View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.edit_atribute_icon);
mCheckBox = itemView.findViewById(R.id.enabledAtribute);
}
}
public EditAtributesAdapter(ArrayList<AtributeEditItem> editAtributeList){
mEditAtributeList = editAtributeList;
}
#Override
public EditAtributesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.atribute_edit_item, parent, false);
final Context forClickCTX = parent.getContext();
final EditAtributesViewHolder eavh = new EditAtributesViewHolder(v);
eavh.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int pos = eavh.getAdapterPosition();
// System.out.println("Hello, you clicked: " + mEditAtributeList.get(pos).getName());
dbHelper = new DBHelper(forClickCTX);
try {
dbHelper.createDataBase();}
catch (IOException ioe) {
throw new Error("Не удалось создать базу данных");}
try {
dbHelper.openDataBase();}
catch (SQLException sqle) {
throw sqle;}
int countSelected = 0;
if (countSelected == 0){
Cursor cursor = null;
try {
SQLiteDatabase db = dbHelper.getReadableDatabase();
cursor = db.query(DBHelper.TABLE_TASKS, null, "id = " + mEditAtributeList.get(pos).getTask_id(), null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
String ids = cursor.getString(cursor.getColumnIndexOrThrow(DBHelper.TASK_ATRIBUTE_ID));
String[] parts = ids.split(",");
int[] ints = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
ints[i] = Integer.parseInt(parts[i]);
}
countSelected = ints.length;
}
} finally {
if (cursor != null)
cursor.close();
}
if(countSelected>=5){
if(!eavh.mCheckBox.isChecked())
eavh.mCheckBox.setEnabled(false);
else
eavh.mCheckBox.setEnabled(true);
}
else
eavh.mCheckBox.setEnabled(true);
}
//here is saving new status to db ( a lot of code)
});
return eavh;
}
#Override
public void onBindViewHolder(EditAtributesViewHolder holder, int position) {
AtributeEditItem currentItem = mEditAtributeList.get(position);
holder.mImageView.setImageResource(currentItem.getImage());
holder.mCheckBox.setText(currentItem.getName());
holder.mCheckBox.setChecked(currentItem.isSelected());
int countSelected = 0;
for (int i = 0 ; i < mEditAtributeList.size() ; i++){
if(mEditAtributeList.get(i).isSelected())
countSelected++;
}
if(countSelected>=5){
if(!holder.mCheckBox.isChecked())
holder.mCheckBox.setEnabled(false);
else
holder.mCheckBox.setEnabled(true);
}
else
holder.mCheckBox.setEnabled(true);
}
#Override
public int getItemCount() {
return mEditAtributeList.size();
}
}
Your adapter and viewholder are actually doing too much, but for a small list you can probably get away with it. Calling the sqldatabse like this onclick can be a bit awkward, i am not sure, but i think you are blocking the main thread with this which means you may notice that onclick slightly freezes your app.
But the simplest way disable the items in your recyclerview is to update the items in your
private ArrayList<AtributeEditItem> mEditAtributeList
and call
notifyDataSetChanged()
on the adapter, as you can then derive the state based on the data
UPDATE: I tried to create 3 different layouts, one with a green card, other with red, and other yellow. It still not working. All color remain the same. :(
I have a RFID device. When I read tags, it would populate a listview with cardviews which has the tag's code.
I have just 3 rules: If the tag read exists in a text file loaded into the device, the card will become green. If the tag read does not exists in the text file, it becomes yellow. And if I don't read a tag which code exists in the text file (in other words, if the tag is missing) the cardview will become red.
Everything is working. The RFID reader, the app, ok.
But I simply cannot change the colors correctly.
Does anyone know how to do it? Looks simple, but I really can't figure it out.
I did a switch case, I tried if/else, but nothing worked. It simply changes all cards colors to the same one.
It does not changing the color individually.
Actually, the information inside the cardviews are correct! But I cannot understand why the colors does not change individually. All cards become with the same color, does not matter if they were read or not.
This is my adapter class. Please ignore the commented lines, they were all my tries...
public class NewAdapter extends BaseAdapter {
private Context context;
private List<PropsCard> cardProps1;
//private RecyclerView.Recycler<PropsCard> cardProps2;
public NewAdapter(Context context, List<PropsCard> cardProps) {
this.context = context;
this.cardProps1 = cardProps;
}
#Override
public int getCount() {
return cardProps1.size();
}
#Override
public Object getItem(int position) {
return cardProps1.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
PropsCard cardProps = cardProps1.get(position);
if (convertView == null) {
for(com.example.compexrf.PropsCard card: cardProps1) {
switch (card.cor) {
case 0:
//Red
//cd.setBackgroundColor(Color.parseColor("#F4BABA"));
//cdview_red.setCardBackgroundColor(Color.RED);
//bt.setBackgroundColor(Color.RED);
//CardView cdview_red = (CardView) convertView.findViewById(R.id.cdviewred);
//convertView.setBackgroundColor(Color.parseColor("#F4BABA"));
//convertView = View.inflate(context, R.layout.card_itens, null);
convertView = LayoutInflater.from(context).inflate(R.layout.card_itens, null);
//convertView.setBackgroundColor(Color.RED);
break;
case 1:
//Yellow
//cd.setBackgroundColor(Color.parseColor("#FCECA4"));
//cdview_yellow.setBackgroundColor(Color.YELLOW);
//CardView cdview_yellow = (CardView) convertView.findViewById(R.id.cdviewyellow);
//convertView.setBackgroundColor(Color.parseColor("#FCECA4"));
//convertView = View.inflate(context, R.layout.card_itens2, null);
convertView = LayoutInflater.from(context).inflate(R.layout.card_itens2, null);
//convertView.setBackgroundColor(Color.YELLOW);
break;
case 2:
//Green
//cd.setBackgroundColor(Color.parseColor("#5FDDC1"));
//cdview_green.setBackgroundColor(Color.GREEN);
//CardView cdview_green = (CardView) convertView.findViewById(R.id.cdviewgreen);
//convertView.setBackgroundColor(Color.parseColor("#5FDDC1"));
//convertView = View.inflate(context, R.layout.card_itens3, null);
convertView = LayoutInflater.from(context).inflate(R.layout.card_itens3, null);
//convertView.setBackgroundColor(Color.GREEN);
break;
default:
break;
}
}
}
TextView desc_txt = (TextView) convertView.findViewById(R.id.descricao);
TextView cod_txt = (TextView) convertView.findViewById(R.id.codigoRFID);
ImageView imageView = (ImageView) convertView.findViewById(R.id.image);
//RelativeLayout relativeLayout = (RelativeLayout) convertView.findViewById(R.id.relative);
CardView cd = (CardView) convertView.findViewById(R.id.cdviewred);
CardView cd2 = (CardView) convertView.findViewById(R.id.cdviewyellow);
CardView cd3 = (CardView) convertView.findViewById(R.id.cdviewgreen);
//Button bt = (Button) convertView.findViewById(R.id.botao);
desc_txt.setText(cardProps.desc);
cod_txt.setText(cardProps.id);
if (cardProps.desc.contains("Controlador")) {
imageView.setImageResource(R.drawable.quadro);
//relativeLayout.setBackgroundColor(Color.parseColor("F4BABA"));
}
else if (cardProps.desc.contains("Quadro")) {
imageView.setImageResource(R.drawable.quadro);
//relativeLayout.setBackgroundColor(Color.parseColor("FCECA4"));
}
else if (cardProps.desc.contains("Quadro")) {
imageView.setImageResource(R.drawable.quadro);
}
else if (cardProps.desc.contains("Câmera de Validação Esteira")) {
imageView.setImageResource(R.drawable.quadro);
}
else if (cardProps.desc.contains("Medicamentos")) {
imageView.setImageResource(R.drawable.med);
//relativeLayout.setBackgroundColor(Color.parseColor("5FDDC1"));
}
else if (cardProps.desc.contains("Impressora")) {
imageView.setImageResource(R.drawable.printer01);
}
else {
imageView.setImageResource(R.drawable.cpx);
}
return convertView;
}
This is another class to help to what I need:
public class PropsCard implements Comparable<PropsCard> {
String id;
String desc;
int cor;
public PropsCard(String id, String desc, int cor){
this.id = id;
this.desc = desc;
this.cor = cor;
}
public PropsCard(String id, int cor){
this.id = id;
this.desc = "";
this.cor = cor;
}
#Override
public boolean equals (Object object){
if(object != null && object instanceof PropsCard){
PropsCard obj = (PropsCard) object;
return id.equals(obj.id);
}
return false;
}
#Override
public int compareTo(PropsCard cdProp){
if(cor > cdProp.cor)
return 1;
else if(cor == cdProp.cor)
return 0;
else
return -1;
}
}
Ans this is my method of RFID reading:
public void onScanCompleted(String code, String rssi, int type) {
PropsCard rdCard = new PropsCard(code, 0);
if(!cardList.contains(rdCard)){
rdCard.cor = 1;
cardList.add(rdCard);
}
else {
int idx = cardList.indexOf(rdCard);
rdCard = cardList.get(idx);
if(rdCard.cor == 0) {
rdCard.cor = 2;
cardList.set(idx, rdCard);
}
}
if(!ScanAndUhf.getHasData())
makeCards(cardList);
}
}
To change CardView background colour, you should use card.setCardBackgroundColor(color); instead of cd.setBackgroundColor(Color.parseColor("#FCECA4"));
I would recommend you to use a custom adapter and use a drawable.
You can see something like that in this Example
Although it changes on click, you just have to implement your logic, but it's going to be something like this:
public void onBindViewHolder(MyViewHolder holder, int position) {
if(youritem==somevalue)
holder.view.setBackgroundColor(Color.GREEN);
else if(youritem==anothervalue)
holder.view.setBackgroundColor(Color.parseColor("#somecolor"));
}
I use something like this to change text colors all the time:
#Override
public void onBindViewHolder(OSCardAdapter.MyViewHolder holder, int position) {
OSActivity OS = OSListFiltered.get(position);
if(holder.status.getText().toString().equals("CLOSED"))
{
holder.status.setTextColor(Color.RED);
}
}
You can use it to change the text color according to your background changes.
I'm trying to implement a listview that has fast scrolling with header previews. It looks like it's almost working correct, but I'm encountering some weird, bug-like behaviour. When I scroll down without using the fast scroll bar, the fast scroll bar disappears, and reappears only almost at the end. So there seems to be a gap or something like that.
My ListView's ArrayAdapter implements SectionIndexer and it's methods getSections(), getPositionForSection(int sectionIndex) and getSectionForPosition(int position). The getPositionForSection method is in my belief, the one causing trouble. When I log the value given by sectionIndex and I scroll down the list, this value exceeds the length of the actual sections (which is 20). This value comes from the SectionIndexer, not from myself. The Android refererence states:
If the section's starting position is outside of the adapter bounds,
the position must be clipped to fall within the size of the adapter.
But when I clip the value to either 0 or section_size -1 (=19), the weird behaviour keeps appearing. Below is my ListView's ArrayAdapter implementing SectionIndexer. One note: the updateSections method is called from outside the adapter when the data changes in an AsyncTask. I hope someone knows what the problem is! Thanks in advance.
public class SoortArrayAdapter extends ArrayAdapter<Soort> implements SectionIndexer {
List<Soort> data;
private HashMap<String, Integer> alphaIndexer;
private ArrayList<String> sections;
public SoortArrayAdapter(#NonNull Context context, int resource, int textViewResourceId, List<Soort> data) {
super(context, resource, textViewResourceId, data);
this.data = data;
sections = new ArrayList<>();
alphaIndexer = new HashMap<String, Integer>();
}
private void updateSections() {
alphaIndexer.clear();
sections = new ArrayList<String>();
for (int i = 0; i < data.size(); i++) {
String s = data.get(i).getNaam().substring(0, 1).toUpperCase();
if (!alphaIndexer.containsKey(s)) {
alphaIndexer.put(s, i);
sections.add(s);
}
}
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(android.R.layout.simple_list_item_1, parent, false);
}
TextView textView = convertView.findViewById(android.R.id.text1);
textView.setText(data.get(position).getNaam());
return convertView;
}
#Override
public Object[] getSections() {
return sections.toArray(new String[0]);
}
#Override
public int getPositionForSection(int sectionIndex) {
System.out.println(sectionIndex);
if (sectionIndex >= sections.size()) {
return 0;
}
System.out.println("position for section=" + sections.get(sectionIndex));
return alphaIndexer.get(sections.get(sectionIndex));
}
#Override
public int getSectionForPosition(int position) {
String section = data.get(position).getNaam().substring(0, 1).toUpperCase();
System.out.println("section for position=" + section);
return alphaIndexer.get(section);
}
}
Looks like I misread the documentation for the above stated methods getSectionForPosition() and getPositionForSection(), especially for getSectionForPosition(). This made getPositionForSection() act strangely. I corrected my implementation, so the final implementation of the two methods is as follows:
#Override
public int getPositionForSection(int sectionIndex) {
if (sectionIndex >= sections.size()) {
return data.size() - 1;
}
if (sectionIndex < 0) {
return 0;
}
return alphaIndexer.get(sections.get(sectionIndex));
}
#Override
public int getSectionForPosition(int position) {
String section = data.get(position).getNaam().substring(0, 1).toUpperCase();
for (int i = 0; i < sections.size(); i++) {
if (section.equals(sections.get(i))) {
return i;
}
}
return 0;
}
I have a custom adapter that visualize each row in the list of Orders.
public class OrderRowAdapter extends BaseAdapter implements OnClickListener {
OrderList items_;
LayoutInflater inflater_;
int list_view_resource_id_;
private final String TAG = "OrderRowAdapter";
public OrderRowAdapter(Context context, int list_view_resource_id,
OrderList items) {
this.list_view_resource_id_ = list_view_resource_id;
this.items_ = items;
this.inflater_ = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public Object getItem(int position) {
return items_.getOrders(position);
}
public View getView(int position, View convertView, ViewGroup parent) {
Log.d(TAG, "View updated for item in position = " + position);
View v = convertView;
if (v == null) {
v = inflater_.inflate(list_view_resource_id_, parent);
}
Order item = items_.getOrders(position);
if (item != null) {
TextView order_info_tv = (TextView) v.findViewById(R.id.order_info);
TextView order_status_tv = (TextView) v.findViewById(R.id.order_status);
if (order_info_tv != null) {
order_info_tv.setText(
String.format("For customer: %s\nTotal of %d items", item.getCustomerId(), item.getItemsCount()));
}
if (order_status_tv != null) {
order_status_tv.setText("Status: " + getStatusText(item.getStatus()));
}
}
return v;
}
public int getCount() {
if (items_ == null) {
Log.d(TAG, "Null so get count returned 0");
return 0;
} else {
Log.d(TAG, "Get count returned " + items_.getOrdersCount());
return items_.getOrdersCount();
}
};
After querying new list of orders from a web service, I want to update the content of the ListView, so I have my Activity does the updating before calling notifyDataSetChanged()
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.orders);
initThreading();
findViews();
setUrls();
// Load the list of order from disk
try {
order_list_ = OrderList.parseFrom(new FileInputStream(
"/sdcard/orderList.bin"));
} catch (FileNotFoundException e) {
Log.e(TAG, "Cannot find the file", e);
} catch (IOException e) {
Log.e(TAG, "Cannot read the file", e);
}
order_row_adapter_ = new OrderRowAdapter(OrderActivity.this,
R.layout.order_row, order_list_);
orders_listview_.setAdapter(order_row_adapter_);
// Request new updates from the server
updateOrderInformation(-1);
}
public void updateOrders(InputStream new_order_stream) {
Log.d(TAG, "Updating order UI");
try {
order_list_.parseFrom(new_order_stream);
} catch (IOException e) {
Log.e(TAG, "IOException" , e);
}
runOnUiThread(new Runnable() {
public void run() {
guiUpdateOrders();
}
});
}
private void guiUpdateOrders() {
order_row_adapter_.notifyDataSetChanged();
Log.d(TAG, "Dataset notified that it has changed. GUI update anytime now.");
}
But, the getView() method of OrderRowAdapter is never called. The ListView is never updated.
It turns out the problem with my getView() not being called is because it is not visible. My layout xml has the upper TextView with fill_parent for its height. Thus the entire view only has that single TextView visible.
Solution: check the graphical view of the layout in question to make sure the ListView is visible.
Make sure BaseAdapter methods
registerDataSetObserver(DataSetObserver observer)
unregisterDataSetObserver(DataSetObserver observer)
are not overridden.
To change the content of your ListView, you must keep using the same reference to the List. Here you're creating another list and assigning it to the items_ variable (which does not contain the list itself, it's just a place to store a reference to a List), but your View still has a reference to the old list.
Instead of items_ = new_order_list this should work :
items_.clear();
items_.addAll(new_order_list);
EDIT :
To explain it better, try to create a new variable named old_items :
public void setNewOrderList(List<Order> new_order_list)
{
Log.d(TAG, "New Order List available. Num items = " + new_order_list.size());
List<Order> old_items = items_; // store the reference to the old list
items_ = new_order_list;
Log.d(TAG, "items_.size() = " + items_.size());
Log.d(TAG, "old_items.size() = " + old_items.size()); // The old list still exists, and it's the one used by your ListView
notifyDataSetChanged();
}
If all above answers not working try with invalidateViews()
ListView.invalidateViews() is used to tell the ListView to invalidate all its child item views (redraw them).
Note that there not need to be an equal number of views than items. That's because a ListView recycles its item views and moves them around the screen in a smart way while you scroll.
listView.invalidateViews()
My sample implementation,
lvitems.post(new Runnable() {
#Override
public void run() {
lvitems.invalidateViews(); //invalidate old
CustomAdapter customAdapter=new CustomAdapter(context, names, images); // load new data
customAdapter.notifyDataSetChanged();// call notifydatasetChanged
}
});
If any mistake in this answer please correct my mistakes.
I'm developing for Android and I have an app (called Forget-Me-Not) that uses a ListView as the Tasks screen (Its a ToDo list app). And I have set the items to be grayed out when they are completed.
When the list is bigger than the screen, scrolling occur as usual. But the problem is when scrolled upwards/downwards and come back again, the uncompleted items too have been grayed out. I have checked this many times and the graying-out seems to be random. The items are clickable and they are functioning as expected (i.e. long-clicking, etc). The only problem is the graying-out. (This confuses the users as this app is ToDo list managing app and graying-out is "completing" a task).
I don't know what code to post, so if anyone can tell me what code I should post or could give an answer straightaway, I would be really thankful.
PS: I've got a custom ListAdapter class. It mainly reads the items from SQLite database and sets them to the adapter. You can see the bug in my app- its in the Android Market (Forget-Me-Not).
EDIT:
Here is my custom TaskAdapter class:
public class TasksAdapter extends BaseAdapter {
private String[] items;
private int[] priorities;
private Vector<String> completed;
private Context context;
public TasksAdapter(TasksActivity context, int textViewResourceId, String[] items) {
if(items != null)
this.items = items;
else
this.items = new String[0];
this.priorities = new int[this.items.length];
for(int i = 0; i < this.priorities.length; i++) {
this.priorities[i] = FMN.PRIORITY_LOW;
}
this.completed = new Vector<String>(0);
this.context = context;
}
public TasksAdapter(TasksActivity context, int textViewResourceId, String[] items, int[] priorities, Vector<String> completed) {
if(items != null)
this.items = items;
else
this.items = new String[0];
if(priorities != null)
this.priorities = priorities;
else {
this.priorities = new int[this.items.length];
for(int i = 0; i < this.priorities.length; i++) {
this.priorities[i] = FMN.PRIORITY_LOW;
}
}
if(completed != null)
this.completed = completed;
else
this.completed = new Vector<String>(0);
this.context = context;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.task_item, null);
}
TextView task = (TextView) v.findViewById(R.id.tv_task);
task.setText(items[position]);
ImageView prio = (ImageView) v.findViewById(R.id.im_task_priority);
switch(priorities[position]) {
case FMN.PRIORITY_HIGH:
prio.setImageResource(R.drawable.fmn_priority_high);
break;
case FMN.PRIORITY_MEDIUM:
prio.setImageResource(R.drawable.fmn_priority_low);
break;
case FMN.PRIORITY_LOW:
prio.setImageResource(R.drawable.fmn_priority_medium);
break;
default:
prio.setImageResource(R.drawable.fmn_priority_medium);
break;
}
if((completed.size() != 0) && (completed.contains(task.getText()))) {
task.setTextColor(Color.rgb(155, 175, 155));
}
return v;
}
public int getCount() {
return items.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
}
And a screenshot BEFORE scrolling:
And AFTER scrolling:
It is probably a bug in your ListAdapter getView. Do you recycle your list objects/items? The properties for colors and so on will remain on the object when recycled. Make sure you set the properties correct depending on in which state the task is in.
In the getView function you probably check if the view-item allready is set and then you just change the text for the task on the item (recycling)? At the same place in the code change the color settings for background to the correct task state.
EDIT
Now that I see your code it looks like you have got the priorities mixed up, default and priority low set the background to prio.setImageResource(R.drawable.fmn_priority_medium);
and medium sets the background to prio.setImageResource(R.drawable.fmn_priority_low);
Ok I found the problem:
if((completed.size() != 0) && (completed.contains(task.getText()))) {
task.setTextColor(Color.rgb(155, 175, 155));
}
You need an else statement here to set the text color to the original color if it is not completed. Like:
if((completed.size() != 0) && (completed.contains(task.getText()))) {
task.setTextColor(Color.rgb(155, 175, 155));
} else {
task.setTextColor(Color.rgb(0, 0, 0));
}