android Recyclerview removing item does not update adapter's get item count - android

I have a RecyclerView with swiping feature to reveal a delete and edit button.
I added: adapter.notifyItemRemoved(position) and this:
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
when the revealed delete button is clicked, the animation for removing the item works and
The item is deleted from my database
BUT then the deleted item re-appears in my recyclerview. When I change activity and go back, to the activity with the recyclerview, the list that I should be seeing is good.
If I remove the "notifyItemRangeChanged" code, the list updates with the last item repeated.
I think it is my Adapter's getItemCount not properly updating. so what I tried differently was to call my method that generates the list in the first place. This did the trick BUT my remove item animation is gone now because I guess it just skips to re-generate the list....
Any ideas?
Thank you in advanced for your feedback!
****************** UPDATE - ADDING ADAPTER CLASS CODE ****************
public class RVCategoryAdapter extends RecyclerView.Adapter {
Context context;
List categoryItemList;
public RVCategoryAdapter(Context context, List<CategoryItem> categoryItemList) {
this.context = context;
this.categoryItemList = categoryItemList;
}
#NonNull
#Override
public CategoryViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(R.layout.category_item_layout, parent, false);
return new CategoryViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull CategoryViewHolder holder, final int position) {
final int categoryID;
final String categoryTitle;
Glide.with(context).load(categoryItemList.get(position).getImage()).into(holder.ivCategoryIcon);
holder.txtCatID.setText(""+categoryItemList.get(position).getCategoryID());
holder.txtCategoryTitle.setText(categoryItemList.get(position).getTitle());
holder.txtCategoryDesc.setText(categoryItemList.get(position).getDescription());
categoryID = Integer.parseInt(holder.txtCatID.getText().toString());
categoryTitle = holder.txtCategoryTitle.getText().toString();
holder.cardViewItemLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, NotesListActivity.class);
intent.putExtra("CategoryID", categoryID);
intent.putExtra("CategoryTitle", categoryTitle);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return categoryItemList.size();
}
}

In your swipe delete button click listener remove your item from the list, too.
I would suggest you to add delete function in your adapter. Then in that method delete your item from list and call notifyItemRemoved.
public void delete(int position){
categoryItemList.remove(position);
notifyItemRemoved(position);
}

Related

how to set color the selected Item of Recycler View From Activity? [duplicate]

How to change the background color of only selected view in my recycle view example?only the background color of clicked itemview needs to be changed.
Only one selected item must be displayed with background color change at a time and the rest needs to be as before selecting.
here is my code :
MainActivity
public class MainActivity extends AppCompatActivity {
RecyclerView rv1;
private final String android_versions[]={
"Donut",
"Eclair",
"Froyo",
"Gingerbread",
"Honeycomb",
"Ice Cream Sandwich",
"Jelly Bean",
"KitKat",
"Lollipop",
"Marshmallow"
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews(){
rv1=(RecyclerView)findViewById(R.id.recyclerView1);
rv1.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager=new LinearLayoutManager(getApplicationContext());
rv1.setLayoutManager(layoutManager);
RecyclerDataAdapter rda=new RecyclerDataAdapter(rv1,getApplicationContext(),android_versions);
rv1.setAdapter(rda);
}
}
RecyclerDataadapter
public class RecyclerDataAdapter extends RecyclerView.Adapter<RecyclerDataAdapter.ViewHolder> {
private String android_versionnames[];
private Context context1;
private RecyclerView mRecyclerView;
public RecyclerDataAdapter(RecyclerView recylcerView,Context context,String android_versionnames[]){
this.android_versionnames=android_versionnames;
this.context1=context;
mRecyclerView=recylcerView;
setHasStableIds(true);
System.out.println("Inside dataadapter,Android names : \n ");
for(int i=0;i<android_versionnames.length;i++){
System.out.println("\n"+android_versionnames[i]);
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.tv1.setText(android_versionnames[position]);
}
#Override
public int getItemCount() {
return android_versionnames.length;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView tv1;
LinearLayout row_linearlayout;
RecyclerView rv2;
public ViewHolder(final View itemView) {
super(itemView);
tv1=(TextView)itemView.findViewById(R.id.txtView1);
row_linearlayout=(LinearLayout)itemView.findViewById(R.id.row_linrLayout);
rv2=(RecyclerView)itemView.findViewById(R.id.recyclerView1);
/*itemView.setBackgroundColor(0x00000000);//to transparent*/
}
}
}
Finally, I got the answer.
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.tv1.setText(android_versionnames[position]);
holder.row_linearlayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
row_index=position;
notifyDataSetChanged();
}
});
if(row_index==position){
holder.row_linearlayout.setBackgroundColor(Color.parseColor("#567845"));
holder.tv1.setTextColor(Color.parseColor("#ffffff"));
}
else
{
holder.row_linearlayout.setBackgroundColor(Color.parseColor("#ffffff"));
holder.tv1.setTextColor(Color.parseColor("#000000"));
}
}
here 'row_index' is set as '-1' initially
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView tv1;
LinearLayout row_linearlayout;
RecyclerView rv2;
public ViewHolder(final View itemView) {
super(itemView);
tv1=(TextView)itemView.findViewById(R.id.txtView1);
row_linearlayout=(LinearLayout)itemView.findViewById(R.id.row_linrLayout);
rv2=(RecyclerView)itemView.findViewById(R.id.recyclerView1);
}
}
A really simple way to achieve this would be:
//instance variable
List<View>itemViewList = new ArrayList<>();
//OnCreateViewHolderMethod
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row, parent, false);
final MyViewHolder myViewHolder = new MyViewHolder(itemView);
itemViewList.add(itemView); //to add all the 'list row item' views
//Set on click listener for each item view
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
for(View tempItemView : itemViewList) {
/** navigate through all the itemViews and change color
of selected view to colorSelected and rest of the views to colorDefault **/
if(itemViewList.get(myViewHolder.getAdapterPosition()) == tempItemView) {
tempItemView.setBackgroundResource(R.color.colorSelected);
}
else{
tempItemView.setBackgroundResource(R.color.colorDefault);
}
}
}
});
return myViewHolder;
}
UPDATE
The method above may ruin some default attributes of the itemView, in my case, i was using CardView, and the corner radius of the card was getting removed on click.
Better solution:
//instance variable
List<CardView>cardViewList = new ArrayList<>();
public class MyViewHolder extends RecyclerView.ViewHolder {
CardView cardView; //THIS IS MY ROOT VIEW
...
public MyViewHolder(View view) {
super(view);
cardView = view.findViewById(R.id.row_item_card);
...
}
}
#Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
final OurLocationObject locationObject = locationsList.get(position);
...
cardViewList.add(holder.cardView); //add all the cards to this list
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//All card color is set to colorDefault
for(CardView cardView : cardViewList){
cardView.setCardBackgroundColor(context.getResources().getColor(R.color.colorDefault));
}
//The selected card is set to colorSelected
holder.cardView.setCardBackgroundColor(context.getResources().getColor(R.color.colorSelected));
}
});
}
UPDATE 2 - IMPORTANT
onBindViewHolder method is called multiple times, and also every time the user scrolls the view out of sight and back in sight!
This will cause the same view to be added to the list multiple times which may cause problems and minor delay in code executions!
To fix this,
change
cardViewList.add(holder.cardView);
to
if (!cardViewList.contains(holder.cardView)) {
cardViewList.add(holder.cardView);
}
I can suggest this solution, which I used in my app. I've placed this code of onTouchListener in my ViewHolder class's constructor. itemView is constructor's argument. Be sure to use return false on this method because this need for working OnClickListener
itemView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
v.setBackgroundColor(Color.parseColor("#f0f0f0"));
}
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL)
{
v.setBackgroundColor(Color.TRANSPARENT);
}
return false;
}
});
Create Drawable file in Drawable foloder
<item android:drawable="#color/SelectedColor" android:state_pressed="true"></item>
<item android:drawable="#color/SelectedColor" android:state_selected="true"></item>
<item android:drawable="#color/DefultColor"></item>
And in xml file
android:background="#drawable/Drawable file"
In RecyclerView onBindViewHolder
holder.button.setSelected(holder.button.isSelected()?true:false);
Like toggle button
I was able to change the selected view color like this. I think this is the SIMPLE WAY (because you don't have to create instance of layouts and variables.
MAKE SURE YOU DONT GIVE ANY BACKGROUND COLOR INSIDE YOUR RECYCLER VIEW's TAG.
holder.itemView.setBackgroundColor(Color.parseColor("#8DFFFFFF"));
onBindViewHolder() method is given below
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
holder.item_1.setText(list_items.get(position).item_1);
holder.item_2.setText(list_items.get(position).item_2);
holder.select_cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked){
holder.itemView.setBackgroundColor(Color.parseColor("#8DFFFFFF"));
}else {
holder.itemView.setBackgroundColor(Color.parseColor("#FFFFFF"));
}
}
});
}
What I did to achieve this was actually taking a static variable to store the last clicked position of the item in the RecyclerView and then notify the adapter to update the layout at the position on the last clicked position i.e. notifyItemChanged(lastClickedPosition) whenever a new position is clicked. Calling notifyDataSetChanged() on the whole layout is very costly and unfeasible so doing this for only one position is much better.
Here's the code for this:
public class RecyclerDataAdapter extends RecyclerView.Adapter<RecyclerDataAdapter.ViewHolder> {
private String android_versionnames[];
private Context mContext;
private static lastClickedPosition = -1; // Variable to store the last clicked item position
public RecyclerDataAdapter(Context context,String android_versionnames[]){
this.android_versionnames = android_versionnames;
this.mContext = context;
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.row_layout,
parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.tv1.setText(android_versionnames[position]);
holder.itemView.setBackgroundColor(mContext.getResources().
getColor(R.color.cardview_light_background));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.setBackgroundColor(mContext.getResources().
getColor(R.color.dark_background));
if (lastClickedPosition != -1)
notifyItemChanged(lastClickedPosition);
lastClickedPosition = position;
}
});
}
#Override
public int getItemCount() {
return android_versionnames.length;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView tv1;
public ViewHolder(final View itemView) {
super(itemView);
tv1=(TextView)itemView.findViewById(R.id.txtView1);
}
}
}
So we will be actually updating only the intended item and not re-running unnecessary updates to the items which have not even been changed.
If you use kotlin, it's really simple.
In your RecyclerAdapter class
userV.invalidateRecycler()
holder.card_User.setCardBackgroundColor(Color.parseColor("#3eb1ae").withAlpha(60))
In your fragment or Activity
override fun invalidateRecycler() {
if (v1.recyclerCompanies.childCount > 0) {
v1.recyclerCompanies.childrenRecursiveSequence().iterator().forEach { card ->
if (card is CardView) {
card.setCardBackgroundColor(Color.WHITE)
}
}
}
}
There is a very simple solution to this, you don't have to work in the adapter. To change the background of a clicked item in the RecyclerView you need to catch the click in the adapter using an iterface:
interface ItemClickListener {
fun onItemClickListener(item: Item, position: Int)
}
When we click we will get the item and the items position. In our bind function in the adapter we will set the on click listener:
container.setOnClickListener {
onClickListener.onItemClickListener(item, position)
}
In your activity you will then implement this interface:
class MainActivity : AppCompatActivity(), ItemAdapter.ItemClickListener {
Next we need to implement the background changing logic on item click. The logic is this: when the user clicks on an item, we check if the background on the clicked item is white (the item is not previously clicked) and if this condition is true, we change the background on all of the items in the RecyclerView to white (to invalidate previously clicked and marked items if there are any) and then change the background color of the clicked item to teal to mark it. And if the background of the clicked item is teal (which means the user clicks again on the same previously marked item), we change the background color on all of the items to white. First we will need to get our item background color as a ColorDrawable. We will use an iterator function to go through all of the items (children) of the RecyclerView and forEach() function to change the background on everyone of them. This method will look like this:
override fun onItemClickListener(item: Item, position: Int) {
val itemBackground: ColorDrawable =
binding.recycler[position].background as ColorDrawable
if (itemBackground.color == ContextCompat.getColor(this, R.color.white)) {
binding.recycler.children.iterator().forEach { item ->
item.setBackgroundColor(
ContextCompat.getColor(
this,
R.color.white
)
)
}
binding.recycler[position].setBackgroundColor(
ContextCompat.getColor(this, R.color.teal_200)
)
} else {
binding.recycler.children.iterator().forEach { item ->
item.setBackgroundColor(
ContextCompat.getColor(
this,
R.color.white
)
)
}
}
}
So now you change the background on item click, if you click the same item, you will change the background back to what it was before.
My solution:
public static class SimpleItemRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder> {
private final MainActivity mParentActivity;
private final List<DummyContent.DummyItem> mValues;
private final boolean mTwoPane;
private static int lastClickedPosition=-1;
**private static View viewOld=null;**
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
DummyContent.DummyItem item = (DummyContent.DummyItem) view.getTag();
if (mTwoPane) {
Bundle arguments = new Bundle();
arguments.putString(ItemDetailFragment.ARG_ITEM_ID, item.id);
ItemDetailFragment fragment = new ItemDetailFragment();
fragment.setArguments(arguments);
mParentActivity.getSupportFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment)
.commit();
} else {
Context context = view.getContext();
Intent intent = new Intent(context, ItemDetailActivity.class);
intent.putExtra(ItemDetailFragment.ARG_ITEM_ID, item.id);
context.startActivity(intent);
}
**view.setBackgroundColor(mParentActivity.getResources().getColor(R.color.SelectedColor));
if(viewOld!=null)
viewOld.setBackgroundColor(mParentActivity.getResources().getColor(R.color.DefaultColor));
viewOld=view;**
}
};
viewOld is null at the beginning, then points to the last selected view.
With onClick you change the background of the selected view and redefine the background of the penultimate view selected.
Simple and functional.
In your adapter class make Integer variable as index and assign it to "0" (if you want to select 1st item by default, if not assign "-1").Then on your onBindViewHolder method,
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
holder.texttitle.setText(listTitle.get(position));
holder.itemView.setTag(listTitle.get(position));
holder.texttitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = position;
notifyDataSetChanged();
}
});
if (index == position)
holder.texttitle.setTextColor(mContext.getResources().getColor(R.color.selectedColor));
else
holder.texttitle.setTextColor(mContext.getResources().getColor(R.color.unSelectedColor));
}
Thats it and you are good to go.in If condition true section place your selected color or what ever you need, and else section place unselected color or what ever.
Calling Notifydatasetchanged May be expensive when you need to change one item We can overcome by saving the old position and call notifyItemChanged
var old_postion=-1
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.tv1.setText(android_versionnames[position]);
holder.row_linearlayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
notifyItemChanged(old_position)
//After item change happens set the old_postion as current position
old_position=position
}
});
if(old_position==position){
holder.row_linearlayout.setBackgroundColor(Color.parseColor("#567845"));
holder.tv1.setTextColor(Color.parseColor("#ffffff"));
}
else
{
holder.row_linearlayout.setBackgroundColor(Color.parseColor("#ffffff"));
holder.tv1.setTextColor(Color.parseColor("#000000"));
}
}
Create a selector into Drawable folder:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#color/blue" />
</shape>
</item>
<item android:state_pressed="false">
<shape>
<solid android:color="#android:color/transparent" />
</shape>
</item>
</selector>
Add the property into your xml (where you declare the RecyclerView):
android:background="#drawable/selector"
Add click listener for item view in .onBindViewHolder() of your RecyclerView's adapter. get currently selected position and change color by .setBackground() for previously selected and current item
Most Simpler Way From My Side is to Add a variable in adapterPage as last Clicked Position.
in onBindViewHolder paste this code which checks for last stored position matched with loading positions
Constants is the class where i declare my global variables
if(Constants.LAST_SELECTED_POSITION_SINGLE_PRODUCT == position) {
//change the view background here
holder.colorVariantThumb.setBackgroundResource(R.drawable.selected_background);
}
//on view click you store the position value and notifyItemRangeChanged will
// call the onBindViewHolder and will check the condition
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view){
Constants.LAST_SELECTED_POSITION_SINGLE_PRODUCT=position;
notifyItemRangeChanged(0, mColorVariants.size());
}
});
I managed to do this from my Activity where i'm setting my Rv and not from the adapter
If someone need to do something similar here's the code
In this case the color changes on a logClick
#Override
public void onLongClick(View view, int position) {
Toast.makeText(UltimasConsultasActivity.this, "Item agregado a la lista de mails",
Toast.LENGTH_SHORT).show();
sendMultipleMails.setVisibility(View.VISIBLE);
valueEmail.setVisibility(View.VISIBLE);
itemsSeleccionados.setVisibility(View.VISIBLE);
listaEmails.add(superListItems.get(position));
listaItems ="";
NameOfyourRecyclerInActivity.findViewHolderForAdapterPosition(position).NameOfYourViewInTheViewholder.setBackgroundColor((Color.parseColor("#336F0D")));
for(int itemsSelect = 0; itemsSelect <= listaEmails.size() -1; itemsSelect++){
listaItems += "*"+listaEmails.get(itemsSelect).getDescripcion() + "\n";
}
itemsSeleccionados.setText("Items Seleccionados : "+ "\n" + listaItems);
}
}));
My Solution
With my solution I'm not using notifyDataSetChanged(), because annoying whenever item is clicked, all the items from list got refreshed. To tackle this problem, I used notifyItemChanged(position); This will only change the selected item.
Below I have added the code of my omBindViewHolder.
private int previousPosition = -1;
private SingleViewItemBinding previousView;
#Override
public void onBindViewHolder(#NonNull final ItemViewHolder holder, final int position) {
holder.viewBinding.setItem(itemList.get(position));
holder.viewBinding.rlContainerMain.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
clickRecyclerView.clickRecyclerItem(position, 0);
previousPosition = position;
notifyItemChanged(position);
if(previousView != null){
previousView.rlContainerMain.setBackgroundColor(
ContextCompat.getColor(context, R.color.colorGrayLight));
}
}
});
if(position == previousPosition){
previousView = holder.viewBinding;
holder.viewBinding.rlContainerMain.setBackgroundColor(
ContextCompat.getColor(context, R.color.colorPrimary));
}
else {
holder.viewBinding.rlContainerMain.setBackgroundColor(
ContextCompat.getColor(context, R.color.colorGrayLight));
}
}
in the Kotlin you can do this simply:
all you need is to create a static variable like this:
companion object {
var last_position = 0
}
then in your onBindViewHolder add this code:
holder.item.setOnClickListener{
holder.item.setBackgroundResource(R.drawable.selected_item)
notifyItemChanged(last_position)
last_position=position
}
which item is the child of recyclerView which you want to change its background after clicking on it.
I made this implementation in kotlin I thing is not very efficient but works
ivIsSelected is a ImageView that represent in my case a check mark
var selectedItems = mutableListOf<Int>(-1)
override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
// holder.setData(ContactViewModel, position) // I'm passing this to the ViewHolder
holder.itemView.setBackgroundColor(Color.WHITE)
holder.itemView.ivIsSelected.visibility = INVISIBLE
selectedItems.forEach {
if (it == position) {
holder.itemView.setBackgroundColor(Color.argb(45, 0, 255, 43))
holder.itemView.ivIsSelected.visibility = VISIBLE
}
}
holder.itemView.setOnClickListener { it ->
it.setBackgroundColor(Color.BLUE)
selectedItems.add(position)
selectedItems.forEach { selectedItem -> // this forEach is required to refresh all the list
notifyItemChanged(selectedItem)
}
}
}
A faster and simpler way is saving the previous View element selected, so you don't have to use notifyDataSetChanged() or notifyItemChanged(position).
First, add an instance variable inside your Adapter (RecyclerDataAdapter):
View prevElement;
Then, inside your function onClick() (or in my case the lambda function version) you insert this:
holder.itemView.setOnClickListener(v -> {
// CODE TO INSERT
if (prevElement != null)
prevElement.setBackgroundColor(Color.TRANSPARENT);
v.setBackgroundColor(R.color.selected);
prevElement = v;
// DO SOMETHING
...
});
As you can see, the first thing done is checking if the prevElement is not null (an element was clicked before this), so we change its background color to Color.TRANSPARENT (even if it's the same element clicked twice). Then, we set the background color of the View element clicked (v) is changed to R.color.selected. Finally set the element clicked to the prevElement variable, so it can be modified in the next click action.
The response from #Sudhanshu Vohra above was the best in my case and much simpler.
I did minor changes to handle the new selection and previous selection to adjust the display.
I modified it as:
//Handle selected item and previous selection
if (lastSelectedIndex != -1) {
notifyItemChanged(lastSelectedIndex);
}
notifyItemChanged(bindingAdapterPosition);
lastSelectedIndex = bindingAdapterPosition;
Now I refresh only two items, rather than the entire list and it works like a charm. Thank you.
I got it like this
public void onClick(View v){
v.findViewById(R.id.textView).setBackgroundColor(R.drawable.selector_row);
}
Thanks
я не знаю на сколько это поможет но я типа так сделал:) в адаптере #Override public void onBindViewHolder(#NonNull NoteViewHolder holder, int position) { holder.bind(sortedList.get(position)); holder.itemView.setBackgroundResource(R.drawable.bacground_button); }

Change value in one recyclerview from selection of another recyclerview

I have two recyclerviews in same activity I want value of first item to changed as soon as I click on the item of 2nd recyclerView
I have been able to retrieve the selected value of 2nd recyclerView
I want bank icon to be changed on the selection of color
It gets changed when I reselect or select a new Icon
As for your requirement you have to use onclicklistner in your both recycleview holder and pass callback to both adapter.
your call back interface is:
interface Result(){
public setposition(int position);
}
public BankAdapter(Context context,Arralist<Bank> mList,Result result){
this.context=context;
this.mList=mList;
this.result=result;
}
private final OnClickListener mOnClickListener = new MyOnClickListener();
#Override
public MyViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.myview, parent, false);
view.setOnClickListener(mOnClickListener);
return new MyViewHolder(view);
}
The onClick method:
#Override
public void onClick(final View view) {
int itemPosition = mRecyclerView.getChildLayoutPosition(view);
result.setposition(itemPosition);
}
In your Main activity:
private int selectedBank= -1;
private BankAdapter bankAdapter;
private ColorAdapter colorAdapter;
bankAdapter=new BankAdapter(this, listBank, new Result(){
#Override
public void onClick(,int position) {
selectedBank=position;
}
});
bankRecyleView.setAdapter(bankAdapter);
Same code use in your ColorReycleView Adapter for detecting item click:
colorAdapter=new ColorAdapter(this, listColor, new Result(){
#Override
public void onClick(View v,int position) {
if(selectedBank==-1)
{
Toast.makeText(this, "Select bank first", Toast.LENGTH_LONG).show();
}
else{
//// Do your code here
/* here you get your color postion selected
remove item from bank list and change with selected color image as per your list
listBank.add(selectedBank,"changed color image");
bankAdapter.notifyItemChanged();
*/
}
}
});
colorRecyleView.setAdapter();
Hope it helps.

How should I initialize an array inside RecyclerView Adapter which has the size of a list while the list will not available on Adapter construction?

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)

How is the position of a RecyclerView adapter related to the index of its dataset?

I thought they were the same, but they're not. The following code gives an indexOutOfBounds exception when I try to access the "position" index of my dataset, in this case a list of a model I created called Task:
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskViewHolder> {
private List<Task> taskList;
private TaskAdapter thisAdapter = this;
// cache of views to reduce number of findViewById calls
public static class TaskViewHolder extends RecyclerView.ViewHolder {
protected TextView taskTV;
protected ImageView closeBtn;
public TaskViewHolder(View v) {
super(v);
taskTV = (TextView)v.findViewById(R.id.taskDesc);
closeBtn = (ImageView)v.findViewById(R.id.xImg);
}
}
public TaskAdapter(List<Task> tasks) {
if(tasks == null)
throw new IllegalArgumentException("tasks cannot be null");
taskList = tasks;
}
// onBindViewHolder binds a model to a viewholder
#Override
public void onBindViewHolder(TaskViewHolder taskViewHolder, int pos) {
final int position = pos;
Task currTask = taskList.get(pos);
taskViewHolder.taskTV.setText(currTask.getDescription());
**taskViewHolder.closeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("TRACE", "Closing task at position " + position);
// delete from SQLite DB
Task taskToDel = taskList.get(position);
taskToDel.delete();
// updating UI
taskList.remove(position);
thisAdapter.notifyItemRemoved(position);
}
});**
}
#Override
public int getItemCount() {
//Log.d("TRACE", taskList.size() + " tasks in DB");
return taskList.size();
}
// inflates row to create a viewHolder
#Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int pos) {
View itemView = LayoutInflater.from(parent.getContext()).
inflate(R.layout.list_item, parent, false);
Task currTask = taskList.get(pos);
//itemView.setBackgroundColor(Color.parseColor(currTask.getColor()));
return new TaskViewHolder(itemView);
}
}
Deleting from my recyclerview gives unexpected results sometimes. Sometimes the element ahead of the one clicked is deleted, other times an indexOutOfBounds exception occurs at "taskList.get(position)".
Reading https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html and https://developer.android.com/training/material/lists-cards.html did not give me any more insight into why this was happening and how to fix it.
It looks like RecyclerView recycles the rows, but I wouldn't expect an indexoutofbounds exception using a smaller subset of numbers to index my list.
RecyclerView does not rebind views when their positions change (for obvious performance reasons).
For example, if your data set looks like this:
A B C D
and you add item X via
mItems.add(1, X);
notifyItemInserted(1, 1);
to get
A X B C D
RecyclerView will only bind X and run the animation.
There is a getPosition method in ViewHolder but that may not match adapter position if you call it in the middle of an animation.
If you need the adapter position, your safest option is getting the position from the Adapter.
update for your comment
Add a Task field to the ViewHolder.
Change onCreateViewHolder as follows to avoid creating a listener object on each rebind.
// inflates row to create a viewHolder
#Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int type) {
View itemView = LayoutInflater.from(parent.getContext()).
inflate(R.layout.list_item, parent, false);
final TaskViewHolder vh = new TaskViewHolder(itemView);
taskViewHolder.closeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// delete from SQLite DB
Task taskToDel = vh.getTask();
final int pos = taskList.indexOf(taskToDel);
if (pos == -1) return;
taskToDel.delete();
// updating UI
taskList.remove(pos);
thisAdapter.notifyItemRemoved(pos);
}
});
}
so in your on bind method, you do
// onBindViewHolder binds a model to a viewholder
#Override
public void onBindViewHolder(TaskViewHolder taskViewHolder, int pos) {
Task currTask = taskList.get(pos);
taskViewHolder.setTask(currTask);
taskViewHolder.taskTV.setText(currTask.getDescription());
}
Like yigit said, RecyclerView works like that:
A B C D
and you add item X via
mItems.add(1, X);
notifyItemInserted(1, 1);
you get
A X B C D
Using holder.getAdapterPosition() in onClickListener() will give you the right item from dataset to be removed, not the "static" view position. Here's the doc about it onBindViewHolder
Why dont you use a public interface for the button click and controle the action in the MainActivity.
In your adapter add:
public interface OnItemClickListener {
void onItemClick(View view, int position, List<Task> mTaskList);
}
and
public OnItemClickListener mItemClickListener;
// Provide a suitable constructor (depends on the kind of dataset)
public TaskAdapter (List<Task> myDataset, OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
this.mDataset = mDataset;
}
plus the call in the ViewHolder class
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ViewHolder(View v) {
super(v);
...
closeBtn = (ImageView)v.findViewById(R.id.xImg);
closeBtn.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// If not long clicked, pass last variable as false.
mItemClickListener.onItemClick(v, getAdapterPosition(), mDataset);
}
}
In your MainActivity change your adapter to handle the call
// set Adapter
mAdapter = new TaskAdapter(taskList, new TaskAdapter.OnItemClickListener() {
#Override
public void onItemClick(View v, int position) {
if (v.getId() == R.id.xImg) {
Task taskToDel = taskList.get(position);
// updating UI
taskList.remove(position);
thisAdapter.notifyItemRemoved(position);
// remove from db with unique id to use delete query
// dont use the position but something like taskToDel.getId()
taskToDel.delete();
}
}
});
Thanks to #yigit for his answer, his solution mainly worked, I just tweaked it a little bit so as to avoid using vh.getTask() which I was not sure how to implement.
final ViewHolder vh = new ViewHolder(customView);
final KittyAdapter final_copy_of_this = this;
// We attach a CheckChange Listener here instead of onBindViewHolder
// to avoid creating a listener object on each rebind
// Note Rebind is only called if animation must be called on view (for efficiency)
// It does not call on the removed if the last item is checked
vh.done.setChecked(false);
vh.done.setOnCheckedChangeListener(null);
vh.done.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
buttonView.setEnabled(false);
final int pos2 = vh.getAdapterPosition(); // THIS IS HOW TO GET THE UPDATED POSITION
// YOU MUST UPDATE THE DATABASE, removed by Title
DatabaseHandler db = new DatabaseHandler(mContext);
db.remove(mDataSet.get(pos2).getTitle(), fp);
db.close();
// Update UI
mDataSet.remove(pos2);
final_copy_of_this.notifyItemRemoved(pos2);
}
});
Notice instead to get the updated position, you can call vh.getAdapterPosition(), which is the line that will give you the updated position from the underlying dataset rather than the fake view.
This is working for me as of now, if someone knows of a drawback to using this please let me know. Hope this helps someone.
Personally, I don't like this concept of RecyclerViews. Seems like it wasn't thought of completely.
As it was said when removing an item the Recycler view just hides an item. But usually you don't want to leave that item in your collection. When deleting an item from the collection "it shifts its elements towards 0" whereas recyclerView keeps the same size.
If you are calling taskList.remove(position); your position must be evaluated again:
int position = recyclerView.getChildAdapterPosition(taskViewHolder.itemView);

How to delete a custom listview item in android?

I have a listview and a button in my layout file. I'am adding items to listview on click of that button. The listview should be empty when the activity is started but it should grow by adding the items to it.
This is my code inside onCreate() :
list = (ListView)findViewById(R.id.inverterListView);
adapter = new ArrayAdapter<String>(InverterList.this, R.layout.inverters_list_row, R.id.inverterNumberTextViewInPanelListRow);
list.setAdapter(adapter);
And here iam adding the items to listview onclick of a button.
adapter.add(inverterNo);
adapter.notifyDataSetChanged();
This works fine. Can anyone guide me to delete custom listview item ? Thanks in advance.
If you know the position of the item you can do this:
Object item = adapter.getItem(position);
adapter.remove(item);
adapter.notifyDataSetChanged();
You may write your own adapter extends BaseAdapter and implement all you need methods.
It is example of my adapter:
public class PeopleUserAdapter extends BaseAdapter
{
private List<User> users;
private int viewResourceId;
private Context context;
public PeopleUserAdapter(Context context, int viewResourceId)
{
this.context = context;
this.viewResourceId = viewResourceId;
this.users = new ArrayList<User>();
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
UserItemHolder holder;
if (convertView == null)
{
convertView = LayoutInflater.from(context).inflate(viewResourceId, parent, false);
holder = new UserItemHolder(convertView);
}
else holder = (UserItemHolder) convertView.getTag();
User user = getItem(position);
holder.name.setText("#" + user.getLogin());
return convertView;
}
#Override
public int getCount()
{
return users.size();
}
#Override
public User getItem(int position)
{
return users.get(position);
}
#Override
public long getItemId(int position)
{
return getItem(position).hashCode();
}
public void clear()
{
users.clear();
}
public void addAll(Collection<User> users)
{
this.users.addAll(users);
notifyDataSetChanged();
}
public void replace(Collection<User> users)
{
clear();
addAll(users);
}
public static PeopleUserAdapter init(Context context)
{
return new PeopleUserAdapter(context, R.layout.item_user);
}
}
adapter.remove(item) .. and then call adapter.notifyDataSetChanged();
In case you are using a custom adapter (for a custom layout listview), you will want to do this:
When your Adapter is something like:
public class YourAdapterName extends ArrayAdapter<yourObject>
then the code for deleting the selected ListView Item will be:
ListView yourListView = (ListView) findViewById(R.id.listviewid);
YourAdapterName adapter;
adapter = (YourAdapterName) yourListView.getAdapter();
yourObject theitem = adapter.getItem(position);
adapter.remove(theitem);
adapte.notifyDataSetChanged();
This is assuming you are inside an event that gives you access to the current position inside the listview. like:
public boolean onItemLongClick(AdapterView<?> parent, View strings,int position, long id)
or
public void onItemClick(AdapterView<?> arg0, View v, int position, long id)
Otherwise you will need to obtain that position some other way, like storing it (onItemClick or onItemLongClick) in a textView with Visibility.GONE, and retrieve it when clicking the button (this is silly, you can use all kinds of storage options, like global variables, database and such).
Make sure you have overridden the remove method on your custom adapter
For example if this is your add method:
#Override
public void add(String[] object) {
scoreList.add(object);
super.add(object);
}
then your remove method would look something like this:
#Override
public void remove(String[] object) {
scoreList.remove(object);
super.remove(object);
}
call the below two lines::
adapter.remove(inverterNo);
adapter.notifyDataSetChanged();
where inverterNo is your item
It easy; you only to need is: add a method public in your personalize adapter some this:
public void remove(int position) {
itemsMovieModelFiltered.remove(position);
notifyDataSetChanged();
}
Remenber, this method you must add in your personalize adapter.
Then, call this method from other
adapte=new PersonalizeListAdapter(getActivity().getApplicationContext(),
movieModelList);
adapte.remove(position);

Categories

Resources