In my activity I have one recyclerview and each item view contains buttons. I want to be able to change some UI elements and other things such as an Array of custom objects for the adapter itself in my activity from the recyclerview adapter. Until now I declared the needed views as static but I found out that it's a terrible practice.
Example: I have the following recyclerview that represents a cart filled with a custom viewholder, from an Array of custom "cart_product" objects. (One of this custom oject's proprieties is "quantity" - represented by the spinner). I want to be able to change the object's "quantity" property by changing the spinner's value from the adapter... How could this be done? And when all the products are removed from cart (by swiping & detected from adapter) I want to display a textvie
ScreenShot
You can use callbacks:
In adapter create an interface:
public interface EventHandler {
void handle(int position) // if u need know position. If no, just create method without params
}
Create an private instance of interface in adapter:
public class YourAdapter extends RecyclerView.Adapter<YourHolder> {
private EventHanlder handler;
}
Implement EventHanlder in activity:
public class Mainacitivity extends Activity implements YourAdapter.EventHandler {
//.....
#Override
void handle (int position) {
// TODO do whatever u want
}
}
Add EventHandler to constructor parameters:
public YourAdapter (List<YourObject> data, EventHandler handler) {
//....
this.handler = handler;
}
When you need to change UI call
handler.hanlde(position);
And, finally pass this when initializing adapter
adapter = new YourAdapter (data, this)
If u need something else (not position), just change signature of handle() method
Related
I've a vertical RecyclerView and each element of it, is a nested horizontal RecyclerView. Both have their Adapter and ViewHolder. When I change a flag, I want to be able to refresh the drawing of all items in each inner horizontal RecyclerView. I've written a method in the outer adapter that consequently call the inner one:
OuterAdapter:
public void setEditEnabled(boolean enabled) {
innerAdapter.setEditEnabled(enabled);
notifyDataSetChanged();
}
InnerAdapter:
public void setEditEnabled(boolean enabled) {
this.editable = enabled;
notifyDataSetChanged();
}
Then in the activity I call:
outerAdapter.setEditEnabled(editable);
outerRecyclerView.invalidate();
But only some "rows" are correctly updated...How can I solve this?
EDIT: so the flow is:
Outer setEditEnabled -> inner setEditEnabled -> inner notify -> outer notify
First of all, why are you calling invalidate on the RecyclerView. when you call notifyDataSetChanged() on the adapter, it automatically updates the RecyclerView items. And why is there only one inner Adapter. I think there should be a different adapter for each horizontal RecyclerView.
I don't know if I'm doing it right, but it seems to work fine.
Previously I was instantiating a new innerAdapter in the ViewHolder constructor:
class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView innerRecyclerView;
ViewHolder(View view) {
super(view);
[...] // init things
innerRecyclerView.setAdapter(new InnerAdapter(context, onItemClickListener));
}
}
Now I'm doing the new in the OuterAdapter constructor and saving the reference as private field of the class:
public DailyMenusAdapter(Context context, OnItemClickListener onItemClickListener) {
innerAdapter = new InnerAdapter(context, onItemClickListener);
}
Then I pass the reference to:
innerRecyclerView.setAdapter(innerAdapter);
Is that ok in your opinion? So reusing the same adapter over and over again.
You know, I think if you will create annonimous Adapter class instead of notifyDataSetChanged (somethink like mRecyclerView.setAdapter(new YourAdapter(this, listOfDataYouPass))) it will work fine.
EDIT:Usually we do something like this to set adapter to RecyclerView:
List mLogs = new ArrayList<>;
mLogs.addAll (logs);
mLogAdapter = new LogAdapter(this, mLogs);
mRecyclerView = findViewById(R.id.log_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mLogAdapter);
and then, when we need to modify something we call mLogAdapter.notifyDataSetChanged. Or just call notifyDataSetChanged inside Adapter class. But in many cases it works wrong, hard to say why in your case.
So here is what I advise:
instead of call notifyDataSetChanged make this method and call it when you need to update or change something:
private void updateRecyclerView (){
mRecyclerView.setAdapter(new LogAdapter(this, mLogs));
}
Note: Do not pay attention to the names, I took code from the first project I got
I have a Main Activity that has a bunch of child views and then I have a ListView whose items are dynamically generated. I'd like to be able to long click any of these items and have this event handled in the Main Activity.
MainActivity
LinearLayout
ListView
Item - (long click here)
Now I know how to subscribe the the onLongClick event of the dynamically created ListView item itself but that's in the adapter I use to create the items.
What I was hoping was, that I could simply subscribe to the onLongClick event of the Main Activity and I'd see the event from the dynamically created ListView item propagate up to the MainActivity. But that's not happening.
My MainActivity implements View.OnLongClickListener, like so
class MainActivity : Activity(), View.OnLongClickListener {
override fun onLongClick(v: View?): Boolean {
Log.d("APP", "LongClicked: ")
return true
}
......
}
However, I'm not sure how to set the handler in the Activity itself. That is, for onClick, I would set android:onClcik="onCLick", in the XML of the Main Activity, however, I don't see an option to do the same for onLongClick
Also, I have not set any attributes such as android::longClickable="true/false"
any of the views. I'm a bit confused by the true/false and what the actually mean.
Any help would be appreciated.
UPDATE
I tried calling the setOnLongCLickListener on the ListView in the Main Activity, since I have access to the ListView there. Like so
availableZonesListView = findViewById(R.id.availableZonesListView)
availableZonesListView!!.onLongClickListener(this)
The MainActivity ('this') implements the View.OnLongClickListener and I see events coming in there when I long click directly on the MainActivity/Layout. But I don't see eventing coming in there when I long click on the items of the ListView
Now I know how to subscribe the the onLongClick event of the dynamically created ListView item itself but that's in the adapter I use to create the items.
public MainActivity extends .. implements MyClickListener {
... onCreate {
... = new MyAdapter(yourParams, this); // `this` is the new param
}
#Override
public void onAnyItemLongClicked() {
// Do whatever you want
}
}
Create your new interface:
interface MyClickListener {
void onAnyItemLongClicked();
}
Then in your adapter where you "Now I know how to subscribe the the onLongClick event". It would be something like this:
public class MyAdapter {
// other fields
private final MyClickListener listener;
public MyAdapter(otherParams, MyClickListener listener) {
.. assign other params
this.listener = listener;
}
}
// However you set your item click listener (in getView)
adapter.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public void onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
listener.onAnyItemLongClicked();
}
});
This will propagate the click event from the adapter to the activity.
(I had to improvise a bit because you didn't share many code details)
I have an activity with RecyclerView.Adapter class. RecyclerView contains button. When click that button I want to change a value defined in main activity and display it in TextView that also defined in main activity , otherwise I want to refresh the activity.
I already tried some code like:
Android, How to call onCreate() explicitly from other method?
How to restart the onCreate function
YourActivity extends Activity implements YourAdapter.ClickCallback{
#override
oncreate(Bundle bundle){
// initialize views
YourAdapter adapter = new YourAdapter(this);
// do something with your adapter
}
#Override
public void updateView(){
//update your views here
}
}
//In Recycler Adapter pass ClickCallBack As parameter
class YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ClickCallBack clickCallBack;
public YourAdapter(ClickCallBack clickCallBack){
this.clickCallBack=clickCallBack;
}
//ClickCallback Interface
interface ClickCallBack {
void updateView();
}
//In your ButtonClick
clickCallBack.updateView();
}
You can also add parameters in updateView method.
Pass an instance of those TextView through constructor and update the value there in the Adaptor. That would be the easiest way.
You don't need to recreate Activity in this case. The best solution would be:
Declare an interface.
Implement it in Activity(here implement your updates to TextView, etc.).
Pass object that implements the interface to your adapter.
Call the method on this interface in adapter.
Here is example:
https://stackoverflow.com/a/32720879/2528967
This is a my use case. I have an activity with a listview and a textview. The textview is the sum of all numbers on the listview, the listview has all the numbers.I have a custom adapter for this case.
On each row of the listview, I have a button. This button will change the number on this row.
what I want to do is this:
when user clicks the button, the value on each row is changed - doable.
the sum on the textview also changes accordingly.
This is a simplified example. In reality, I also have a constraint that I have a data model to represent the data on each role. I am not able to extend the data model to DataSetObserver.
any help?
You should create a listener, which listens for touch on your increment button and inside it call a method of the class extending an interface you created.
To make it easy, in your adapter you will have:
public interface OnIncrementListener{
onNumberIncremented();
}
private OnIncrementListener mListener;
//This is inside getView method of your adapter
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mListener.onNumberIncremented();
}
});
Then your activity
public class MyActivity extends Activity implements OnIncrementListener {
//inside onCreate
myAdapter.setOnIncrementListener(this);
//end onCreate
#Override
public void onNumberIncremented() {
//Change value of your TextView here
}
}
I think the question says it all: I want to create a custom OnItemClickListener for a ListView. I want to add and change the parameters for a OnItemClickListener but how can I create an own so it is called if I click on an Item in the ListView?
You create an interface first, then implement a method, where you set the listener in your custom class and already then make a main class to listen for your custom event
Interface:
public interface OnCustomEventListener{
public void onEvent(); //can have parameters
}
method in your e.g. adapter:
private OnCustomEventListener mListener; //field
//setter method
public void setCustomEventListener(OnCustomEventListener eventListener) {
this.mListener=eventListener;
}
listener:
someObjectYouWantToHaveYourCustomListenerToBeAssignedFor.setCustomEventListener(new OnCustomEventListener(){
public void onEvent(){
//do whatever you want to do when the event is performed.
}
});
how to call e.g. from your adapter:
if(this.mListener!=null){
this.mListener.onEvent();
}
P.S. Your custom listener may have as many parameters as you want
Source