Recyclerview not reloading data when notifyDataSetChanged() is called - android

I am using recycler view in my application. I am initialising the adapter and recycler view inside onActivity Created as given below,
adapter = new CardsRecyclerAdapterInternal(WalletStoreFragment.this);
cardSummaryList = new ArrayList<LoyaltyCardSummary>();
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setHasFixedSize(true);
Now I am calling an API to load data for the recycler view. Here is my hierarchy
Fragment --> Fragment presenter --> API Layer
So my fragment invokes a method in presenter which in turns calls the API. The API returns data back to the presenter. I am using EventBus for that.
The fragment implements an interface given below,
public interface StoreView
{
void reloadCardList (List<CardSummary> cards);
void dismissCardLoad();
}
Presenter calls the interface method to reload the recycler view data, using the code given below.
#Override
public void reloadCardList(final List<CardSummary> cards)
{
cardSummaryList.clear();
cardSummaryList.addAll(cards);
adapter.notifyDataSetChanged();
recyclerView.invalidate();
}
Here is the code for adapter
public class CardsRecyclerAdapterInternal extends
RecyclerView.Adapter<CardsRecyclerAdapterInternal.ViewHolder>
{
private Fragment frag;
public CardsRecyclerAdapterInternal()
{
//this.frag = _frag;
}
public void setCardSummaryList(List<CardSummary> cards)
{
//this.cardSummaryList = cards;
}
#Override
public CardsRecyclerAdapterInternal.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
// Inflate the custom layout
View view = inflater.inflate(R.layout.fragment_wallet_store_item, parent, false);
// Return a new holder instance
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(CardsRecyclerAdapterInternal.ViewHolder holder, int position) {
CardSummary card = cardSummaryList.get(position);
holder.txt_CardName.setText(card.getCardName());
holder.txt_CardCategory.setText(card.getCardCategory());
String cardURL = Global.CARD_IMAGE + Uri.encode(card.getCardImage());
Glide
.with(WalletStoreFragment.this)
.load(cardURL)
.into(holder.cardImage);
}
#Override
public int getItemCount() {
return cardSummaryList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView txt_CardName;
TextView txt_CardCategory;
ImageView cardImage;
public ViewHolder(View convertView) {
super(convertView);
txt_CardName = (TextView)convertView.findViewById(R.id.txt_CardName);
txt_CardCategory = (TextView)convertView.findViewById(R.id.txt_CardCategory);
cardImage = (ImageView)convertView.findViewById(R.id.img_CardImage);
}
}
}
But this is not reloading the data in recycler view. Not even calling the getItemCount() inside the recycler view. Whats going wrong here?
****************************Update************************
I have more inputs to my issue. I am having a tabbed view pager aplication, and the recycler view is loaded from one of the tabs when a button is clicked. If I directly load the recycler view fragment in main TabPage everything works fine, but when its inside another view the recycler is not reloading. Here is how I load the new fragment,
FragmentManager manager = getFragmentManager();
Fragment fragment = new StoreFragment();
manager.beginTransaction()
.add(R.id.container, fragment)
.addToBackStack("STORE_FRAGMENT")
.commit();
and here is my container within the XML file,
<FrameLayout android:id="#+id/container" android:layout_width="match_parent"
android:layout_height="match_parent"/>
Thanks

The variable (List) cardSummaryListis not declared anywhere in the Adapter class. So the cardSummaryList.size(); won't return anything and if at all it should raise NullPointerException.
You need change your Adapter code by: including a local variable List<CardSummary> cardSummaryList; and then create a constructor that takes a List as one of its argument -
private List<CardSummary> cardSummaryList;
....
public CardsRecyclerAdapterInternal(List<CardSummary> cardsList)
{
cardSummaryList = cardsList;
}
you should also add a method that updates your list (call it something like reloadCards(List<CardSummary> cards) and then the body of this method is something like:
public void reloadCards(List<CardSummary> cards){
cardSummaryList.clear();
cardSummaryList.addAll(cards);
}
Finally, you then change your reloadCardList(final List<CardSummary> cards) code into:
public void reloadCardList(final List<CardSummary> cards)
{
adapter.reloadCards(cards);
adapter.notifyDataSetChanged();
recyclerView.invalidate();
}
Please give it a try and let me know if this helps you resolve your problem.
By the way, please look at an example of how to use RecyclerView - you can also look at the tutorial here on Creating Lists and Cards

Related

How to build an Arraylist of objects from toggle OnClickListeners inside RecyclerView Adapter's items

I'm building an Android app of media, and trying to add a Playlist feature to it, the user will be able to create a playlist of his own and modify it.
I'm using a RecyclerView to show the user list of songs which he can choose from.
The problem is I don't understand how to pass the Arraylist of chosen songs from the adapter to the fragment.
I've tried to use the Observer pattern but the don't know how to use that information.
This is my Fragment for creating the playlist:
public class CreatePlaylistFragment extends Fragment implements PlaylistAdapterInterface {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_create_playlist, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ArrayList<ProgramsData> dataArrayList = ProgramsReceiver.getPrograms();
ArrayList<ProgramsData> sortedList = new ArrayList<>(dataArrayList);
adapter = new CreatePlaylistAdapter(dataArrayList, view.getContext(), this);
adapter.adapterInterface = this;
ivCreatePlaylist.setOnClickListener(v -> {
Toast.makeText(v.getContext(), "Creating Playlist!", Toast.LENGTH_SHORT).show();
new PlaylistsJsonWriter(playlistArrayList,getContext()).execute();
});
}
#Override
public void OnItemClicked(ArrayList<ProgramsData> programs) {
programsToCreate = programs;
String s = etListName.getText().toString();
playlistArrayList.add(new Playlist(s, programsToCreate));
}
}
This is the Recycler Adapter with ViewHolder as inner class:
public class CreatePlaylistAdapter extends RecyclerView.Adapter<CreatePlaylistViewHolder> {
List<ProgramsData> programsDataList;
Context context;
public PlaylistAdapterInterface adapterInterface = null;
public CreatePlaylistAdapter(List<ProgramsData> programsDataList, Context context , PlaylistAdapterInterface adapterInterface) {
this.programsDataList = programsDataList;
this.context = context;
this.adapterInterface = adapterInterface;
}
#NonNull
#Override
public CreatePlaylistViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.chose_program_to_playlist_item, viewGroup, false);
return new CreatePlaylistViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CreatePlaylistViewHolder holder, int i) {
ProgramsData programsData = programsDataList.get(i);
holder.tvProgramName.setText(programsData.getProgramName());
if (programsData.getStudentName() != null)
holder.tvStudentName.setText(programsData.getStudentName());
else holder.tvLine.setText(""); //if there is no student the line won't be printed
holder.ivProfilePic.setImageResource(programsData.getProfilePic());
holder.programsData = programsData;
// holder.mAdapterInterface = adapterInterface;
adapterInterface.OnItemClicked(holder.programs);
}
#Override
public int getItemCount() {
return programsDataList.size();
}
}
class CreatePlaylistViewHolder extends RecyclerView.ViewHolder {
TextView tvProgramName;
TextView tvStudentName;
TextView tvLine;
CircleImageView ivProfilePic;
ToggleButton tbCheck;
ProgramsData programsData;
ArrayList<ProgramsData> programs;
PlaylistAdapterInterface mAdapterInterface;
public CreatePlaylistViewHolder(#NonNull View itemView) {
super(itemView);
tvProgramName = itemView.findViewById(R.id.tvProgramName);
tvStudentName = itemView.findViewById(R.id.tvStudentName);
ivProfilePic = itemView.findViewById(R.id.ivProfilePic);
tvLine = itemView.findViewById(R.id.tvLine);
tbCheck = itemView.findViewById(R.id.tbCheck);
programs= new ArrayList<>();
tbCheck.setOnClickListener(v -> {
if (tbCheck.isChecked()) {
tbCheck.setBackgroundResource(R.drawable.ic_radio_button_checked);
programs.add(programsData);
} else if (!tbCheck.isChecked()) {
tbCheck.setBackgroundResource(R.drawable.ic_check);
programs.remove(programsData);
}
});
}
}
And this is the interface for the Observer Pattern:
public interface PlaylistAdapterInterface {
void OnItemClicked(ArrayList<ProgramsData> programs);
}
I know it's a lot of code, but I just don't understand how to pass the data from the adapter back to the fragment...
I don't understand exactly what are you trying to do.
The code contains several errors that I'll try to explain.
A clear error that you have made stays in onBindViewholder where you call the listener at the creation of every item instead than after clicking on it.
You have simply add an onClickListener in the viewHolder.getItemView() or in a specific view of the viewholder and then perform the operation you need to do once an item is clicked.
If you set a listener inside onBindViewHolder, you also have a method called
holder.getAdapterPosition() that you can use to understand which item are you clicking on.
The viewholder should be used only to setup the views accordingly to the data you are binding and nothing else. For this reason, you should not pass any object or listener to it and instead use the approach above.
If you have just to retrieve the selected songs after an user confirms it's playlist you can just add a public method on your adapter
public List<ProgramsData> getSelectedSongs()
that you can call from your fragment when an user click a confirm button.
In order to have a list of all selected song, you can have another list
ArrayList<ProgramsData> selectedPrograms;
that you are going to fill after the click.
The content of the listener inside the onBindViewHolder could be
ProgramsData currentProgram = programs.get(holder.getAdapterPosition());
if(selectedPrograms.contains(currentProgram){
selectedPrograms.remove(currentProgram);
}else{
selectedPrograms.add(currentProgram);
}
notifyItemChanged(holder.getAdapterPosition); //You can use this to update the view of the selected item
Then inside the onBindViewHolderMethod you can check whether the items you are binding are part of the selectedList and update the views accordingly.
You can use callback method. Maintain list of selected items in array list and send back to fragment when done button is clicked or any other button you have placed for complete action.
Follow these steps
-Create an Interface with list parameter.
-Fragment should implement this interface.
-Then when you initialize Recyclerview adapter pass this interface object.
-When done is clicked call overridden method of this interface and send selected songs list as argument.

How to create an interface between an adapter and a viewholder

I want to create an interface between an adapter and a view holder (this view holder is an inner class of another adapter) so that I can update the text view (number). How can I do this?
In detail:
I have two recycle views (Main List Recycler View and Sub List Recycler View horizontally placed as shown in the fig) one having a number (as one of its item) and other having checkbox (as its item).
I have two adapters FilterMainListAdapter and FilterSubListAdapter with view holders FilterMainListViewHolder and FilterSubListViewHolder populating the fields.
When checkboxes are selected in the Sub List Recycler View, I want the corresponding number in the Main List Recycler View to update.
For this, I'm using and Interface.
public interface ChangeFilterMainNumber {
void OnChangeFilterMainNumberListener(int totalCheckedNumber);
}
I've checkbox's onClick method inside the FilterSubListViewHolder and I'm trying to send the total check boxes checked number as follows.
changeFilterMainNumber.OnChangeFilterMainNumberListener(totalCheckedNumber);
After that, I'm implementing ChangeFilterMainNumber interface inside the FilterMainListViewHolder
public class FilterMainListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,
ChangeFilterMainNumber {...}
But How can I define this interface inside the FilterSubListAdapter?
changeFilterMainNumber = ???;
[If it is an activity one can define the interface like this changeFilterMainNumber = (ChangeFilterMainNumber) context inside the default constructor of FilterSubListAdapter. But what about a view holder that is an inner class of another adapter?]
or is there a better approach in finding a solution to my problem other than this?
Update: You can take a look at the code here https://github.com/gSrikar/FilterScreen
If I implement the function as you want, I will implement like this:
(This is like an Observer pattern)
class Fragment/Activity implement OnChangeFilterMainNumberListener{
FilterMainListAdapter mainAdapter;
FilterSubListAdapter subAdapter;
void oncreate() {
mainAdapter = new FilterMainListAdapter(this);
}
#Override
void OnChangeFilterMainNumberListener(int totalCheckedNumber) {
.....
// Update data to sub list
}
}
class FilterMainListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
public interface ChangeFilterMainNumber {
void OnChangeFilterMainNumberListener(int totalCheckedNumber);
}
ChangeFilterMainNumber listener;
FilterMainListAdapter(ChangeFilterMainNumber listener) {
this.listener = listener;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null) {
listener.OnChangeFilterMainNumberListener(position)
}
}
});
}
}
class FilterSubListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
}

RecyclerView adapter taking wrong values during scrolling

I have read several questions on stackoverflow addressing this problem but I could not solve my problem. The recyclerview shows webviews. My issue is that the adapter, initially, load the right values (below image):
but when I scroll down, and scroll up to first position again, wrong values are displayed in webview (below image):
I think this problem may originate from previously started loads into the webview, but I don't have any idea about handling this behavior. Here is related parts of my code:
/* in my notation, pg is equivalent to page.*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_ebook_list, container, false);
mEbookRecyclerView = (RecyclerView) view.findViewById(R.id.ebook_recycler_view);
mLayoutManager = new LinearLayoutManager(getActivity());
mEbookRecyclerView.setLayoutManager(mLayoutManager);
updateUI();
return view;
}
#Override
public void onResume() {
super.onResume();
updateUI();
}
private void updateUI() {
if (mAdapter == null) {
EbookLab ebookLab = EbookLab.get(getActivity());
List<String> pgs = ebookLab.getPgs();
mAdapter = new EbookAdapter(pgs);
mEbookRecyclerView.setAdapter(mAdapter);
}
}
/* ******codes for preparation of menus which I have ignored *******/
private class EbookHolder extends RecyclerView.ViewHolder {
private String mPg;
public EbookHolder(LayoutInflater inflater, ViewGroup container) {
super(inflater.inflate(R.layout.list_item_ebook, container, false));
mListWebView = (WebView) itemView.findViewById(R.id.list_web_View);
mListWebView.getSettings().setLoadWithOverviewMode(true);
}
public void bindEbook(String pg) {
mPg = pg;
mListWebView.loadDataWithBaseURL("file:///android_asset/", "Stack Over Flow " + String.valueOf(glbPos), "text/html", "UTF-8", null);
}
}
private class EbookAdapter extends RecyclerView.Adapter<EbookHolder> {
private List<String> mPgs;
public EbookAdapter(List<String> pgs) {
mPgs = pgs;
}
#Override
public EbookHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
return new EbookHolder(layoutInflater, parent);
}
#Override
public void onBindViewHolder(EbookHolder holder, int position) {
String pg = mPgs.get(position);
glbPos = position;//A global variable for position
holder.bindEbook(pg);
}
#Override
public int getItemCount() {
return mPgs.size();
}
public void setPgs(List<String> pgs) {
mPgs.clear();
mPgs.addAll(pgs);
notifyDataSetChanged();
}
}
Any helps is appreciated.
Update:In my code , I defined mListWebView as a global variable. But it should be defined inside private class EbookHolder extends RecyclerView.ViewHolder.
Then I removed bindEbook(String pg) method and directly updated mListWevView
inside public void onBindViewHolder(EbookHolder holder, int position):
holder.mListWebView.loadDataWithBaseURL("file:///android_asset/", "Stack Over Flow " + String.valueOf(glbPos), "text/html", "UTF-8", null);
My Question >> Does calling bindEbook(String pg) takes longer time compared to scrolling recyclerview or it is related to Android Architecture??
Please try few things:
1) put log after String pg = mPgs.get(position); and see, which pg value is after that line;
In bindEbook():
2) put log to see which pg value is incoming;
3) put mListWebView.clearHistory() or mListWebView.loadUrl("about:blank") before mListWebView.loadDataWithBaseURL() just to see is your webview really loading new page
Also - try to replace webview with simple textView - you will see, is problem in webview or in adapter.
I had a very similar problem which I solved after extensive debugging. I hope this can help you:
In my project, I am showing a horizontal RecyclerView within a vertical RecyclerView. The code can be seen here and here and I explained what I do on my website here.
I noticed that when scrolling down the outer (vertical) RecyclerView, the values of the inner (horizontal) RecyclerView where messed up. That is similar to your problem. Set some console log commands (Log.v) here and there in my inner RecyclerView adapter (called MeasurementsAdapter), one specifically in the inner view holder class, where the data is bound:
public class MeasurementsViewHolder extends RecyclerView.ViewHolder {
[code omitted here]
public MeasurementsViewHolder (View itemView) {
super(itemView);
[code omitted here]
}
public void bindMeasurement (String name, double value, String unit, int color) {
mNameView.setText(name);
mValueView.setText(String.valueOf(Math.round(value)));
mUnitsView.setText(unit);
mBoxLayout.setBackgroundColor(color);
// Log.v(TAG, name + " " + value); <-- here I was logging
}
I was observing that when I was scrolling at some point this "bindMeasurement" method was no longer executed.
My guess is that RecyclerView was "recycling" some random data. This is not what I wanted and my result looked like yours' very much.
So, after I read the notifyDataSetChanged-method in the RecyclerView documentation. In the adapter of my outer RecyclerView (called Stationsadapter) I modified the binding method in the inner ViewHolder class and execute the adapter.notifyDataHasChanged method there (see line 84 in 1)
public class StationsViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {public TextView mStationInfoView;
private MeasurementsAdapter mMeasurementsAdapter;
/**
* Constructor of inner class StationsViewHolder
* #param itemView
*/
public StationsViewHolder(View itemView) {
super(itemView);
[code omitted here]
}
/**
* Bind weather station to view
* #param station is a class containing data of one weather station
*/
public void bindStation (Station station) {
[code omitted here]
mMeasurementsAdapter.notifyDataSetChanged();
}
Now my RecyclerViews show the right data. I very much hope this helps you and others.
Cheers
Ben

How to get child view from RecyclerView?

I am trying to get child view by position. I could get view when one item is clicked:
rvSellRecords.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
((MainActivity) getActivity()).showSellRecordFragment(position, view);
}
}));
Now I cannot get child view, without click - let's say by position for example:
rvSellRecords.someMagicalMethodWhichReturnsViewByPosition(5);
Question: How to get child view from RecyclerView?
EDIT FOR BOUNTY:
I have RecyclerView to show products list. When I click on it, I am adding new Fragment where I show product information. While opening I am updating toolbar with view from RecyclerView - this is working perfectly:
rvSellRecords.addOnItemTouchListener(new RecyclerItemClickListener(getContext(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
sellPresenter.onSellRecordSelected(position, view);
}
}));
When I click blue button with "+", I am incrementing quantity by 1.
public void onIncrementButtonClicked(){
sellRecord.setCount(sellRecord.getCount() + 1);
showQuantity();
bus.post(new SellRecordChangedEvent(sellRecord, sellRecordPosition));
}
Then I am posting updated sellRecord to first fragment using EventBus. There I am updating list data. I supposed that updating value(sell) automatically updates adapter. Now I am getting view from adapter using custom method(getView) which was created by me(you can find it below).
#Subscribe
public void onEvent(SellRecordChangedEvent event){
sell.getSellRecords().set(event.getSellRecordPosition(), event.getSellRecord());
sell.recalculate();
int position = event.getSellRecordPosition();
View view = adapter.getView(position);
bus.post(new TransactionTitleChangedEvent(null, view));
}
This is my adapter class - I changed adapter little bit to collect view in list and added method which returns view for respective position:
public class SellRecordsAdapter extends RecyclerView.Adapter<SellRecordsAdapter.ViewHolder> {
.....
.....
.....
List<View> viewList;
public SellRecordsAdapter(List<SellRecord> sellRecordList) {
.....
viewList = new ArrayList<>();
}
.....
.....
.....
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
.....
.....
.....
viewList.add(i, viewHolder.itemView);
}
public View getView(int position){
return viewList.get(position);
}
}
My problem: when I updating view in toolbar, I am getting old view. When quantity is 3, I am getting view with 2. When quantity 10 - view is with 9.
My question: how to get view from recycler view using position of item(without on click listener)?
Use recyclerView.findViewHolderForLayoutPosition(position) or
reyclerView.findViewHolderForAdapterPosition(position) to get the viewholder for postion. Then you can access any child from your viewholder.
Checkout Recyclerview
RecyclerView.ViewHolder holder = recycleView.findViewHolderForAdapterPosition(position);
ImageView imageView = holder.itemView.findViewById(R.id.iv_product);
This is a supplement to #Ravi Teja's answer. You can get the viewHolder from the recyclerView using position of the particular item, then get a particular view from the viewHolder as shown above
You can use RecyclerView's LayoutManager for it.
View view = layoutManager.findViewByPosition(position)
Hope this helps someone:
I was getting null pointer exceptions with:
recyclerView.findViewHolderForAdapterPosition
recyclerView.findViewHolderForItemId
layoutManager.findViewByPosition.
The reason was that there is a slight delay for the viewholder to be created.
I found the solution here: https://stackoverflow.com/a/33414430/7952427
I post an answer because which is really complex to findviews() from RecyclerView.
#Joe: After spending 4hours found one answer. Which gives me the proper view of the index.
mAdapter is adapter of RecyclerView
View v = recyclerView.findViewHolderForItemId(mAdapter.getItemId(index/position)).itemView;
Now just access your views by:
v.findViewById(R.id.edittext) OR any id.
it helped me, make a 100 ms delay before manipulate it, like this:
Handler handler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
// rcv is my recyclerview
rcvStatus.getChildAt(1).setBackground(getActivity().getResources().getDrawable(R.drawable.disabled));
// or:
rcvStatus.getChildAt(1).setClickable(false);
}
}, 100);
Write this method in adapter.
public Object getItem(int position) {
return yourArrayList.get(position);
}
and you just need to call it like
yourAdapter.getItem(2);
pass your required position.
Hope it solves your problem.
just put this method in your code and you can call it as you likes
void someMagicalMethodWhichReturnsViewByPosition(int position){
//I assumes child views are CardView
CardView c = (CardView)rvSellRecords.getItem(int position);
///optional codes
//////////
}
now I understand your problem. you need to use interface for join recyclerview item and activity.
you must define an interface class like below:
public interface IViewClick {
public void onClickButtonAdd();
}
add this parameter to your adapter class:
private IViewClick mListener;
and initialize it in constructor with value that get from inputs.
when user click on PLUS button, you send event to activity by this line:
mListener.onClickButtonAdd();
in your activity class you must implements IViewClick interface and add your code there, like this:
#Override
public void onClickButtonAdd() {
/// TODO every thing that you want.
/// change your toolbar values.
}
it is not good solution for you.
RecyclerView.ViewHolder holder =
mRecyclerView.findViewHolderForItemId(mAdapter.getItemId(i));
I wouldn't recommend tracking the view list yourself. It could lead to weird issues with item updates, position updates, etc.
Instead on your SellRecordChangedEvent, use findViewHolderForAdapterPosition() instead of adapter.getView().
#Subscribe
public void onEvent(SellRecordChangedEvent event){
sell.getSellRecords().set(event.getSellRecordPosition(), event.getSellRecord());
sell.recalculate();
int position = event.getSellRecordPosition();
View view = yourrecyclerview.findViewHolderForAdapterPosition(position);
bus.post(new TransactionTitleChangedEvent(null, view));
}
http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#findViewHolderForAdapterPosition(int)
And as a side note, it's better to implement an actual item click listener to the itemView on the ViewHolder instead of using touch listener. There's lots of examples of this online.
So the recyclerview and your product information are in 2 different fragments yes? You are expecting the recyclerview's views to update when they are not even in foreground? also you are changing adapter data item's data at position event.getSellRecordPosition() , but you are not notifying the adapter that its dataset changed, either by adapter.notifyDataSetChanged() or the other notifyItemChanged(position) methods.
I'd modify your onEvent() like so:
#Subscribe
public void onEvent(SellRecordChangedEvent event){
sell.getSellRecords().set(event.getSellRecordPosition(), event.getSellRecord());
sell.recalculate();
int position = event.getSellRecordPosition();
MyViewHolder holder = adapter.onCreateViewHolder(yourRecyclerView, 0);
adapter.onBindViewHolder(holder,position);
View view = adapter.getView(position);
bus.post(new TransactionTitleChangedEvent(null, view));
}
Calling on createViewHolder and next BindViewHolder on your adapter will definitely update the views for that position, then your adapter.getView(position) should return you the latest view.
Here MyViewHolder is your viewholder class and yourRecyclerview, is the reference to your recycler view
for (int i = 0; i < recycler_view.getAdapter().getItemCount(); i++) {
View viewTelefone = recycler_view.getChildAt(i);
}
If you want to replace text on a particular edit text for same position:
for (int i = 0; i < recycler_view.getAdapter().getItemCount(); i++) {
if(adpterPostion==i)
{
View viewTelefone = recycler_view.getChildAt(i);
EditText et_mobile = (EditText) viewTelefone.findViewById(R.id.et_mobile);
et_mobile.setText("1111111");
}
}

How to Keep RecyclerView's Buttons Clicked Respectfully - Android

I have a RecyclerView that is populated with CardViews. On each of the CardViews there is a button, which up votes the post.
Here is what the button looks when it is not pressed,
Here is what the button looks when it is pressed,
My code works for achieving this but I have a problem since it is a RecyclerView. When I scroll down the posts the RecyclerView recycles the previous posts that have been up voted. So a post will show that it was up voted even though a user never up voted it.
How can I keep the buttons pressed respectfully for each CardView?
This is my Adapter
public class DiscoverRecyclerAdapter
extends RecyclerView.Adapter<DiscoverRecyclerAdapter.ViewHolder> {
private String[] mDataset;
Typeface customFont;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTitle;
public TextView mVoterCounter;
public ImageButton mVoterButton;
public ViewHolder(android.support.v7.widget.CardView v) {
super(v);
mTitle = (TextView) v.findViewById(R.id.title);
mVoterCounter = (TextView) v.findViewById(R.id.voter_counter);
//Initialize voter button
mVoterButton = (ImageButton)v.findViewById(R.id.voter);
mVoterButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mVoterButton.setImageResource(R.drawable.ic_voter_pressed);
}
});
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public DiscoverRecyclerAdapter(String[] myDataset, Typeface passedFont) {
mDataset = myDataset;
customFont = passedFont;
}
// Create new views (invoked by the layout manager)
#Override
public DiscoverRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_discover, parent, false);
// set the view's size, margins, paddings and layout parameters
return new ViewHolder((android.support.v7.widget.CardView)v);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTitle.setText(mDataset[position]);
holder.mTitle.setTypeface(customFont);
holder.mVoterCounter.setTypeface(customFont);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
}
along with mDataset you will also need a boolean array say mIsSelected
now size of this will be equal to size of array mDataSet or create class if you want.
Then in onBindViewHolder do as
if(mIsSelected[position]
mVoterButton.setImageResource(R.drawable.ic_voter_pressed);
else
mVoterButton.setImageResource(R.drawable.ic_voter_unpressed);
and move button onclick inside onBindViewHolder as below
holder.mVoterButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mVoterButton.setImageResource(R.drawable.ic_voter_pressed);
mIsSelected[position] = true;
}
});
You need to clear the previous rows view of all previous data at the beginning of the onBindViewHolder.
In your case it seems you need to clear all the visibility params of the view components to whatever you deem to be the default. After that go ahead and populate the card with the data.
Being that your passed in dataset is only a string you will need to either make a call to your own API to get the up-vote count / status. Or change your dataset to a custom object array that tracks all of the different components you need to setup and record the data of each card.
In short: As the views get recycled you need to clean them up before re-use.

Categories

Resources