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().
Related
I have two recycler views. The first recyclerview is basically a list of data where I can choose an item and its quantity and I am storing this chosen item data into a map. The second one is the list of the selected data. which I am generating from getting the values() from the map. The second one also has similar viewholder and I can change the quantity there also. One the quantity reaches zero I remove the item from the list and try to notifydatasetChanged().
The problem is the removing of an item from the second list is not working properly and the app crashes with error
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder
I am using a listener interface on my first Recycler so that when the quantity is changed and the item is added to the map. The adapter of the second recycler is notified of the changes. below is the code i am using to update the second recycler view.
public void updateList(){
mMap = ((UserMainActivity)getActivity()).getItemMap();
inputs.clear();
// adapter.notifyDataSetChanged();
adapter = new MyCartAdapter(inputs,getContext());
cartList.setAdapter(adapter);
for(AllItems t:mMap.values()) {
inputs.add(t);
}
adapter.notifyDataSetChanged();
}
Below is my second recycler view's adapter. Where I am changing the quantities of the selected items.
public class MyCartAdapter extends RecyclerView.Adapter<MyCartAdapter.MyCartViewHolder>{
private List<AllItems> listItems1;
private Context context;
private Typeface typeface;
public MyCartAdapter(List<AllItems> listItems1, Context context) {
this.listItems1 = listItems1;
this.context = context;
}
#NonNull
#Override
public MyCartAdapter.MyCartViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cart_items_layout, parent, false);
return new MyCartViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final MyCartAdapter.MyCartViewHolder holder, final int position) {
final AllItems orderItem = listItems1.get(position);
holder.setProductImage(orderItem.getImageUrl(),context);
holder.name.setText(orderItem.getName());
String price = String.valueOf(orderItem.getPrice());
holder.price.setText(price);
final HashMap<String, AllItems> items = ((UserMainActivity)context).getItemMap();
holder.counter.setText(orderItem.getQuantity());
holder.add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String quantity = String.valueOf(holder.counter.getText());
int count = Integer.parseInt(quantity)+1;
holder.counter.setText(String.valueOf(count));
String url = orderItem.getImageUrl();
AllItems newitem = new AllItems(orderItem.getName(),orderItem.getComname(),url, String.valueOf(count),orderItem.getWeight,orderItem.getPrice());
((UserMainActivity)context).addItem(orderitemname,newitem);
// notifyItemChanged(position);
}
});
//counter text iitem.textview showing the quantity of the selected item . integer count returns the value of counter text below i am checking if its zero than it simply sets the value to zero and else reduce it and update the map.
holder.minus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String counterText = String.valueOf(holder.counter.getText());
int count = Integer.parseInt(counterText);
if (count==0){
holder.counter.setText("0");
}
if (count==1){
holder.counter.setText("0");
AllItems item = items.get(orderItem.getName());
if (item!=null){
String orderit = orderItem.getName();
((UserMainActivity)context).removeItem(orderit);
// here i am removing the value from the list which throws the exception
listItems1.remove(position);
notifyItemRemoved(position);
}
}
if (count>1){
String quantity = String.valueOf(count-1);
holder.counter.setText(quantity);
String orderitemname = orderItem.getName();
String url = orderItem.getImageUrl();
String weight = "100";
long weightl = Long.parseLong(weight);
AllItems newitem = new AllItems(orderItem.getName(),orderItem.getComname(),url, quantity,weight,orderItem.getPrice());
((UserMainActivity)context).addItem(orderitemname,newitem);
// listItems1.set(position, newitem);
// notifyItemChanged(position);
}
}
});
}
#Override
public int getItemCount() {
return listItems1.size();
}
public class MyCartViewHolder extends RecyclerView.ViewHolder {
public TextView name,price,count,comname;
public TextView weight;
LinearLayout add,minus;
TextView counter;
public MyCartViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.ProName);
price = (TextView) itemView.findViewById(R.id.proPrice);
weight = (TextView) itemView.findViewById(R.id.ProWeight);
counter = (TextView) itemView.findViewById(R.id.counter);
add = (LinearLayout) itemView.findViewById(R.id.addLin);
minus= (LinearLayout) itemView.findViewById(R.id.minusLin);
}
public void setProductImage(final String thumb_image, final Context ctx){
productImage = (ImageView) itemView.findViewById(R.id.ProImage);
Picasso.with(ctx).setIndicatorsEnabled(false);
Picasso.with(ctx)
.load(thumb_image)
.networkPolicy(NetworkPolicy.OFFLINE)
.placeholder(R.drawable.basket_b).into(productImage, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(ctx).load(thumb_image).placeholder(R.drawable.basket).into(productImage);
}
});
}
public void setComname(String name){
comname = (TextView)itemView.findViewById(R.id.proComName);
comname.setText(name);
}
}
}
This jumps out at me:
listItems1.remove(position);
notifyItemChanged(position);
The notifyItemChanged() method exists to tell the adapter that the data at the given position has changed, and that the ViewHolder should be re-bound. This is not what you're doing; you're removing an item.
Probably your app is crashing because you're removing the last item in your data set (e.g. position 10) and then telling the adapter that the item at position 10 has changed... but now the maximum position in your data set is 9.
Instead, you should use the notifyItemRemoved() method.
listItems1.remove(position);
notifyItemRemoved(position);
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
I am trying to load a list in RecyclerView and show the first row of the list as selected. I have achieved it using the following code:
#Override
public void onBindViewHolder(NavigationDrawerAdapter.ViewHolder holder, final int position) {
if (!mNavClassrooms.get(position).equals("")) {
holder.mTextViewClassroom.setText(mNavClassrooms.get(position)); // Setting the Text with the array of our Titles
holder.mRelLayClassroom.setSelected(mSelectedItems.get(position, false));
/*
The following code was written to make the first item in the Classroom list as selected.
It leads to the item always being selected and hence has been commented out.
*/
if(position == 0 && intOldSelectedItem == -1){
holder.mRelLayClassroom.setSelected(mSelectedItems.get(position, true));
intOldSelectedItem = 0;
mSelectedView = holder.mRelLayClassroom.getChildAt(position);
mSelectedItems.put(position, true);
}
else{
holder.mRelLayClassroom.setSelected(mSelectedItems.get(position, false));
}
} else {
holder.mTextViewClassroom.setText("No classes found");
holder.mTextViewClassroom.setPadding(40, 0, 0, 0);
}
holder.mRelLayClassroom.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mSharedPreferences = mContext.getSharedPreferences(Constants.AAPREFERENCES, Context.MODE_PRIVATE);
String strClassroomValue = mNavClassrooms.get(position);
int strClassroomName = mNavClassroomNames.get(position);
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(Constants.CLASSROOM_VALUE, strClassroomValue);
editor.putInt(Constants.CLASSROOM_NAME, strClassroomName);
editor.commit();
/*
We are storing the position of the selected row in the SparseBooleanArray.
We delete it in case another row has been selected.
*/
if (mSelectedItems.get(position, false)) {
/*
Do nothing
*/
} else {
mSelectedItems.put(position, true);
/*
Making sure that the delete code is called only if some view is selected
*/
if (mSelectedView != null) {
mSelectedView.setSelected(false);
mSelectedItems.delete(intOldSelectedItem);
view.setSelected(false);
}
mSelectedView = view;
intOldSelectedItem = position;
view.setSelected(true);
}
}
However, now the first row stays selected always. I am unable to deselect it. I cannot seem to get this working.
I referred to the following answer to achieve most of this functionlaity.
https://stackoverflow.com/a/29984220/2186220
Any help will be appreciated.
I'm not answering your question by posting a fixed version of your onBindViewHolder method since it's kinda hard to understand and we don't know how the rest of your adapter looks like. So following a RecyclerView Adapter which does what you want: Selecting the first row by default and deselecting it once a other row is selected.
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
// ... other fields
// default selection position is the first one
private int selectedPosition = 0;
// ... constructor etc.
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
if(position == selectedPosition){
holder.itemView.setSelected(true);
} else {
holder.itemView.setSelected(false);
}
// Actual selection / deselection logic
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int currentPosition = holder.getLayoutPosition();
if(selectedPosition != currentPosition){
// Temporarily save the last selected position
int lastSelectedPosition = selectedPosition;
// Save the new selected position
selectedPosition = currentPosition;
// update the previous selected row
notifyItemChanged(lastSelectedPosition);
// select the clicked row
holder.itemView.setSelected(true);
}
}
});
// other adapter code
}
// other adapter stuff like onCreateViewHolder, getItemCount, ViewHolder etc.
}
Note: I guess there's no need to use a SparseBooleanArray so simply remove it and replace it with the int field used in the example above.
Initialize your
int intOldSelectedItem=0 and keep one boolean isVisible= false;
And do it as below:
if (holder.getPosition() == intOldSelectedItem) {
if (isVisible) {
//background for selected item
} else {
//background for unselected item
}
} else {
//background for unselected item
}
holder.mRelLayClassroom.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (intOldSelectedItem== holder.getPosition()) {
isVisible = !isVisible;
} else {
if (intOldSelectedItem!= 0) {
isVisible = false;
notifyItemChanged(intOldSelectedItem);
}
isVisible = true;
}
intOldSelectedItem= holder.getPosition();
notifyItemChanged(intOldSelectedItem);
}
});
I hope it might help you.
Add background selector to your ViewHolder layout.
Create your selector handler something like this:
public class SingleSelector {
private View oldVIew;
public void setSelection(View newView) {
if (oldVIew == null) {
newView.setSelected(true);
oldVIew = newView;
} else {
oldVIew.setSelected(false);
newView.setSelected(true);
oldVIew = newView;
}
}
}
Set default selection when you need it:
#Override
public void onBindViewHolder(SimpleViewHolder holder, int position) {
if (position == 0) {
singleSelector.setSelection(holder.itemView);
}
}
In your ViewHolder add listener to itemView and pass it to the handler:
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
singleSelector.setSelection(itemView);
}
});
I have a RecyclerView that will contain list of item retrieved from the internet. So at first, the list will be empty. After the data retrieved from the internet, it will update the list and call notifyDataSetChanged().
I can adapt the data into the RecyclerView just fine. But, I have an ImageButton for each of item which has different Image if it's clicked. If I initialize the flags array inside onBindViewHolder, each time I scrolled the RecyclerView, the flag array will be reinitialize to false. If I initialize it in the Adapter constructor, it will be 0 index since the list will be empty at first. Where should I put array initializing in adapter if the data will come at some amount of time later?
Below is my code, but the flag array (isTrue) is always reinitialize each time I scrolled my RecyclerView.
public class SomethingAdapter extends RecyclerView.Adapter<SomethingAdapter.ViewHolder> {
private ArrayList<String> someList;
private boolean[] isTrue;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView someText;
public ImageButton someButton;
public ViewHolder(View v) {
super(v);
someText = (TextView) v.findViewById(R.id.text);
someButton = (ImageButton) v.findViewById(R.id.button);
}
}
public SomethingAdapter(ArrayList<String> someList) {
this.someList = someList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
//TODO: This thing will make isTrue always reinitialize if scrolled
this.isTrue = new boolean[someList.getResults().size()];
viewHolder.someText.setText(someList.get(position));
if (isTrue[position]) {
viewHolder.someButton.setImageResource(R.drawable.button_true);
} else {
viewHolder.someButton.setImageResource(R.drawable.button_false);
}
viewHolder.someButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isTrue[position]) {
//Connect to the internet and if response is positive {
//isTrue[position] = false;
//viewHolder.someButton.setImageResource(R.drawable.button_false);
//}
} else {
//Connect to the internet and if response is positive {
//isTrue[position] = true;
//viewHolder.someButton.setImageResource(R.drawable.button_true);
//}
}
}
});
}
#Override
public int getItemCount() {
return someList.size();
}
Initialize it when you add items to someList.
Also, don't add click listener in your onBind, create it in onCreateViewHolder. You cannot use position in the click callback, instead you should be using ViewHolder#getAdapterPosition.
See docs for details:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onBindViewHolder(VH, int)
I have a list view in left of the screen and on click of the item i want to update a text on the right half of the screen, what i want to do here is that to move the clicked item in center of the listview. Like if the item is on top and i click on it it automatically moves to the center of the list view, how I can do this? Any kind of help will be appreciated.
I have a listview in which 7 items are visible and on startup 4th item will be selected as this is in center of the visible items in listview and if there are n items and whichever item is selected by user will be in center of the visible items in listview. Like i have 10 items and on start 4th is selected and when user selects the 3rd item, nth item from listview should come to index zero and and 3rd will come to position 4. Similarly for every other selected item? Can any one provide a code snippet for this?
Change items order in ListView source Array and then call notifyDataSetChanged() in ListView Adapter
EDIT: Code sample
public class ListAdapter extends BaseAdapter{
private Activity activity;
private ArrayList<ListRowObject> listItems;
public ListAdapter(Activity activity){
this.activity = activity;
listItems = new ArrayList<ListRowObject>();
}
public void addItem(ListRowObject item){
listItems.add(item);
notifyDataSetChanged();
}
public void addItems(ArrayList<ListRowObject> items){
listItems = items;
notifyDataSetChanged();
}
public void clear(){
listItems = null;
listItems = new ArrayList<ListRowObject>();
notifyDataSetChanged();
}
#Override
public int getCount() {
return listItems.size();
}
#Override
public Object getItem(int position) {
return listItems.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder holder;
if(convertView == null){
holder = new ViewHolder();
convertView = activity.getLayoutInflater().inflate(R.layout.list_row, null);
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.bgLayout = (LinearLayout) convertView.findViewById(R.id.bgLayout);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
ListRowObject row = listItems.get(position);
if(row.isSelected())
holder.bgLayout.setBackgroundColor(Color.GRAY);
else
holder.bgLayout.setBackgroundColor(Color.WHITE);
holder.text.setText(row.getText());
return convertView;
}
}
//--------
public class ListRowObject {
private String text;
private int positionInList;
private boolean isSelected;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getPositionInList() {
return positionInList;
}
public void setPositionInList(int positionInList) {
this.positionInList = positionInList;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
//------
public class Main extends Activity {
private ListView listView;
private ListAdapter adapter;
private Activity activity;
private ArrayList<ListRowObject> items;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
activity = this;
initializeFormViews();
initializeOnClickEvents();
fillList();
}
private void initializeFormViews(){
listView = (ListView) findViewById(R.id.listView);
}
private void initializeOnClickEvents(){
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Toast.makeText(activity, "Pressed " +position, Toast.LENGTH_SHORT).show();
// unselect all rows
for(ListRowObject item : items){
item.setSelected(false);
}
int first = adapterView.getFirstVisiblePosition();
int last = adapterView.getLastVisiblePosition();
int centerPosition = (first + last) / 2;
// change bg for centerPosition row
adapterView.getChildAt(centerPosition).findViewById(R.id.bgLayout).setBackgroundColor(Color.GRAY);
changeItems(position, centerPosition);
}
});
}
private void changeItems(int pressedPosition, int centerPosition){
ListRowObject centerRow = items.get(centerPosition);
ListRowObject pressedRow = items.get(pressedPosition);
pressedRow.setSelected(true);
centerRow.setSelected(false);
items.remove(centerPosition);
items.add(centerPosition, pressedRow);
items.remove(pressedPosition);
items.add(pressedPosition, centerRow);
adapter.clear();
adapter.addItems(items);
}
private void fillList(){
adapter = new ListAdapter(activity);
items = new ArrayList<ListRowObject>();
items = getItems();
for(ListRowObject item : items){
adapter.addItem(item);
}
listView.setAdapter(adapter);
}
private ArrayList<ListRowObject> getItems(){
ArrayList<ListRowObject> result = new ArrayList<ListRowObject>();
for(int i = 0; i < 15; i++){
ListRowObject object = new ListRowObject();
object.setPositionInList(i);
object.setText("Item #" + i);
if(i != 4)
object.setSelected(false);
else
object.setSelected(true);
result.add(object);
}
return result;
}
}
//------
public class ViewHolder {
public TextView text;
public LinearLayout bgLayout;
}
list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="#+id/bgLayout">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="#+id/text"
android:textColor="#000000"
android:textSize="24dp"
android:gravity="center"/>
</LinearLayout>
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/listView"/>
</LinearLayout>
when you create ArrayAdapter for your listview you send a ListArray to it.when you want change content .you only change this listArray then when click your item you can change ListArray and call notifyDataSetChanged(); method your adapter.