3

I'm creating an android app, in which I'm using recyclerView and the row of recyclerView is having editText.

This is my ReadingAdapter class

public class ReadingAdapter extends RecyclerView.Adapter<ReadingAdapter.ViewHolder>  implements AdapterView.OnItemSelectedListener {

    Context context;
    String valOpenReading, valClosReading, valConsumption;
    private List<ReadingData> readingList;
    static String[] arrValOpenRead, arrValClosRead, arrValConsumption;
    public ReadingAdapter(Context context, List<ReadingData> readingList) {
        this.context = context;
        this.readingList = readingList;

        arrValOpenRead = new String[readingList.size()];
        arrValClosRead = new String[readingList.size()];
        arrValConsumption = new String[readingList.size()];
    }

    @Override
    public ReadingAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.reading_sheet_layout, parent, false);
        return new ReadingAdapter.ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ReadingAdapter.ViewHolder holder, final int position) {
        ReadingData tempData = readingList.get(position);
        holder.pdtName.setText(tempData.pdtName);
        holder.keyId.setText("Key "+tempData.keyId);

        holder.etClosRead.addTextChangedListener(new TextWatcher() {
            boolean ignore = false;
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }
            @Override
            public void afterTextChanged(Editable s) {
                if (ignore)
                    return;
                ignore = true;
                valOpenReading = holder.etOpenRead.getText().toString();
                arrValOpenRead[position] = valOpenReading;
                valClosReading = s.toString().equals("") ? "0": s.toString();
                arrValClosRead[position] = valClosReading;
                if (!valOpenReading.equals("")) {
                    if (Integer.parseInt(valClosReading) < Integer.parseInt(valOpenReading)) {
                        Toast.makeText(context, "Check once! closing reading should be more than opening reading!", Toast.LENGTH_LONG).show();
                        valConsumption = "0";
                        holder.consumption.setText("");
                    } else {
                        valConsumption = (Integer.parseInt(valClosReading) - Integer.parseInt(valOpenReading))+"";
                        arrValConsumption[position] = valConsumption;
                        holder.consumption.setText(valConsumption);
                    }
                } else
                    Toast.makeText(context, "Please fill the opening reading!", Toast.LENGTH_SHORT).show();
                ignore = false;
            }
        });
    }

    @Override
    public int getItemCount() {
        return readingList.size();
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        TextView pdtName, keyId, consumption;
        EditText etOpenRead, etClosRead;
        public ViewHolder(View view) {
            super(view);
            pdtName = (TextView)view.findViewById(R.id.txt_list_pdt_supp);
            keyId = (TextView)view.findViewById(R.id.key_set);
            etOpenRead = (EditText)view.findViewById(R.id.open_val_set);
            etClosRead = (EditText)view.findViewById(R.id.clos_val_set);
            consumption = (TextView)view.findViewById(R.id.consumption_val);
        }
    }
}

This is my ReadingData.java

public class ReadingData {
    String pdtName, keyId, openReading, closReading, consumption;
    public ReadingData(String pdtName, String keyId) {
        this.pdtName = pdtName;
        this.keyId = keyId;
    }
}

Here, if I enter value in the starting items of the recyclerView then as I scroll up the items to the bottom of the list, the last item will have that value.

error in the list

Please ignore the quality of image as we can't upload above of 2MiB of snap.

Here the views are recycled as the list is scrolled. How to prevent the copying values to the other item in the list.

And that Toast is also repeated several times. How to stop this.

update: By the suggetion of LQ Gioan through the SO question How ListView's recycling mechanism works , I got the logic how ListView actually works with recycling of views.

But I'm not sure whether the recyclerView also works same.

But here in my case, how can I implement this process. pls someone help me here.

Mosius
  • 1,602
  • 23
  • 32
Shambhu
  • 181
  • 6
  • 16
  • `ListView` reuse an item view which is not visible on the screen. You should have a look this answer for understanding: https://stackoverflow.com/questions/11945563/how-listviews-recycling-mechanism-works?answertab=active#tab-top . To solve your problem you should keep the data that were leaved by the user in the `ReadingData`. Every time the `getView` method is called you set these data back to the `EditText`(s) – John Le Dec 20 '17 at 02:15
  • Thanks for the link. Now I got the logic how `listView` works. But what should I use in my case? How can I can I achieve this without recycling the views? Should I use the `recyclerView` here? – Shambhu Dec 20 '17 at 03:23
  • If you want to use `ListView` without recycling then you remove `if (convertView == null)` in your code. But you can get `OutOfMemoryException` if there are a lots of items in your list. So I **strong recommend** you using recycling in `ListView`. You can try my solution in my first comment – John Le Dec 20 '17 at 03:28
  • `ReadingData` is created just for binding the variables. Sorry for this, but I didn't understand the solution. Can you please explain more. – Shambhu Dec 20 '17 at 03:37
  • You are able to use `TextWatcher` for the `EditText`(s) to watch anything that is input by the user using `EditText.addTextChangedListener(TextWatcher)`([example here](https://stackoverflow.com/questions/33257937/edittext-addtextchangedlistener-only-for-user-input?answertab=active#tab-top)). And then, adding new variables to `ReadingData`(or create a new object -> it's up to you) to keep these input data. Anytime `getView` method is called you set these input data back to the EditText(s). – John Le Dec 20 '17 at 03:50
  • See here: http://www.webplusandroid.com/creating-listview-with-edittext-and-textwatcher-in-android/ . It's the same with your case – John Le Dec 20 '17 at 03:53
  • I've updated the code with your solution and the answered on this question. But I'm getting `java.lang.NullPointerException` here in this line `if (tempReading[holder.holdPos].openReading != null)`. how to handle this? I've updated my code in question. – Shambhu Dec 20 '17 at 06:55
  • That mean is that `tempReading[holder.holdPos]` is null. Could you check the `tempReading` list. Are there any updating on the list? And you should have a look on your code carefully. I see a strange line of code `tempReading[holder.holdPos] = (new ReadingData(valOpenReading, valClosReading, valConsumption));` why don't you update the current object instead of creating a new one ? – John Le Dec 20 '17 at 08:12
  • Bcoz, there are total 5 data. 2 of them I'm getting from DB and setting to the `textViews` of the row of `listView`. So those are in the separate object. and the other 3 values I've to get dynamically from the list, so managing with separate object. Is there any other solution here? – Shambhu Dec 21 '17 at 13:54
  • I'm ready, can I send teamViewer credentials? – Shambhu Dec 22 '17 at 06:37
  • i also have the same issue but i manage it by setting the data directly from list . check here(https://hastebin.com/ujiwanekut.java) . this may help you – Devil10 Dec 23 '17 at 12:50
  • According to my knowledge the recycling mechanism of listview and recylerview works same. The solution to your problem is that you need to cache the values of edittext in an array/arraylist with respect to the position of item and then you should set it your editext. – Muhammad Babar Dec 26 '17 at 05:31
  • According to my knowledge, you should save your data back to your model when text is changed and put values in EditText every time in onBindViewHolder method. This way, your recycled item will get updated values whenever it gets scrolled. – Pranit More Mar 28 '18 at 06:31

2 Answers2

2

RecyclerView reuse views, in fact it only generate the as many as views that is visible on the screen. so it's expected if you can see a value you set for other rows

The solution would be set all attributes of the view that you are changing to default or whatever the row should present from your data set

So put addTextChangedListener insode ViewHolder constructor(you can get position by calling getAdapterPosition()) for better performance and set the editText value inside onBindViewHolder method from your data set

Mosius
  • 1,602
  • 23
  • 32
0

Your Activity Code:

ListView listview = (ListView) findViewById(R.id.list_view);
listview.setItemsCanFocus(true);
Adapter adapter = new Adapter (YourActivity.this, YourArrayList);
listview .setAdapter(adapter);

Adapter class

public class Adapter extends BaseAdapter {

// Declare Variables \\
Context mContext;
LayoutInflater inflater;
Activity act;
String[] temp;


public Adapter(Context context, ArrayList<String> list) {
    mContext = context;
    inflater = LayoutInflater.from(mContext);
    act = (Activity) context;
    //-------Temp String Array-------\\
    temp = new String[this.count];
    for (int i = 0; i < this.count; i++) {
        temp[i] = list.get(i);
    }
    //---------------------------\\

}

public class ViewHolder {
    TextView optionTitle;
    EditText optionText;
    int ref;
}

@Override
public int getCount() {
    return list.size;
}

@Override
public Object getItem(int position) {
    return temp[position];
}

@Override
public long getItemId(int position) {
    return position;
}

public View getView(final int position, View view, ViewGroup parent) {
    final ViewHolder holder;
    if (view == null) {
        holder = new ViewHolder();
        view = inflater.inflate(R.layout.lv_items_add_ques_options_mcq, null);
        holder.optionTitle = (TextView) view.findViewById(R.id.add_ques_opts_count_mcq_tv);
        holder.optionText = (EditText) view.findViewById(R.id.add_ques_opts_title_mcq_et);
        view.setTag(holder);
    } else {
        holder = (ViewHolder) view.getTag();
    }
    holder.ref = position;

    holder.optionTitle.setText(getCharForNumber(position) + ":");

    holder.optionText.setText(temp[position]);
    holder.optionText.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        }

        @Override
        public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
                                      int arg3) {
        }

        @Override
        public void afterTextChanged(Editable arg0) {
            temp[holder.ref] = arg0.toString().trim();
        }
    });

    return view;
}

public void getList() {
    StaticValues.arrayListOptions = new ArrayList<String>(Arrays.asList(temp));
    StaticValues.arrayListOptionsCount = new ArrayList<String>();
    for (int i = 0; i < count; i++) {
        StaticValues.arrayListOptionsCount.add(String.valueOf(i+1));
        Log.e("err_al", StaticValues.arrayListOptions.get(i));
        Log.e("err_al", StaticValues.arrayListOptionsCount.get(i));
    }
}

private String getCharForNumber(int i) {
    char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    if (i > 25) {
        return null;
    }
    return Character.toString(alphabet[i]);
}}
Rahul Singh Chandrabhan
  • 2,531
  • 5
  • 22
  • 33