listview toggle switch works only last row - java

i'm facing a problem which is, when i toggle switch it's works for only last item of listview. It doesn't matter which switch i toggle. I research other asked questions but i couldn't figure it out.
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = LayoutInflater.from(getContext());
View customView = inflater.inflate(R.layout.row, parent, false);
String allData = getItem(position);
final Switch status = (Switch) customView.findViewById(R.id.switchStatus);
status.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
Toast.makeText(getContext(),"selectid",Toast.LENGTH_LONG).show();
}
});
return customView;
}
You can see my adapter activiy in here.
public class adapter_test extends ArrayAdapter<String> {
public adapter_allservicerecords(Context context, String[] data){
super(context, R.layout.row, data);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = LayoutInflater.from(getContext());
View customView = inflater.inflate(R.layout.row, parent, false);
String allData = getItem(position);
try {
if(!all_data.isEmpty()) {
JSONObject jsonRowData = new JSONObject(all_data);
try {
text.setText(jsonRowData.getString("TITLE"));
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
Log.e("FAILED", "Json parsing error: " + e.getMessage());
}
return customView;
}
}

The solution to the problem is you have to instead of making your data as a String, you should instead change it into something that would represent each row.
For example you can set this class to represent your row:
class Item {
String data;
boolean toggled;
public Item(String data) {
this.data = data;
}
public void setToggled(boolean toggle) {
toggled = toggle;
}
}
Your data source now must not be Strings, so you should instead have your List as List and you can easily add your strings to the list by passing on new Item("THE STRING DATA HERE"); when adding data to the list.
So instead of
String allData = getItem(position);
you would use
Item item = getItem(position);
then on the getView would look something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = LayoutInflater.from(getContext());
View customView = inflater.inflate(R.layout.row, parent, false);
Item item = getItem(position);
final Switch status = (Switch) customView.findViewById(R.id.switchStatus);
status.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
// here we change the value of the item which is referring to the specific index in the array returned by getItems()
item.setToggled(isChecked);
Toast.makeText(getContext(),"selectid",Toast.LENGTH_LONG).show();
}
});
// here we get what the latest value of the switch is
status.setChecked(item.toggled);
return customView;
}
Make sure you also change your data source into a data structure/list that represents somewhat like List or anything similar on which you decide to use.
BONUS TIP:
It is recommended to use a ViewHolder to hold your View and for efficient recycling of instances.
class ViewHolder {
Switch statusSwitch;
View customView;
}
Then to use that in the getView() part you can easily.
You actually don't have to create a new instance of the view since convertView or the second parameter of the method represents the view that could be reused or instantiated if null.
Heterogeneous lists can specify their number of view types, so that this View is always of the right type, so you are recommended to use that.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(getContext());
ViewHolder holder = null;
Item item = getItem(position);
if (convertView == null) { // instantiate if not yet instantiated
convertView = inflater.inflate(R.layout.supplies_list_item, null);
holder.statusSwitch = (Switch) convertView.findViewById(R.id.switchStatus);
}
else {
holder = (ViewHolder) convertView.getTag();
}
// you could set the values/ listeners here
holder.statusSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
item.setToggled(isChecked);
Toast.makeText(getContext(),"selectid",Toast.LENGTH_LONG).show();
}
});
holder.statusSwitch.setChecked(item.toggled);
return convertView;
}

Related

dynamically built checkboxes are being reset on scroll

I have a list view with adaptor where I create some checkboxes, in the adaptor I add an on change listener to change the value in my realm database. Just above this I am getting the current values stored in the database to persist the previous values.
This seems to work rather well, but when you scroll the items that you have checked will become unchecked when past the screen.
As seen here:
The code that generates this:
Adaptor.java
if(check != null){
viewHolder.checkbox.setText(check.title);
if (check.completed == 1) {
viewHolder.checkbox.setChecked(true);
} else {
viewHolder.checkbox.setChecked(false);
}
viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
check.setCompleted();
} else {
check.setNotCompleted();
}
}
});
}
Fragment.java
final ChecksAdaptor checksAdaptor = new ChecksAdaptor(device.checks(), getContext());
listView.setAdapter(checksAdaptor);
Full Adaptor
public class ChecksAdaptor extends ArrayAdapter<DeviceCheck> {
public RealmResults<DeviceCheck> checks;
Context mContext;
// View lookup cache
private static class ViewHolder {
CheckBox checkbox;
}
public ChecksAdaptor(RealmResults<DeviceCheck> checks, Context context) {
super(context, R.layout.check_row_item, checks);
this.checks = checks;
this.mContext=context;
}
private int lastPosition = -1;
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// Get the data item for this position
final DeviceCheck check = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.check_row_item, parent, false);
viewHolder.checkbox = convertView.findViewById(R.id.checkbox);
result=convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
lastPosition = position;
if(check != null){
viewHolder.checkbox.setText(check.title);
if (check.completed == 1) {
viewHolder.checkbox.setChecked(true);
} else {
viewHolder.checkbox.setChecked(false);
}
viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
check.setCompleted();
} else {
check.setNotCompleted();
}
}
});
}
// Return the completed view to render on screen
return convertView;
}
}
As suggested by mTak
viewHolder.checkbox.setOnCheckedChangeListener(null)

Listview lag when scrolling, and crash when add item

I have a custom listview with a button to add more elements
but when I add and element the app crash, but when I restart I find that the element is added, (rarely it doesn't crash)
And i
I use custom adapter
class CustomAdapter extends BaseAdapter {
ArrayList<ListItem> listItems = new ArrayList<ListItem>();
CustomAdapter(ArrayList<ListItem> list){
this.listItems = list;
}
#Override
public int getCount() {
return listItems.size();
}
#Override
public Object getItem(int position) {
return listItems.get(position).name;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int i, View convertView, ViewGroup parent) {
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.list_item,null);
TextView name = (TextView) view.findViewById(R.id.name);
TextView lastm = (TextView) view.findViewById(R.id.last);
TextView time = (TextView) view.findViewById(R.id.time);
CircleImageView pic= (CircleImageView) view.findViewById(R.id.pic);
name.setText(listItems.get(i).name);
lastm.setText(listItems.get(i).lastm);
time.setText(listItems.get(i).time);
Bitmap bmp = BitmapFactory.decodeByteArray(listItems.get(i).pic,0,listItems.get(i).pic.length);
pic.setImageBitmap(bmp);
return view;
}
}
and when I add an element the app crashs
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText editText=(EditText) mView.findViewById(R.id.name);
String name=editText.getText().toString();
boolean result=myDataBase.insertData(imageViewToByte(img),name,"no messages yet","");
if (result) {
Toast.makeText(Main2Activity.this, "Saved in DATABASE", Toast.LENGTH_SHORT).show();
viewLastData();
dialog.dismiss();
}
If your ListView lags it's because you used wrap_content for the listView's layout_height. It forces your ListView to count all the items inside and it has a huge performance impact.
So replace wrap_content by match_parent to avoid those lags.
EDIT: Use a ViewHolder pattern too in your Adapter, see this link.
Here is an example:
// ViewHolder Pattern
private static class ViewHolder {
TextView name, status;
}
#Override #NonNull
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
LayoutInflater vi = LayoutInflater.from(getContext());
convertView = vi.inflate(R.layout.list_item, parent, false);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.text_name);
holder.status = (TextView) convertView.findViewById(R.id.another_text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// Then do the other stuff with your item
// to populate your listView
// ...
return convertView
}

All values in RecycleView and ListView are changing while I'm using dialog

I have a dialog and I want to add a recyclerView or listView and I know that I have to use a List to control items in each row. Now when I want to change a value of a checkbox or a radioButton all the data in List are changing. I used Log and I got that all the booleans data changed at the same time. I moved all items to an activity but the problem still remains. It seems there is a link between the arrayList data.
Also I changed my codes and used SwitchCompat but all Boolean data in mDataSet are changing when I want to change just one of them.
public class AdapterSetOnOffTime extends RecyclerView.Adapter<AdapterSetOnOffTime.ViewHolder> {
private Context mContext;
private ArrayList<StructSetOnOffTime> mDataSet;
private View mView;
public AdapterSetOnOffTime(Context context, ArrayList<StructSetOnOffTime> dataSet) {
mContext = context;
mDataSet = dataSet;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int id = R.layout.layout_list_settime_items;
mView = LayoutInflater.from(mContext).inflate(id, parent, false);
return new ViewHolder(mView);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.mSwitch.setChecked(mDataSet.get(position).setOnOffValue);
holder.mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if(isChecked){
mDataSet.get(position).setOnOffValue = true;
}else{
mDataSet.get(position).setOnOffValue = false;
}
}
});
}
#Override
public int getItemCount() {
return mDataSet.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextTitle;
public TextView mTextValue;
public ImageButton mBtn;
public SwitchCompat mSwitch;
public ViewHolder(View itemView) {
super(itemView);
mTextTitle = (TextView) itemView.findViewById(R.id.txtSetTime);
mTextValue = (TextView) itemView.findViewById(R.id.txtOnOffStatus);
mBtn = (ImageButton) itemView.findViewById(R.id.btnSetTime);
mSwitch = (SwitchCompat) itemView.findViewById(R.id.swPrefSwitch);
}
}
}
Update answer
After wasting a day I recognized that I made a little mistake to fill ArrayList and I'm going to share it.
Previous method of fill adapter:
StructSetOnOffTime item = new StructSetOnOffTime();
ArrayList<StructSetOnOffTime> arrayList = new ArrayList<>();
for(int i = 0; i < 9; i++){
item.setTimeValue = null;
item.setOnOffValue = false;
arrayList.add(item);
}
As #Anatoli said, I had a problem in this section and I solved it by changing the upper method to this one:
ArrayList<StructSetOnOffTime> arrayList = new ArrayList<>();
for(int i = 0; i < 9; i++){
StructSetOnOffTime item = new StructSetOnOffTime();
item.setTimeValue = null;
item.setOnOffValue = false;
arrayList.add(item);
}
You set to radOn and radOff same OnCheckedChangeListener and checks in listener if it was set to true. Why you don't make 2 listeners?
CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mDataSet.get(position).onOffStatus = true;
// update adapter
notifyDataSetChanged();
}
}
};
holder.radOn.setOnCheckedChangeListener(listener);
CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mDataSet.get(position).onOffStatus = false;
// update adapter
notifyDataSetChanged();
}
}
};
holder.radOff.setOnCheckedChangeListener(listener);
looks much simpler
calling of
holder.radOn.setChecked(holder.radOn == buttonView);
holder.radOff.setChecked(holder.radOff == buttonView);
triggers the listeners. To update values in list, just call adapter.notifyDataSetChanged();
UPDATE
For each new item in list you must initialize new Holder
RecyclerView
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
// Inflate the custom layout
View itemView = LayoutInflater.from(parent.getContext())
.inflate(layoutId, parent, false);
return new Holder(itemView, viewType);
}
ArayList
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
Holder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHolder = new Holder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (Holder) convertView.getTag();
}
Item item = getItem(position);
fill(item, position);
return convertView;
}
When ever you set the check state of your view(from your code also if view didn't get destroyed) your check listener will get called so you need to manually maintain the state. It will work for all radiobutton, checkbox etc.
holder.mSwitch.setOnCheckedChangeListener(null);
holder.mSwitch.setChecked(mDataSet.get(position).setOnOffValue);
holder.mSwitch.setOnClickListener(new OnClickListener(){
//holder.mSwitch.setCheck(!mDataSet.get(position).setOnOffValue);
mDataSet.get(position).setOnOffValue = !mDataSet.get(position).setOnOffValue;
});

ExpandableListView.getGroupView() convertView arg null in fragment

Im new to android programming, the current architecture of my application is a Main Activity with a Fragment that fills the display. I am trying to create an ExpandableListView inside the Fragment which also fills the display. Everything works as i am expecting it, i think, however I cannot populate either the Child View or Group View.
In my Fragment Java class file i have;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ExpandableListView lv = (ExpandableListView) getView().findViewById(R.id.list);
lv.setAdapter(new ParentLevel(null));
}
ParentLevel which extends BaseExpandableListAdapter has;
#Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent)
{
return convertView;
}
convertView returns null, but parent returns the ExpandableListView in my layout file.
I'm not sure how to work with this or what to do next, if i wanted to just have a simple TextView, for a title, and a the group arrow what would i do?
I plan to use an enum for the data, which I am already inspecting for getGroupCount() and getChildCount()
I was able to solve my problem with this peice of code;
private final LayoutInflater inf;
private FragmentActivity activity;
public ParentLevel(FragmentActivity act, MenuItem current) {
activity = act;
inf = LayoutInflater.from(act);
};
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent)
{
ViewHolder holder;
if (convertView == null) {
convertView = inf.inflate(R.layout.expandable_list_item, parent, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(activity.getString(siblings.get(groupPosition).getName()));
return convertView;
}
and this class;
private class ViewHolder {
TextView text;
}

Java Android Data Binding ListView

I have problem with list view in data binding because I don't know how to set custom ArrayAdapter in view model, and how to add onItemClick listener in view model. Someone can show how to do this? Internet have really little information about this.
Simply set the adapter for the ListView. R.id.listview must refer to your ListView defined in the Layout obviously and R.layout.listviewrow to a layout that a row should have. Furthermore add a new Instance of onItemClickListener to the ListView.
MyCustomArrayAdapter adapter = new MyCustomArrayAdapter(getActivity(), R.layout.listviewrow);
ListView lv = (ListView) getActivity().findViewById(R.id.listview);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//perform desired action here
}
} );
The Adapter itself should look something like this:
public class MyCustomArrayAdapter extends ArrayAdapter<Item> {
public MyCustomArrayAdapter(Context context, int resource) {
super(context, resource);
}
public MyCustomArrayAdapter(Context context, int resource, List<Item> items) {
super(context, resource, items);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(resource, null);
}
Item item = getItem(position);
if (item != null) {
TextView tvFirstName = (TextView) v.findViewById(R.id.firstName);
TextView tvLastName = (TextView) v.findViewById(R.id.lastName);
if (tvFirstName != null) {
tvFirstName.setText(item.getFirstName());
}
if (tvLastName != null) {
tvLastName.setText(item.getLastName);
}
}
return v;
}

Categories