Remember selected ListView Items - java

I'm new to android and I have following problem
I have a ListView. The ListView is filled with data from an ArrayList. I added a CheckBox to every row.
After clicking on a "Resume-Button" I want to write the selected items to a SQLliteDB (the db works fine - I tested it with static data).
My question is now:
How can I get the checked items from the list?
Thx everyone for help!
Best regards
kyp

You can use this ListView method getCheckedItemPositions() to get all the checked positions. Make sure that the child view in your ListView actually implements the Checkable interface otherwise the checkmark information will not be passed along to the ListView.

You can go through the list, using a for loop and getItemAtPosition(int).
For each row you just check if the item is checked, isChecked() for a checkbox. Without seeing the code that is as specific as I can get. If this is not clear enough then post some code about how/what items are stored in the listView
Hope this helps!

But i figured it out myself.
I only wanted to select the row by clicking on the checkBox view.
I modified my custom Array Adapter
Here is my code:
public class ContactAdapter extends ArrayAdapter<Contact> implements OnClickListener{
private static HashMap<Integer, Boolean> teamItems = new HashMap<Integer, Boolean>();
int count = 0;
private List<Contact> items;
private int mode;
CheckBox add_to_team;
static int counter = 3;
public ContactAdapter(Context context, int textViewResourceId, List<Contact> objects, int mode) {
super(context, textViewResourceId, objects);
// TODO Auto-generated constructor stub
this.context = context;
this.mode = mode;
this.items = objects;
}
#Override
public View getView(int position, View convertView, final ViewGroup parent) {
View v = convertView;
if(v == null) {
//get a reference to the LayoutInflator
LayoutInflater li = (LayoutInflater)getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//and inflate our XML into the View
v = li.inflate(R.layout.list_contacts_item, null);
}
String race = items.get(position).race;
String nick = items.get(position).nick;
String name = items.get(position).name;
final int pos = position;
if(v!= null) {
// Do the work for the other views
// MODE 1 = SELECTION-MODE
if(mode == 1) {
add_to_team.setVisibility(0);
try {
if(count!=0) {
boolean b = teamItems.get(pos);
if(b==false) {
add_to_team.setChecked(false);
}
else {
add_to_team.setChecked(true);
}
}
} catch (NullPointerException e) {
}
add_to_team.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
teamItems.put(pos, arg1);
count++;
}
});
} else {
add_to_team.setVisibility(8);
}
}
return v;
}
}
I added a HashMap in which I save the selected items. I can call the select state from my activity, by calling the HashMap... And it works great!

Related

RecyclerView Perform Item Click

I have a RecyclerView that contains expandable items. clicking on an item expands it. The problem is it also expand some other cards, unexpectedly. I checked everything and I couldn't find why is this happening, but I did manage to find out that the clicked item always somehow has the same id as the other expanded item. The error occurs only when the list is big enough, so I think it has something to do with the RecyclerViews functionality. Also using notifyDataSetChanged() works, but it eliminates the animations, and I want the layout to be animated...
this question looks to discuss the same problem I'm facing... but yet I don't know how to solve it.
I couldn't understand why is this happening or how to fix this... below are some images and code to help you understand better, and maybe see if the problem is in the code...
this is the RecyclerView:
An expanded card item looks like this:
Here's my Adapters class:
public class ActiveGoalsAdapter extends RecyclerView.Adapter<ActiveGoalsAdapter.ActiveGoalsViewHolder> {
private Context context;
private Cursor cursor;
private ArrayList<Goal> activeGoals;
private static boolean[] openedFromParent = new boolean[]{false, true}, editing = new boolean[]{false};
public ActiveGoalsAdapter(Context context, ArrayList<Goal> activeGoals, Cursor cursor) {
this.context = context;
this.activeGoals = activeGoals;
this.cursor = cursor;
}
public class ActiveGoalsViewHolder extends RecyclerView.ViewHolder {
public LinearLayout shrunkContainer, subGoalsTitleContainer;
public RelativeLayout expandedContainer, subGoalsRecyclerViewContainer, btnDelete, btnCancel, btnSave;
public ConstraintLayout editPanel;
public CustomProgressBar shrunkProgressBar, expandedProgressBar;
public ImageButton btnExpandShrink, btnEdit, btnBackToParent;
public TextView title, description;
public RecyclerView subGoalsRecyclerView;
public ExtendedEditText nameET, descriptionET;
public ActiveGoalsViewHolder(#NonNull View itemView) {
super(itemView);
shrunkContainer = itemView.findViewById(R.id.shrunk_active_goal_container);
expandedContainer = itemView.findViewById(R.id.expanded_active_goal_container);
editPanel = itemView.findViewById(R.id.edit_panel);
btnExpandShrink = itemView.findViewById(R.id.active_goal_expand_shrink_btn);
btnEdit = itemView.findViewById(R.id.active_goal_edit_btn);
btnBackToParent = itemView.findViewById(R.id.active_goal_back_to_parent_btn);
shrunkProgressBar = itemView.findViewById(R.id.shrunk_active_goal_progress_bar);
shrunkProgressBar.enableDefaultGradient(true);
title = itemView.findViewById(R.id.expanded_active_goal_title);
expandedProgressBar = itemView.findViewById(R.id.expanded_active_goal_progress_bar);
expandedProgressBar.enableDefaultGradient(true);
description = itemView.findViewById(R.id.expanded_active_goal_description);
subGoalsTitleContainer = itemView.findViewById(R.id.expanded_active_goal_sub_goals_title_container);
subGoalsRecyclerViewContainer = itemView.findViewById(R.id.expanded_active_goal_sub_goals_container);
subGoalsRecyclerView = itemView.findViewById(R.id.expanded_active_goal_sub_goals_recyclerview);
nameET = itemView.findViewById(R.id.expanded_active_goal_edit_name_edit_text);
descriptionET = itemView.findViewById(R.id.expanded_active_goal_edit_description_edit_text);
btnDelete = itemView.findViewById(R.id.edit_delete_button);
btnCancel = itemView.findViewById(R.id.edit_cancel_button);
btnSave = itemView.findViewById(R.id.edit_save_button);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (expandedContainer.getVisibility() == View.VISIBLE) {
shrink();
} else {
expand();
}
}
});
}
private void expand(){
TransitionManager.beginDelayedTransition((ViewGroup) itemView.getRootView(), new AutoTransition());
expandedContainer.setVisibility(View.VISIBLE);
shrunkProgressBar.setVisibility(View.INVISIBLE);
}
private void shrink(){
TransitionManager.beginDelayedTransition((ViewGroup) itemView.getRootView(), new AutoTransition());
expandedContainer.setVisibility(View.GONE);
shrunkProgressBar.setVisibility(View.VISIBLE);
}
}
#NonNull
#Override
public ActiveGoalsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.active_goal_card, parent, false);
return new ActiveGoalsViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ActiveGoalsViewHolder holder, int position) {
if (activeGoals.get(position) == null) {
return;
}
GoalDBHelper db = new GoalDBHelper(context);
Goal currentGoal = activeGoals.get(position);
Cursor subGoalsCursor = db.getSubGoalsCursorOf(currentGoal);
ArrayList<Goal> subGoalsArrayList = db.getSubGoalsArrayListOf(currentGoal);
String name = currentGoal.getName(),
description = currentGoal.getDescription(),
parent = currentGoal.getParentGoal();
int timeCounted = currentGoal.getTimeCounted(),
timeEstimated = currentGoal.getTimeEstimated();
for (Goal subGoal : activeGoals) {
if (subGoal.getParentGoal().equals(name)) {
subGoalsArrayList.add(subGoal);
}
}
holder.shrunkProgressBar.setText(name);
holder.shrunkProgressBar.setProgress((timeCounted * 100 / timeEstimated));
holder.shrunkProgressBar.setRadius(300.0f);
holder.expandedProgressBar.setText("");
holder.expandedProgressBar.setProgress((timeCounted * 100 / timeEstimated));
holder.expandedProgressBar.setRadius(300.0f);
holder.title.setText(name);
holder.description.setText(description);
if (subGoalsArrayList.size() <= 0) {
holder.subGoalsTitleContainer.setVisibility(View.GONE);
holder.subGoalsRecyclerViewContainer.setVisibility(View.GONE);
} else {
holder.subGoalsTitleContainer.setVisibility(View.VISIBLE);
holder.subGoalsRecyclerViewContainer.setVisibility(View.VISIBLE);
initSubGoalsAdapter(holder.subGoalsRecyclerView, subGoalsArrayList, subGoalsCursor);
}
if (openedFromParent[0]) {
holder.btnBackToParent.setVisibility(View.VISIBLE);
} else {
holder.btnBackToParent.setVisibility(View.GONE);
}
}
public void initSubGoalsAdapter(RecyclerView subGoalsRecyclerView, ArrayList<Goal> subGoals, Cursor subGoalsCursor) {
GoalsAdapter adapter = new GoalsAdapter(context, subGoals, subGoalsCursor);
final CarouselLayoutManager layoutManager = new CarouselLayoutManager(CarouselLayoutManager.VERTICAL, false);
layoutManager.setPostLayoutListener((CarouselLayoutManager.PostLayoutListener) new CarouselZoomPostLayoutListener());
subGoalsRecyclerView.setLayoutManager(layoutManager);
subGoalsRecyclerView.setHasFixedSize(true);
subGoalsRecyclerView.setAdapter(adapter);
}
#Override
public int getItemCount() {
return activeGoals.size();
}
public void swapCursor(Cursor newCursor) {
if (cursor != null) {
cursor.close();
}
cursor = newCursor;
if (newCursor != null) {
notifyDataSetChanged();
}
}
}
Where is the problem? and how should I fix it?
Help would be highly appreciated
The problem is that RecyclerView reuses ViewHolders during scrolling. For example on position 10 it can uses ViewHolder from position 2 (let's imagine this item was expanded) and if you don't bind expanded / collapsed state for ViewHolder on position 10 it will have expanded state. So to solve the problem you have to track ViewHolder state and update ViewHolder every onBindViewHolder method calling.
Here is a good answer related to selection in RecyclerView and you will have almost the same logic for expanded / collapsed states.
https://stackoverflow.com/a/28838834/9169701
I'm not familiar with the utilities you're using for animation. But, you can do something like this to track and update the visibility of your views:
private ArrayList<MyData> dataList;
private ArrayList<boolean> itemStates; // Create a list to store the item states
public MyAdapter(ArrayList<MyData> myData){
dataList = myData;
itemStates = new ArrayList<>();
// Build the default state values for each position
for(MyData data: dataList){
itemStates.add(false);
}
}
#Override
public void onBindViewHolder(MyHolder holder, int position){
// Whatever you need to do on each item position ...
final boolean visible = itemStates.get(position);
// Set the visibility of whichever view you want
if(visible){
holder.myView.setVisibility(View.VISIBLE);
}else{
holder.myView.setVisibility(View.GONE);
}
// Change the visibility after clicked
holder.itemView.setOnClickListener(new View.OnClickListener(){
// Use the ViewHolder's getAdapterPosition()
// to retrieve a reliable position inside the click callback
int pos = holder.getAdapterPosition();
if(visible){
// Play the hide view animation for this position ...
}else{
// Play the show view animation for this position ...
}
// Set the new item state
itemStates.set(pos, !visible);
// Refresh the Adapter after a delay to give your animation time to play
// (I've used 500 milliseconds here)
new Handler().postDelayed(new Runnable(){
#Override
public void run(){
notifyDataSetChanged();
}
}, 500);
});
}
You can refer to my code for the solution, maybe it'll help.
final boolean isExpanded = position == currentPosition;
holder.childLayout.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
Animation slideDown = AnimationUtils.loadAnimation(context, R.anim.slide_down_animation);
holder.childLayout.startAnimation(slideDown);
if (isExpanded)
currentPosition = position;
holder.parentLayout.setOnClickListener(v -> {
currentPosition = isExpanded ? -1 : position;
notifyItemChanged(currentPosition);
notifyItemChanged(position);
});
Hope this solves your problem.
Edit:
currentPosition is a variable which is assigned to -1 and it stores the current position of the item in the recyclerview.
position is the variable of the BindViewHolder
setActivated() is a method defined for view. You can check it here.
childLayout is the layout of the view that is shown after the expansion.
parentLayout is the layout on which you click to expand.

Array.remove() wont work more than once in onClickListener()

I have a listview in one of my Activities that has a delete button inside of each row. When I click the delete button I want to remove that row value from two different arrays. The first array is the list array that shows the information in the listview and the second is for my sharedPreference array which is the data populating the listview. This works fine the first time I run it but if I try deleting more than one row without recreating the view it won't work.
Here is my code:
deleteBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//this will delete the row from the list view
list.remove(position);
//this deletes the value from the sharedPreference
favoritesList.remove(position);
notifyDataSetChange();
}
}
This works the fine the first time I click the delete button in a row but if I want to delete another row, I click the delete button on a different row and while the row is removed from the listview it isnt removed from my favoritesList array. Which means the favoritesList.remove(postion) is only working the first time. It's almost like favoritesList isn't being refreshed and its just trying to remove the same value no matter what row I click on. Which is weird because list.remove(position) works fine.
So how do I get favoritesList(position) to run more than once without breaking? I can post more code if need be but I figured this would be enough. Any help is appreciated. Thanks!
Here is the whole custom adapter for my listview:
public class ItemCustomAdapter extends BaseAdapter implements ListAdapter {
private ArrayList<String> list = new ArrayList<String>();
ArrayList<String> favoritesList;
private Context context;
//private final SharedPreferences sharedPrefs;
String[] favorites;
public ItemCustomAdapter(ArrayList<String> list, Context context, String[] favorites) {
this.list = list;
this.context = context;
this.favorites = favorites;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int pos) {
return list.get(pos);
}
#Override
public long getItemId(int pos) {
return 0;
//just return 0 if your list items do not have an Id variable.
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
//System.out.println(favoritesList);
favoritesList = new ArrayList<String>(Arrays.asList(favorites));
View view = convertView;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_list_view, null);
}
//Handle TextView and display string from your list
TextView listItemText = (TextView)view.findViewById(R.id.list_item_string);
listItemText.setText(list.get(position));
//Handle buttons and add onClickListeners
ImageButton deleteBtn = (ImageButton)view.findViewById(R.id.delete_btn);
deleteBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
list.remove(position);
favoritesList.remove(position);
notifyDataSetChanged();
}
});
return view;
}
}
As Opiatefuchs said above, all I had to do was move favoritesList = new ArrayList(Arrays.asList(favorites)); outside of getView().

ListView using BaseAdapter not showing in Activity

I'm trying to inflate a list using baseadapter within an activity. The list just doesn't inflate. From the logs implemented within the class, the getView() function doesn't even execute. Here's the code. -
public class CallLog extends Activity {
ListView logList;
List mList;
Context mCtx;
ArrayList<String> logName;
ArrayList<String> logNumber;
ArrayList<String> logTime;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.reject_call_log);
mCtx = getApplicationContext();
ListView logList = (ListView) findViewById(R.id.log_list);
mList = new List(mCtx, R.layout.log_row);
logList.setAdapter(mList);
SharedPreferences savedLogName = PreferenceManager.getDefaultSharedPreferences(mCtx);
SharedPreferences savedLogNumber = PreferenceManager.getDefaultSharedPreferences(mCtx);
SharedPreferences savedLogTime = PreferenceManager.getDefaultSharedPreferences(mCtx);
try{
logName = new ArrayList(Arrays.asList(TextUtils.split(savedLogName.getString("logName", null), ",")));
logNumber = new ArrayList(Arrays.asList(TextUtils.split(savedLogNumber.getString("logNumber", null), ",")));
logTime = new ArrayList(Arrays.asList(TextUtils.split(savedLogTime.getString("logTime", null), ",")));
Collections.reverse(logName);
Collections.reverse(logNumber);
Collections.reverse(logTime);
}catch(NullPointerException e){
e.printStackTrace();
//TextView noLog = (TextView)findViewById(R.id.no_log);
}
}
public class List extends BaseAdapter {
LayoutInflater mInflater;
TextView nameText;
TextView numberText;
TextView timeText;
int timePos = 1;
public List(Context context, int resource) {
}
#Override
public int getCount() {
return 0;
}
#Override
public Object getItem(int i) {
return null;
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (convertView == null) {
v = mInflater.inflate(R.layout.row, null);
}
nameText = (TextView) v.findViewById(R.id.log_name);
numberText = (TextView) v.findViewById(R.id.log_number);
timeText = (TextView) v.findViewById(R.id.log_time);
nameText.setText(logName.get(position));
numberText.setText(logNumber.get(position));
timeText.setText(logTime.get(timePos) + logTime.get(timePos+1));
Log.d("RejectCall", "ListView");
timePos+=2;
return v;
}
}
}
Where is it all going wrong? Also, is there a better way to do what I'm trying to do?
Please replace the following code :
#Override
public int getCount() {
return 0;
}
with
#Override
public int getCount() {
return logName.size();
}
As list view only show the numbers of rows that is returned by this method and right now you are returning 0;
And after fetching the data in arraylist please use adapter.notifyDataSetChanged() to notify the list view.
You have to call notifyDataSetChanged() as you are filling data in array list after setting the adapter. so to notify the list view that data has been changed you have to call notify method(as above)
Your getItem() and getCount() haven't been implemented. If you want any kind of adapter to work for the list, these need to be implemented. Your list is also not holding any actual data, so getItem() has nothing to set.
Don't forget to call notifiyDataSetChanged() in your adapter after you set appropriate implementations for the above two functions.

what am I missing with `notifyDataSetChanged`?

I have an activity that draws some icons on the view.
It sets a list and listAdapter in onCreate().
I have extracted to public method the code that assign the list to my adapter.
So that external code, can call the UI thread and assign new list to the adapter and make it notify the change via notifyDataSetChange()
However the new icons are not drawn, but only after leaving and getting back to the Activity.
How can I fix this?
I have tried adapter.clear()
and doubled checked the UI thread runs this code.
what else?
public class CategoriesActivity extends ActivityBase {
private Category[] categories;
SettingValueAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_values);
((TitleBar)findViewById(R.id.theTitleBar)).init(this,DisplayStrings.DS_CATEGORIES);
adapter = new SettingValueAdapter(this);
DriveToNativeManager nativeManager = DriveToNativeManager.getInstance();
nativeManager.getCategories(new CategoriesListener() {
#Override
public void onComplete(Category[] aCategories) {
categories = aCategories;
refreshListIcons();
}
});
final ListView list = (ListView)findViewById(R.id.settingsValueList);
list.setAdapter(adapter);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
..
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
..
}
public void refreshListIcons() {
NativeManager nativeManager = AppService.getNativeManager();
SettingsValue[] values = new SettingsValue[categories.length];
for (int i = 0; i < categories.length; i++) {
values[i] = new SettingsValue(categories[i].value, nativeManager.getLanguageString(categories[i].displayString), false);
values[i].icon = ResManager.GetSkinDrawable(categories[i].iconName + ".bin");
}
adapter.setValues(values);
}
}
public class SettingValueAdapter extends BaseAdapter {
private SettingsValue[] values;
private LayoutInflater inflater;
public SettingValueAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.settings_item, null);
}
SettingsValue item = values[position];
CheckedTextView name = (CheckedTextView) convertView.findViewById(R.id.itemText);
ImageView iconView = (ImageView) convertView.findViewById(R.id.itemIcon);
if (iconView != null && (item != null) && (item.icon != null)) {
iconView.setImageDrawable(item.icon);
iconView.setVisibility(View.VISIBLE);
} else {
iconView.setVisibility(View.GONE);
}
name.setText(item.display);
name.setChecked(item.isSelected);
View container = convertView.findViewById(R.id.itemContainer);
if (position == 0) {
..
return convertView;
}
public void setValues(SettingsValue[] values) {
this.values = values;
notifyDataSetChanged();
}
}
You should extend ArrayAdapter instead of BaseAdapter:
http://developer.android.com/reference/android/widget/ArrayAdapter.html
This class is suitable for Arrays and Collections and it handles correctly all concerns regarding internal state.
In this case use clear followed by addAll at Adapter object itself before call notifyDataSetChanged. Another aproach is to change the Array or Collection bound to your Adapter.
I've found a very well done tutorial here:
http://www.vogella.com/articles/AndroidListView/article.html
Hope it helps !! Best regards !!
A dirty hack to do this, in case you are having too much problems getting the new notification and in case you dont have a significant amount of data in the adapter, you can always create a new adapter and assign it to the list list.setAdapter(adapter); forcing the list to render it over again from scratch...
Regards!
I don't think you can update the memory location of the list powering your adapter during runtime. This isn't allowed:
this.values = values;
If you know that the number of items won't change, use this piece of code:
public void setValues(SettingsValue[] values) {
for (int i = 0; i < values.length; i++) {
this.values[i] = values[i];
}
notifyDataSetChanged();
}
Otherwise you will need to create a new adapter every time you call setValues and update your listview by pointing to the new adapter. I'll try to link to the article where I read this when I find it. Hope this works.

Switch listener in ListView giving wrong results

Here is my Array adapter. It assigns each switch button in the list with a listener that is dependant on the id given by the database (passed using devices.id). But for some reason, the switch listener is getting the correct states from the switches.
For debugging purpose, I'm just using the same on change listener regardless of the state. So whether I turn it ON or OFF, it runs the same function.
My problem is sometimes when I turn ON 1st switch, 7th switch is turned ON, n when I turn ON the 2nd switch, 8th switch is turned ON. There's something wrong I'm doing, but I cant really figure out. The 6th, 7th and 8th switches don't work properly.
You can see in the LCD, When I turn ON the switches in order, it must give me the output "12345678". But its not. Instead it gives "12345112".
Edit: Answer given by Emil Adz works. Gives me output "12345678". But I have one problem. If I turn ON switch1, scroll down till its not visible and scroll up, the switch resets to OFF. Why is it so?
public class DevListAdapter extends ArrayAdapter<Devices>{
Context context;
int layoutResourceId;
Devices data[] = null;
private TextView txt = null;
public DevListAdapter(Context context, int layoutResourceId, Devices[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("WCAM","GetView");
View row = convertView;
DeviceHolder holder = null;
Devices devices = data[position];
String id = devices.id;
Log.d("WCAM","Inflator");
if(row == null)
{
Log.d("WCAM","True");
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new DeviceHolder();
holder.devTxt = (TextView)row.findViewById(R.id.devdesc);
holder.nameTxt = (TextView)row.findViewById(R.id.devname);
holder.toggleSwitch = (Switch)row.findViewById(R.id.toggleswitch);
holder.txt = (TextView) row.findViewById(R.id.debug);
final int i;
if(id.matches("1")) {
i = 0x31;
}
else if(id.matches("2")) {
i = 0x32;
}
else if(id.matches("3")) {
i = 0x33;
}
else if(id.matches("4")) {
i = 0x34;
}
else if(id.matches("5")) {
i = 0x35;
}
else if(id.matches("6")) {
i = 0x36;
}
else if(id.matches("7")) {
i = 0x37;
}
else {
i = 0x38;
}
holder.toggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Control._service.write(i);
}
});
row.setTag(holder);
}
else
{
Log.d("WCAM","False");
holder = (DeviceHolder)row.getTag();
}
holder.devTxt.setText(devices.dev);
holder.nameTxt.setText(devices.name);
holder.txt.setText(id);
return row;
}
static class DeviceHolder
{
TextView devTxt;
TextView txt;
TextView nameTxt;
Switch toggleSwitch;
}
}
devices is an object of Devices.java
public class Devices {
public String dev;
public String name;
public String id;
public Devices(){
super();
}
public Devices(String _id, String dev, String name) {
super();
this.dev = dev;
this.name = name;
this.id = _id;
}
}
Try it like this:
public class DevListAdapter extends ArrayAdapter<Devices>{
Context context;
int layoutResourceId;
Devices data[] = null;
private TextView txt = null;
public DevListAdapter(Context context, int layoutResourceId, Devices[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("WCAM","GetView");
DeviceHolder holder = null;
Devices devices = data[position];
String id = devices.id;
Log.d("WCAM","Inflator");
Log.d("WCAM","True");
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new DeviceHolder();
holder.devTxt = (TextView)row.findViewById(R.id.devdesc);
holder.nameTxt = (TextView)row.findViewById(R.id.devname);
holder.toggleSwitch = (Switch)row.findViewById(R.id.toggleswitch);
holder.txt = (TextView) row.findViewById(R.id.debug);
final int i;
if(id.matches("1")) {
i = 0x31;
}
else if(id.matches("2")) {
i = 0x32;
}
else if(id.matches("3")) {
i = 0x33;
}
else if(id.matches("4")) {
i = 0x34;
}
else if(id.matches("5")) {
i = 0x35;
}
else if(id.matches("6")) {
i = 0x36;
}
else if(id.matches("7")) {
i = 0x37;
}
else {
i = 0x38;
}
holder.toggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Control._service.write(i);
}
});
holder.devTxt.setText(devices.dev);
holder.nameTxt.setText(devices.name);
holder.txt.setText(id);
return row;
}
Basically for some reason I encountered this problem several times. When you use convertView then all the list is going insane. Be aware of the fact that it derives performance but I didn't find a proper solution for this problem. What more when the list is not that big it doesn't change much.
UPDATE:
First take a look at this guide:
Multi-clickable ListView
As with the check box state there you will see:
//setting data into the the ViewHolder.
holder.title.setText(RowData.getName());
holder.checked.setChecked(RowData.isChecked());
//return the row view.
return convertView;
So in your case after this code:
holder.devTxt.setText(devices.dev);
holder.nameTxt.setText(devices.name);
holder.txt.setText(id);
you should add something like:
holder.toggleSwitch.setChecked(devices.isChecked());
and in your onCheckedChanged persist the state changes.
I had the same kind of problem with my switch view. After a lot of search and reading, I figure out that this problem is result of a sort of optimization of the views promoted by Android. It try to reuse the views objects. That really works, but if you do not pay attention, or just don't know the behavior (like me), some pretty weird results happens.
Anyway, I find this simple solution:
A store my views in a ViewHolder and programmed the onCheckedChanged event, just like you. The trick is, before set if the switch is checked or not and define the CheckedChange listener, I simply reset the callback with null, this ensures that the event of another switch isn't triggered.
Here is the snippet:
...
viewHolder.switchView.setOnCheckedChangeListener(null);
viewHolder.switchView.setChecked(myObject.getStatus() > 0);
...
viewHolder.switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
...
}
});
myObject is a final object that has the Cursor data stored.
And regardless of the switch will be checked or not, you have to call the setChecked method.
Hope it helps someone!
Your ListView is currently associating the state of a switch with the position of a row, rather than with the actual content of a row. When the user scrolls through your ListView, and it's contents are recycled, it's putting in switch values based on position.
To fix this, you need to save the state of a switch in such a way that it is associated with an actual row. Android: Problem With ListViews and CheckBoxes deals with the exact same issue, but using checkboxes. It is extremely useful for resolving these sorts of problems

Categories