I have created a recyclerView -
public class PlayerListRecyclerViewAdapter extends RecyclerView.Adapter<PlayerListRecyclerViewAdapter.ViewHolder>
playerAdapter = new PlayerListRecyclerViewAdapter(this,cursor, playerDataSet);
playerRecyclerView.setAdapter(playerAdapter);
It all works fine, but I created the following method:
public void swapCursor(Cursor newCursor) {
// Always close the previous mCursor first
if (pCursor != null) pCursor.close();
pCursor = newCursor;
if (newCursor != null) {
// Force the RecyclerView to refresh
this.notifyDataSetChanged();
}
}
But when I try to call it from the activity that is linked to the adapter, it does not find it:
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//Do nothing, we are not moving anything
//But maybe we can let the user arrange their list?
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
long id = (long) viewHolder.itemView.getTag();
removePlayer(id);
playerAdapter.swapCursor(getAllPlayers());
}
}).attachToRecyclerView(playerRecyclerView);
}
Swap cusor is red and in the adapter it shows the message it is never used.
I am trying to follow:
https://github.com/udacity/ud851-Exercises/blob/student/Lesson07-Waitlist/T07.05-Solution-AddGuests/app/src/main/java/com/example/android/waitlist/GuestListAdapter.java
But I am not sure what I am missing?
Everything else works fine, I can swipe, it loads the details. All good just calling this method for some reason does not work.
please check your player adapter is getting null value.
because swapCursor method access after defining playeradapter object.
if(playerAdapter!=null){
playerAdapter.swapCursor(getAllPlayers());
}
Check your code for declaration of playerAdapter.
It should be as :-
PlayerListRecyclerViewAdapter playerAdapter;
InsteadOf
RecyclerView.Adapter playerAdapter;
Related
I m new here and I've got a question for which I haven't found answer yet.
I am trying to get some variable value from a clicked object using onClickListener.
This view is an object (actually its a ConstraintLayout) created previously with a boolean value.
Code (that is not the current code I've got but you get the idea hopefully):
public class layoutView extends ConstraintLayout {
boolean bool = false;
public layoutView (Context context) {
super(context);
}
...
}
I am adding this layoutView into my main layout and its working fine but I need to get this boolean value when i click the layout view.
layoutView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.setBackgroundColor(Color.GREEN); //this works fine
boolean check = v.bool //this wouldnt work
if (v.bool == true)... //this woulnt work
}
});
Is it possible at all. How to approach this?
Thanks in advance!
You can try layoutView.setTag()/getTag() instead of keeping your own variable bool.
Note that you may need to use a Boolean object instead of primitive type, for example:
layoutView.setTag(new Boolean(true));
// inside on click listener
if (v.getTag() == true) {
// do something
}
For our application I had to implement a nested RecyclerView. I'm getting a list of Tables from JSON and every Table has another list with groups from each table. I can get everything on the screen as requested, the problem is the selection.
I have 2 different RecyclerViews on the screen and I can not seem to get a single selection working in this environment, especially after scrolling. Every group and every table has a Toggle Button, and only one can be active at a time.
This is how the main screen looks like
So far I've tried putting a boolean isSelected on the Model but that didn't work out at all. The closest solution I came up with was a helper class that searches every CompoundButton on-screen and deselects them all when one is selected. The problem is this helper class cant get the Buttons which are off-screen.
How I populate ParentAdapter (in MainActivity):
public void setAdapter(List<Table> tableList)
{
RecyclerView recycler_view_parent = findViewById(R.id.recyclerparent);
LinearLayoutManager manager=new LinearLayoutManager(MainActivity.this);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
recycler_view_parent.setLayoutManager(manager);
recycler_view_parent.setHasFixedSize(true);
recycler_view_parent.setItemViewCacheSize(tableList.size());
ParentAdapter parentAdapter=new ParentAdapter(tableList,MainActivity.this);
recycler_view_parent.setAdapter(parentAdapter);
}
How i populate ChildAdapter (in onBindViewHolder of ParentAdapter):
FlexboxLayoutManager manager = new FlexboxLayoutManager(context);
manager.setFlexDirection(FlexDirection.COLUMN);
manager.setJustifyContent(JustifyContent.FLEX_START);
manager.setFlexWrap(FlexWrap.WRAP);
manager.setAlignItems(AlignItems.BASELINE);
holder.recycler_view_child.setLayoutManager(manager);
holder.recycler_view_child.setHasFixedSize(true);
adapter = new ChildAdapter(tableList, tableList.get(position).getGroups(), context);
holder.recycler_view_child.setAdapter(adapter);
The desired output should be only 1 Table OR Group at a time can be toggled (in total, not one from every RecyclerView) and the state should be the same after scrolling/device rotation).
I did a lot of research over the last days on this subject and I can not seem to find a working example of nested RecyclerView with single selection over both RVs.
So does anyone have an idea on how to solve this? I think the biggest issue is telling the Parent that a Button in Child was toggled and vice-versa.
I think for the ParentAdapter it should look something like this:
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, final int position) {
final Table table = tablelist.get(position);
ViewHolder viewHolder = (ViewHolder) holder;
if (table.isTableSelected()) {
viewHolder.toggletable.setChecked(true);
lastToggled = position;
} else {
viewHolder.toggletable.setChecked(false);
}
viewHolder.toggletable.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b) {
table.setTableSelected(true);
// notify ChildAdapter and group.setGroupSelected(false)
if (lastToggled >= 0) {
tablelist.get(lastToggled).setTableSelected(false);
// notify ChildAdapter and group.setGroupSelected(false)
notifyItemChanged(lastToggled);
}
lastToggled = position;
} else {
table.setTableSelected(false);
}
}
});
}
Thanks in advance.
UPDATE: Managed to come up with a solution myself, although 100% sure, not the best approach.
First of all, implement Greenrobots EventBus:
implementation 'org.greenrobot:eventbus:3.1.1'
Now in the Activity where you hold both RecyclerViews register the Event listener:
#Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
and subscribe 2 methods. One for Parent Events and one for Children Events. This methods will trigger every time an item is selected!
#Subscribe(threadMode = ThreadMode.MAIN)
public void onParentEventClicked(ParentAdapter.ParentEvent event) {
// to access the inner adapter here you must set it to public in the ParentAdapter(public ChildAdapter adapter;)
adapter.adapter.deSelectChild();
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onChildEventClicked(ChildAdapter.ChildEvent event) {
// normal ParentAdapter reference(ParentAdapter adapter;)
adapter.deSelectParent();
}
Inside your ParentAdapter create a method to deselect all parent items and a static class to fire the event:
public void deSelectParent()
{
for (int i=0;i<data.size();i++)
{
data.get(i).setSelected(false);
}
notifyDataSetChanged();
}
public static class ParentEvent {
View view;
int position;
}
Inside your ChildAdapter create a method to deselect all child items and a static class to fire the event:
public void deSelectChild()
{
for (int i=0;i<data.size();i++)
{
datachild.get(i).setSelected(false);
}
notifyDataSetChanged();
}
public static class ChildEvent {
View view;
int position;
}
now in both Parent and Child onBindViewHolders, you need similar logic for your models:
if (item.isSelected()) {
holder.yourbutton.setChecked(true);
} else {
holder.yourbutton.setChecked(false);
}
holder.yourbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ParentEvent event = new ParentEvent();
event.view = holder.yourbutton;
event.position = position;
EventBus.getDefault().post(event);
if (holder.yourbutton.isChecked()) {
for (int i = 0; i < data.size(); i++) {
data.get(i).setSelected(false);
}
data.get(position).setSelected(true);
} else {
data.get(position).setSelected(false);
}
notifyDataSetChanged();
}
});
And thats pretty much it, every click on a ParentItem will trigger the deselect method for ChildAdapter and vice-versa.
Due to the high usage of notifyDataSetChanged() I recommend using this line to get rid of the blinking:
recycler_view_parent.setItemAnimator(null);
Any problems let me know!
I have a recycler view that uses LinearSnapHelper to snap the items as the user scrolls it. Now, I'd like to listen to the snaps, preferably getting the index of the item that was snapped. However, I can't really figure out if there's a way to do that.
Initially I thought that the LinearSnapHelper's findTargetSnapPosition() would return the index to snap (as the documentation says), however this isn't true. It randomly returns -1 or 0 for the first item, and when the list is scrolled, it gets randomly invoked. Sometimes, the method's not called at all; sometimes, the index is incorrect, and sometimes it's correct. It seems that it's no use trying to find the index using this.
So: How would I find out which item the recycler view snaps to?
I managed to get this working. I'm not sure if it's the best solution, but here's what I did:
private int selectedPosition = -1;
private LinearLayoutManager layoutManager;
private RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
LinearSnapHelper snapHelper = new LinearSnapHelper() {
#Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
View view = super.findSnapView(layoutManager);
if (view != null) {
final int newPosition = layoutManager.getPosition(view);
if (newPosition != selectedPosition && recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {
onViewSnapped(newPosition);
selectedPosition = newPosition;
}
}
return view;
}
};
...
}
private void onViewSnapped(int index) {
// YOUR CODE HERE
}
I have a RecyclerView with a Cursor inside. I remove items after swipe (in ItemTouchHelper.SimpleCallback) - delete from database, change cursor and then call notifyItemRemoved. It works fine, but when all items are on the screen,
the last one is animating.
My code:
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
...
viewHolder.getAdapter().removeFromDb(viewHolder.getAdapterPosition());
viewHolder.getAdapter().removeItem(viewHolder.getAdapterPosition());
}
and inside my adapter:
public void removeItem(int adapterPosition) {
notifyItemRemoved(adapterPosition);
}
public void removeFromDb(int adapterPosition) {
DBHelper db = ...
cursor.moveToPosition(adapterPosition);
db.openDB();
db.removeItem(...);
changeCursor(db.newCursor...);
db.closeDB();
}
public void changeCursor(Cursor _new) {
cursor.close();
cursor = _new;
}
I saw many other questions, and the easiest solution is to set whole data into ArrayList, but I don't think it's the best idea. In my opinion something is wrong with reloading data to cursor, but I have no idea how to solve it.
SOLVED: You have to add
recyclerView.setHasFixedSize(true);
in your activity.
I have created my own adapter which extends BaseAdapter implements Filterable.
1. I am occasionally getting index out of bounds error, in getView method:
private ArrayList<ResultHolderData> originalData;
private ArrayList<ResultHolderData> arrayList;
private LayoutInflater inflater;
private ArrayList<ResultHolderData> suggestions;
public static class ResultHolderData {
public String symbol;
public String fullName;
public ResultHolderData(String a, String b) {
symbol=a;
fullName=b;
}
}
public static class ResultHolder {
public TextView symName;
public TextView symNameFull;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ResultHolder rh;
if(convertView==null) {
rh=new ResultHolder();
convertView=inflater.inflate(R.layout.two_line_dropdown_item, null);
rh.symName=(TextView) convertView.findViewById(R.id.autocompleteSym);
rh.symNameFull=(TextView) convertView.findViewById(R.id.autocompleteName);
convertView.setTag(rh);
} else {
rh=(ResultHolder) convertView.getTag();
}
//rh.symName.setTextColor(Color.GREEN);
/***THE BELLOW LINE THROWS THE ERROR***/
rh.symName.setText(arrayList.get(position).symbol);
rh.symNameFull.setText(arrayList.get(position).fullName);
//rh.symName.setText(arrayList.get(position));
return convertView;
}
The arrayList represents the filtered resultSet:
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
if(results.count>0 && results!=null) {
arrayList=(ArrayList<ResultHolderData>) results.values;
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
The error occurs sometimes, when you have 2 items in dropdown suggestions and when typing another letter causes the dropdown to only suggest one item. Then it says: Invalid index 1, size is 1. Or size is 0..
My opinion: It usually happens when i am typing in fast, so i assume that NotifyDataSetChanged is in progress, but the publishResults changes the content of the arrayList and this causes the error. But then again i would expect this to happen in more situations?
2. Also another error pops out: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.
And the only place where i change the contents of adapter is in publishResults? Why is this happening then?
Its obvious i got something wrong here, or i dont completely understand how this works.
Since i posted the same question twice when i despair, here is the answer i gave: Errors with custom BaseAdapter for AutoCompleteTextView(indexOutOfBounds & content changed but no notification)
The problem was this line in publishResults:
arrayList=(ArrayList<ResultHolderData>) results.values;
which just pointed arrayList to those results, instead i made a "shallow copy" and cleared the list before it:
arrayList.clear();
for(ResultHolderData tempRhd : (ArrayList<ResultHolderData>)results.values)
arrayList.add(tempRhd);
and problem solved!