Checked RadioButton Recycling - android

I'm adding radiogroups programatically to my recyclerview and it's working fine.
But when I check it and scroll the recyclerview it loses checked radios.
I've seen many ways and examples of solutions, but I cannot achieve it. It's been some days in a row.
I'm saving the checked radio in model as you can see in code below.
Adapter:
#Override
public void onBindViewHolder(final NROptionLineHolder holder, int position) {
holder.priceGroup.removeAllViews();
holder.priceGroup.setOnCheckedChangeListener(null);
int id = (position+1)*100;
checklistModel = mChecklists.get(position);
holder.packageName.setText(checklistModel.getTitle());
for(String price : checklistModel.getQuestions()){
RadioButton rb = new RadioButton(NROptionLineAdapter.this.context);
rb.setId(id++);
rb.setText(price);
holder.priceGroup.addView(rb);
}
holder.priceGroup.check(checklistModel.getSelectedId());
holder.priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
checklistModel.setSelectedId(checkedId);
Log.d(TAG, "onCheckedChanged: " + checkedId);
}
});
}
Holder
OnNROptionListener onNROptionListener;
public NROptionLineHolder(View itemView, OnNROptionListener onNROptionListener) {
super(itemView);
packageName = itemView.findViewById(R.id.package_name);
priceGroup = itemView.findViewById(R.id.price_grp);
// priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
// #Override
// public void onCheckedChanged(RadioGroup radioGroup, int i) {
//
// Log.d(TAG, "onCheckedChanged: " + radioGroup.getCheckedRadioButtonId() + " " + i);
// }
// });
this.onNROptionListener = onNROptionListener;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onNROptionListener.onNROptionClick(getAdapterPosition());
}
public interface OnNROptionListener {
void onNROptionClick(int position);
}
}
EDIT 1 - Radio Group
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/package_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RadioGroup
android:id="#+id/price_grp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/package_name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:orientation="horizontal"/>
</android.support.constraint.ConstraintLayout>
EDIT 2
As requested, here is the important code from my ChecklistActivity
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_checklist);
intent = getIntent();
size = intent.getIntExtra("size", 0);
nr = intent.getIntExtra("nr", 0);
Log.d(TAG, "Checklist Activity - Qtd Questões: " + size);
Log.d(TAG, "Checklist Activity - NR: " + nr);
btnSaveCheck = findViewById(R.id.btnSaveChecklist);
mRecyclerView = findViewById(R.id.package_lst);
setupRecycler();
btnSaveCheck.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Sucesso", Toast.LENGTH_SHORT).show();
}
});
}
private void setupRecycler() {
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
setupList();
mAdapter = new NROptionLineAdapter(data, this, getApplication());
mRecyclerView.setAdapter(mAdapter);
}
private void setupList(){
data = new ArrayList<>();
class setupList extends AsyncTask<Void, Void, List<MRNrOption>> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected List<MRNrOption> doInBackground(Void... voids) {
list = DatabaseClient
.getInstance(getApplicationContext())
.getAppDatabase()
.mrNrOptionDAO()
.loadAllByNRId(nr);
return list;
}
#Override
protected void onPostExecute(List<MRNrOption> list) {
super.onPostExecute(list);
List<String> priceList = new ArrayList<>();
priceList.add("Sim");
priceList.add("Não");
priceList.add("Não se Aplica");
for (int i=0; i<list.size(); i++) {
Log.d(TAG, "NRs Activity - Adding To List: " + list.get(i).getTitle());
data.add(new Checklist(
list.get(i).getTitle(),
priceList)
);
mAdapter.notifyDataSetChanged();
}
}
}
setupList lm = new setupList();
lm.execute();
}
EDIT 3 - Important
The RadioGroups and RadioButtons are programatically generated because I'm getting all questions from server, the number of questions are different depending on previous selections made by user, that's why I need it this way.
EDIT 4
GIF to enhance the problem visualization
EDIT 5 - Checklist Model Class
public class Checklist {
String title;
List<String> questions;
boolean isRadioButtonAdded;
int selectedId;
public Checklist(String title, List<String> questions) {
this.title = title;
this.questions = questions;
}
public Checklist(){}
public boolean getIsAdded(){
return isRadioButtonAdded;
}
public void setIsAdded(boolean isAdded){
this.isRadioButtonAdded = isAdded;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getQuestions() {
return questions;
}
public void setQuestions(List<String> questions) {
this.questions = questions;
}
public int getSelectedId() {
return selectedId;
}
public void setSelectedId(int selectedId) {
this.selectedId = selectedId;
}
}

Though i'm not sure as to whether this will solve your problem or not, but as an optimiziation also a good practice you should attach listeners to the onCreateViewHolder instead of the onBindViewHolder this prevents multiple objects from getting created for the listener.
Why dont you move this code inside the onCreateViewHolder
this block inside the view holder you have:
priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
checklistModel.setSelectedId(checkedId);
Log.d(TAG, "onCheckedChanged: " + checkedId);
}
});

Try moving your setOnCheckedChangeListener code to ViewHolder and update your mCheckList here:
priceGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
mCheckList.get(getAdapterPosition()).setSelectedId(checkedId);
Log.d(TAG, "onCheckedChanged: " + radioGroup.getCheckedRadioButtonId() + " " + i);
}});
Main problem is that you're not updating correct items state. When you click on radio button it will update only last item onBindViewHolder called because checklistModel holds only last reference. To fix this you always need to access mainList inside listeners.

Save the checked / unchecked status of the radio button to your model (i.e. your items in the list should have a field for this) when the onClick event happens. When you bind the ViewHolder, make sure you set checkbox's value to whatever you saved in your model.

The unique working solution was to set RecyclerView to not recycleable in ViewHolder.
this.setIsRecyclable(false);

How about the following code?
Adapter:
Since the view is recycled, I thought that the unique id is a mistake.
int id = (position+1)*100;
to
int id = 1;

The way recyclerview works is that when you scroll down or up until the view is invisible, it will store the state but when you scroll down recyclerview or scroll up recyclerview it will destroy the row and it will put a new row into that position.so the entire row's view will be destroyed.
The solution to the question is that you add another variable to the list you are using and when the radio button changes state you store the data and move on.
like this in your data model class
And in Model Class you define which list is recyclerview is using.
Boolean is stateclicked;
int state position;
and in OnBindViewholder you can get the value of this data. If the value is null then it's not clicked and if it is clicked then you change the boolean to yes and put the state's value into the integer

Related

Show only certain items in recycleview according to condition

I have a recycleview showing a list of audio files fetched from my audios.json file hosted on my server. i have a model class with a getter method getLanguage() to see the audio language. I would like to show only audio files of users preference in recycle view. Say for example, if user wants only english and russian i would like to show only list of russian and english. How can we achieve this? Right now the entire list is displayed.
public class AudioAdapter extends RecyclerView.Adapter<AudioAdapter.HomeDataHolder> {
int currentPlayingPosition = -1;
Context context;
ItemClickListener itemClickListener;
List<Output> wikiList;
public AudioAdapter(List<Output> wikiList, Context context) {
this.wikiList = wikiList;
this.context = context;
}
#NonNull
#Override
public HomeDataHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.audio_row_layout,viewGroup,false);
HomeDataHolder mh = new HomeDataHolder(view);
return mh;
}
#Override
public void onBindViewHolder(#NonNull final HomeDataHolder homeDataHolder, int i) {
String desc = wikiList.get(i).getLanguage() + " • " + wikiList.get(i).getType();
homeDataHolder.tvTitle.setText(wikiList.get(i).getTitle());
homeDataHolder.tvotherinfo.setText(desc);
homeDataHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
homeDataHolder.rippleLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return wikiList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
public void setClickListener(ItemClickListener itemClickListener) { // Method for setting clicklistner interface
this.itemClickListener = itemClickListener;
}
public class HomeDataHolder extends RecyclerView.ViewHolder {
TextView tvTitle,tvotherinfo;
MaterialRippleLayout rippleLayout;
public HomeDataHolder(View v) {
super(v);
this.tvTitle = v.findViewById(R.id.title);
this.tvotherinfo = v.findViewById(R.id.audioDesc);
this.rippleLayout = v.findViewById(R.id.ripple);
}
}
}
The general idea for this should be:
you have one list with all items
you have filter rules selected by the user
You filter items from number 1, to see which ones match the constraints and store this in another list.
Then the recycler view only shows the items of the list from number 3.
This means that recycler view's getItemCount would return the size of the filtered list, not the whole list.
Instead of passing the wikiList as it is, filter it then send it:
Lets say that you filled up the wikiList, before passing it to the adapter, filter it like this:
In the activity that you initialize the adapter in:
public class YourActivity extends ............{
........
........
//your filled list
private List<Output> wikiList;
//filtered list
private List<Output> filteredList= new ArrayList<Output>();
//filters
private List<String> filters = new ArrayList<String>();
//lets say the user chooses the languages "english" and "russian" after a button click or anything (you can add as many as you want)
filters.add("english");
filters.add("russian");
//now filter the original list
for(int i = 0 ; i<wikiList.size() ; i++){
Output item = wikiList.get(i);
if(filters.contains(item.getLanguage())){
filteredList.add(item);
}
}
//now create your adapter and pass the filteredList instead of the wikiList
AudioAdapter adapter = new AudioAdapter(filteredList , this);
//set the adapter to your recyclerview........
......
.....
......
}
I use above "english" and "russian" for language. I don't know how they are set in your response, maybe you use "en" for "english" so be careful.

Handling click button in each RecyclerView and update TextView value

When button clicked, i must update a TextView in same position and I have done it, but 9th and 10th position of RecyclerView follow first position and second position. In other word, if I clicked first button position, First position of TextView is updated, but, 9th position of TextView also updated, It should be not updated. How to solve this?
I follow this link
here is my Adapter
class ProductsByStoreAdapter extends RecyclerView.Adapter<ProductsByStoreAdapter.ViewHolder> {
private ArrayList<Products> products;
ProductsByStoreAdapter(ArrayList<Products> productses) {
this.products = productses;
//products = CenterRepository.getCenterRepository()
//.getListOfProductsInShoppingList();
}
#Override
public ProductsByStoreAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.products_card_item, viewGroup, false);
return new ProductsByStoreAdapter.ViewHolder(view);
}
class ViewHolder extends RecyclerView.ViewHolder{
private TextView tv_product_name, tv_product_price, tv_product_quantity;
private ImageView im_product_image;
private ImageButton button_add_product, button_min_product;
private EditText e_note;
private LinearLayout layout_note;
ViewHolder(View view) {
super(view);
im_product_image = (ImageView)view.findViewById(R.id.product_image);
tv_product_name = (TextView)view.findViewById(R.id.product_name);
tv_product_price = (TextView)view.findViewById(R.id.product_price);
tv_product_quantity = (TextView)view.findViewById(R.id.product_quantity);
e_note = (EditText)view.findViewById(R.id.e_note);
layout_note = (LinearLayout)view.findViewById(R.id.layout_note);
this.button_add_product = (ImageButton)view.findViewById(R.id.button_add_product);
button_min_product = (ImageButton)view.findViewById(R.id.button_min_product);
}
}
#Override
public void onBindViewHolder(final ProductsByStoreAdapter.ViewHolder viewHolder, final int position) {
Glide.with(viewHolder.im_product_image.getContext())
.load(products.get(position).getImage_uri())
.centerCrop()
.crossFade()
//.placeholder(R.drawable.placeholder_main)
.into(viewHolder.im_product_image);
CurrencyFormats currencyFormat = new CurrencyFormats();
viewHolder.tv_product_name.setText(products.get(position).getName());
viewHolder.tv_product_price.setText(currencyFormat.toRupiah(products.get(position).getPrice()));
//viewHolder.tv_product_quantity.setText("0");
viewHolder.button_add_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//current object
Products tempObj = (products).get(position);
((ProductsByStoreActivity)view.getContext()).updateItemCount(true);
tempObj.setQuantity(String.valueOf(1));
viewHolder.tv_product_quantity.setText(tempObj.getQuantity());
}
});
viewHolder.button_min_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Products tempObj = (products).get(position);
viewHolder.tv_product_quantity.setText(CenterRepository
.getCenterRepository().getListOfProductsInShoppingList()
.get(indexOfTempInShopingList).getQuantity());
((ProductsByStoreActivity)view.getContext()).updateItemCount(false);
}
}
}else {
}
}
});
}
#Override
public int getItemCount() {
//return products.size();
return products == null ? 0 : products.size();
}}
You need to move your onclick listener into onCreateViewHolder.
final ProductsByStoreAdapter.ViewHolder viewHolder = new ProductsByStoreAdapter.ViewHolder(view);
button_add_product.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//current object
Products tempObj = products.get(viewHolder.getAdapterPosition(););
((ProductsByStoreActivity)view.getContext()).updateItemCount(true);
tempObj.setQuantity(String.valueOf(1));
viewHolder.tv_product_quantity.setText(tempObj.getQuantity());
}
});
return viewHolder;
You can do the same with the other onclicklistener
Update: You do not setText to your product_quantity textview in the BindView function, unless a button is clicked. this means its value will be recycled from other items. you should check with an if statement what is the quantity of the item and present it even without clicking.
Old and not correct answer:
I am not sure if this is the problem, but its an easy check, so try it out. There are 2 positions - the adapter position, and the layout position. I think maybe the position you are using (the one that came from the onBind function) is the latter. You want the adapter position, so try using getAdapterPosition() like this:
Products tempObj = (products).get(getAdapterPosition());
add below line to resolve the problem of 9th and 10th position of item
#Override
public int getItemViewType(int position)
{
return position;
}

Recycler view can't select all items

I'm using RecyclerView with select all option.this option select screen visible items only. Not all select. But I scroll top to bottom after selected. what is the problem?
Adapter class:
public class FilterBrandAdapter extends RecyclerView.Adapter<FilterBrandAdapter.MyViewHolder> {
private ArrayList<FilterBrandDataModel> mBrandModelArraylist;
private Context mContext;
private TinyDB mPrefDb;
private boolean isSelectedAll;
public static final String PREF_SELECTED_ALL = "selectedAll";
private CartTotalListener mCartListener;
public void selectAll() {
Log.e("onClickSelectAll", "yes");
isSelectedAll = true;
notifyDataSetChanged();
}
public void deselectAll() {
Log.e("onClickSelectAll", "no");
isSelectedAll = false;
notifyDataSetChanged();
}
class MyViewHolder extends RecyclerView.ViewHolder {
CheckBox cbItemTitle;
MyViewHolder(View itemView) {
super(itemView);
cbItemTitle = (CheckBox) itemView.findViewById(R.id.checkBox_filter_brand_title);
itemView.setClickable(true);
}
}
public FilterBrandAdapter(BrandFilterActivity activity, ArrayList<FilterBrandDataModel> mArrayList) {
this.mContext = activity;
this.mBrandModelArraylist = mArrayList;
LayoutInflater inflater = activity.getLayoutInflater();
try {
this.mCartListener = ((CartTotalListener) mContext);
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement AdapterCallback.");
}
}
#Override
public FilterBrandAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_recycler_brand_items, parent, false);
mPrefDb = new TinyDB(mContext);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
final FilterBrandDataModel brandsData = mBrandModelArraylist.get(position);
holder.cbItemTitle.setText(mBrandModelArraylist.get(position).getCategoryName());
//set adapter classcheckbox change listener for if clicked or not
holder.cbItemTitle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (holder.cbItemTitle.isChecked()) {
//if check box checked selected checkbox saved in tinyDB
int getPosition = (int) buttonView.getTag();
mBrandModelArraylist.get(getPosition).setSelected(buttonView.isChecked());
mPrefDb.putBoolean(brandsData.getCategoryName(), true);
holder.cbItemTitle.setChecked(true);
Log.e("checked item-->", brandsData.getCategoryName());
mCartListener.onMethodCallback();
} else {
//else check box un-checked selected checkbox save checkbox is un-checked in tinyDB
int getPosition = (int) buttonView.getTag();
mBrandModelArraylist.get(getPosition).setSelected(buttonView.isChecked());
mPrefDb.putBoolean(brandsData.getCategoryName(), false);
holder.cbItemTitle.setChecked(false);
Log.e("un-checked item-->", brandsData.getCategoryName());
mCartListener.onMethodCallback();
}
}
});
//select & deselect all checkbox in recyclerview
if (!isSelectedAll) {
holder.cbItemTitle.setTag(position);
holder.cbItemTitle.setChecked(mBrandModelArraylist.get(position).isSelected());
holder.cbItemTitle.setChecked(false);
} else {
holder.cbItemTitle.setTag(position);
holder.cbItemTitle.setChecked(mBrandModelArraylist.get(position).isSelected());
holder.cbItemTitle.setChecked(true);
}
//save selected (or) deselect checkbox with position in tinyDB
holder.cbItemTitle.setTag(position);
holder.cbItemTitle.setChecked(mBrandModelArraylist.get(position).isSelected());
boolean checked = mPrefDb.getBoolean(brandsData.getCategoryName(), true);
holder.cbItemTitle.setChecked(checked);
}
#Override
public int getItemCount() {
return mBrandModelArraylist.size();
}
}
I use interface(mCartListener.onMethodCallback()) for notify selected items & display items counts in text view.
my interface:
#Override
public void onMethodCallback() {
ArrayList<String> array = new ArrayList<>();
//checking which items are selected in adapter class & save into array list
for (FilterBrandDataModel brandDataModel : mArrayList) {
if (brandDataModel.isSelected()) {
array.add(brandDataModel.getCategoryName());
}
}
int size = array.size();
if (size == mArrayList.size()) {
mPrefDb.putString(PREF_SELECTED_BRANDS_TOT, "true");
String text = String.valueOf(size) + " " + getResources().getString(R.string.msg_selected);
mBrandResults.setText(text);
} else {
mPrefDb.putString(PREF_SELECTED_BRANDS_TOT, "false");
String text = String.valueOf(size) + " " + getResources().getString(R.string.msg_selected);
mBrandResults.setText(text);
}
}
RecyclerView loads the data while scrolling, but I need all data in it without scrolling. So, I changed the RecyclerView in ScrollView like that:
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.85"
android:overScrollMode="never"
android:scrollbars="none">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
/>
</ScrollView>
then, in the Activity,
RecyclerView.setNestedScrollEnabled(false);
I use the ScrollView to scroll items. That's it.
The problem is that you only switch a flag in selectAll() (isSelectedAll = true;). In your adapter there will be for example 3 visible items (i don't know how big they are at your screen). One item is represented by one viewholder. After you switch the flag and call notifyDataSetChanged() the function onBindViewHolder() will be called for every visible item -> in my example 3 times. In this function you decide with if (!isSelectedAll) { if the checkbox should be selected. So it will never be called for all items.
Solution:
Change
public void selectAll() {
Log.e("onClickSelectAll", "yes");
isSelectedAll = true;
notifyDataSetChanged();
}
To
public void selectAll() {
Log.e("onClickSelectAll", "yes");
for(FilterBrandDataModel brandsData : mBrandModelArraylist) {
mPrefDb.putBoolean(brandsData.getCategoryName(), true);
}
notifyDataSetChanged();
}
And the same for deselectAll().

How can I RecyclerView Different View Selected or UnSelected

I currently use RecyclerView but i can't fix the issue;
If user selected a answer, answer in change textview color and background.
If user selected different answer first answer old textview color and background.
Codes;
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
_mContext = holder._mAnswersContainer.getContext();
_mPosition = position;
holder._mImageAnswer.setImageDrawable(Utils.stringToResource(_mContext,
_mAnswerList.get(_mPosition).mAnswerImage));
holder._mImageTextAnswer.setText(_mAnswerList.get(_mPosition).mAnswerText);
holder._mAnswersContainer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder._mImageTextAnswer.setTextColor(_mContext.getResources()
.getColor(R.color.white));
holder._mImageTextAnswer.setBackgroundColor(_mContext.getResources()
.getColor(R.color.red));
Log.d(TAG, "Values : " + QuestionsHelper.getInstance(_mContext)
.getValues(_mAnswerList.get(_mPosition).mAnswerText));
}
});
}
Inside your adapter, make a member variable to keep track of which position is selected:
private int mSelected = -1;
Inside your onBindViewHolder (although it might work inside onCreateViewHolder as well):
int color;
if(position == mSelected){
color = ContextCompat.getColor(context, R.color.selectedColor);
}else{
color = ContextCompat.getColor(context, R.color.regularColor);
}
// Set the color
viewHolder.yourView.setBackgroundColor(color);
Create some helper functions for your RecyclerView adapter to handle the selection:
public void selectPosition(int selected){
mSelected = selected;
notifyDataSetChanged();
}
public void resetSelected(){
mSelected = -1;
notifyDataSetChanged();
}
Wherever you want to set the selected item just call adapter.selectPosition(). And clear the selection with adapter.resetSelected()

How to get the Value out of an RecyclerView item

I have a Fragment which carries a Button and a RecyclerView, set up by an RecyclerView Adapter. In the RecyclerView are several Items, one of it is a EditText. Now I want that when the Button is clicked(which is NOT in the RecyclerView object), that I get the values of the EditTexts.
I already tried to get the recyclerView.getItemAtPosition() but there is no function like that, also tried the same for the adapter. So I would need something like
ArrayList s.add(recyclerView.getItemAtPosition(position).getEditText().getText().toString());
This is my Adapter:
public class RVSetAdapter extends RecyclerView.Adapter<RVSetAdapter.ViewHolder> {
private Exercise exercise;
public static class ViewHolder extends RecyclerView.ViewHolder {
public EditText et_weight;
public TextView tv_sets,tv_indication;
public ViewHolder(#NonNull final View itemView) {
super(itemView);
tv_sets = itemView.findViewById(R.id.tv_sets);
tv_indication = itemView.findViewById(R.id.tv_indication);
et_weight = itemView.findViewById(R.id.et_weight);
}
}
public RVSetAdapter(Exercise exercise) {
this.exercise = exercise;
}
#NonNull
#Override
public RVSetAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.rv_set,viewGroup,false);
RVSetAdapter.ViewHolder vh_set = new RVSetAdapter.ViewHolder(view);
return vh_set;
}
#Override
public void onBindViewHolder(#NonNull final RVSetAdapter.ViewHolder viewHolder,final int i) {
if(exercise.getKind() == 80) {
viewHolder.tv_sets.setText("");
viewHolder.tv_indication.setText("sec.");
}else if(exercise.getKind() == 90) {
viewHolder.tv_sets.setText("");
viewHolder.tv_indication.setText("min.");
}else {
viewHolder.tv_sets.setText(Integer.toString(i + 1) + ".");
}
viewHolder.et_weight.setText(Integer.toString(exercise.getWeights().get(i)));
}
#Override
public int getItemCount() {
return exercise.getWeights().size();
}
}
this is my Fragment:
final View view = layoutInflater.inflate(R.layout.fragment_exercise, container,false);
ImageView iv_exercise = view.findViewById(R.id.iv_exercise);
ImageView iv_musclekind = view.findViewById(R.id.iv_musclekind);
ImageView iv_save = view.findViewById(R.id.iv_save);
TextView tv_exercisename = view.findViewById(R.id.tv_exercisename);
TextView tv_exercisedescription = view.findViewById(R.id.tv_exercisedescription);
iv_exercise.setImageResource(exercises.get(position).getImage());
iv_musclekind.setImageResource(exercises.get(position).getMusclekindImage());
tv_exercisename.setText(exercises.get(position).getName());
tv_exercisedescription.setText(exercises.get(position).getDescription());
iv_save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//here I want to get the Values of the EditTexts and put them into an Array
}
});
recyclerView = view.findViewById(R.id.rv_sets);
recyclerView.setHasFixedSize(true); //maybe change this
layoutManager = new LinearLayoutManager(view.getContext());
adapter = new RVSetAdapter(exercises.get(position));
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
container.addView(view);
return view;
I don't have any ideas to go on so I would appreciate your help. If there is any uncertainty with my description of the problem please don't hesitate to ask.
Greetings Alexander
With RecyclerView, you have to understand that your EditTexts will be recycled. For example, if you have a list of 200 items, and it shows 2 items at one time, you will only ever have 2 EditText. They will reuse the higher EditText for the lower elements.
For example, here is a list that contains EditText showing only 2 at a time, and as the user scrolls, it will recycle and reuse them.
EditText A
Edittext B
EditText A (recycled)
EditText B (recycled)
....
This means you cannot just loop over all the elements later and get the values, as they don't store their values.
So, what you want to do, is when the user modifies an EditText, you want to store that value right away. You can do this by adding a TextWatcher to your EditText.
Note - I did assume you store your weights as String values, so I just took the value from the EditText and stored it into your Exercise Object. You may want to convert it before that.
public class RVSetAdapter extends RecyclerView.Adapter<RVSetAdapter.ViewHolder> {
private Exercise exercise;
// ...
#Override
public void onBindViewHolder(#NonNull final RVSetAdapter.ViewHolder viewHolder,final int i) {
// ...
viewHolder.et_weight.setText(Integer.toString(exercise.getWeights().get(i)));
viewHolder.et_weight..addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
// This will be the text from the EditText
String text = s.toString();
// Store the value back into your exercise Object.
exercise.getWeights().get(i).setWeight(text);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
}
// ...
// Add a method for easy access to your weights.
public ArrayList<String> getWeights() {
return exercise.getWeights();
}
}
And now, within your Fragment, you can easily get the values out of your RVSetAdapter.
public View onCreateView() {
final View view = layoutInflater.inflate(R.layout.fragment_exercise, container,false);
// ...
iv_save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Use the method we added to your adapter to return the weights.
ArrayList<String> weights = adapter.getWeights();
}
});
// ...
return view;
}
I think you should use ArrayList in Adapter class to keep your items (or just Strings of EditText components). Add String to ArrayList in your onBindViewHolder() after you set text for editext. Then make a function which will get item from your ArrayList like:
public String getItem(int position){
arrayList.get(position);
}
and call it from your onClick() function in Fragment.
I think you can create static button and you can then access that button in your adapter then implement the functionality on the onclick of your button.
static Button btn;
Then implement like this in your adapter...
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
for(int i=0;i<arraylist.size();i++)
{
arr[i]= holder.edit_Text.getText().toString();
}
}
});
and put this onclick in your onbindviewholder method.

Categories

Resources