I have a recyclerview which has Switch Button on each item and I want to add switch.setOnCheckedChangeListener for items. how can I make an interface between adapter class and the recyclerview host activity??
First you will create an interface class
public interface ExampleInterface {
void udpateData(String data);
}
The interface will be defined with name and parameters that you want
Second in the activity or fragment having RecycleView, you need implement this interface.
Third, when you call the your adapter please pass this interface to your adapter. Each time your Switch Button change status, interface will call updateData method to update data
Good luck
In your host activity write a method to handle switch button changed, say
private void switchButtonChanged()
pass the host activity when you create the adapter, for example
Adapter adapter = new Adapter(getActivity())
under your onCheckedChangedListener() in adapter, fire hostActivity.switchButtonChanged()
There is a simple way to do it. interface.
public class YourAdapter extends YourAdapterExtends {
private AdapterInteractionListener adapterInteractionListener;
... // your adapter codes
public YourAdapter(AdapterInteractionListener adapterInteractionListener){
this.adapterInteractionListener = adapterInteractionListener;
}
//call where you call switch.setOnCheckedChangeListener method
switch. setOnCheckedChangeListener{
adapterInteractionListener.onSwitched;
}
//here your interaction interface.
public interface AdapterInteractionListener{
void onSwitched();
}
}
And your host activity
public class YourActivity extends YourExtends impelements YourAdapter.AdapterInteractionListener {
...//your activity codes
#Override
onSwitched{
//here your switch listener triggered here
}
}
I hope this helps.
Related
Let's assume that we have Activity/Fragment which contains a RecyclerView. Furthermore, it sets an Adapter. For the sake of the example, let's say the Adapter has to have access to Fragment in order to call a method which displays a Snackbar. Moreover, Let's say there are a couple of items in the adapter. I want to delete one and remove it from the database. Therefore I should call ViewModel's methods. I've made a research but I couldn't find any information if referencing a fragment into the Adapter is good or not.
Could you help me and explain? Also for the ViewModel I've found some ideas here.
But what are the best practices?
good Adapter Classes should be STATIC helping developers to keep it separated from Activity/Fragment part
don't save Activity/Fragment reference inside Adapters
ViewModels should belongs to Activities or Fragments
Adapters should execute Activity/Fragment's actions via Callbacks/Listeners or LiveData
Pseudo-code:
public class MainActivity extends Activity {
private interface Listener {
void OnRemoved(#NonNull xxx removedItem);
}
private static final class MyAdapter extends ArrayAdapter<xxx> {
private final Listener mListener;
private MyAdapter(#NonNull final Listener listener) {
super(...);
this.mListener = listener;
}
#Override
public void remove(xxx item) {
super.remove(xxx); //<-- this removes item from Adapter
this.mListener.OnRemoved(item); //<-- this triggers Activity's code
}
}
public void onCreate(...) {
...
new MyAdapter(new Listener() {
#Override
public void OnRemoved(#NonNull final xxx removedItem) {
Snakbar.makeText(....).show();
}
});
}
}
I seem to be stuck with a problem with an object communicating with my activity class. The object is a view object with an onClick method that when called I would like it to notify my activity class so that it can perform said action. Below is some example code of my situation (assume all conventional setup operations have already been made):
public class MainActivity extends AppCompatActivity{
//...other global methods and objects
//Does not have access to instantiated Entry object(s)
public void entryObjectWasClicked(){
//perform said action
}
}
public class Entry extends View implements View.OnClickListener{
//...other global methods and objects
//Does not have access to the MainActivity object
#Override
public void onClick(View v){
//send a message to the MainActivity to
//somehow call the entryObjectWasClicked() method
}
}
The only way (off the top of my head) that I could think about dealing with this problem is by creating a static method in MainActivity and then calling it from an anonymous MainActivity object in the onClick method of Entry. The problem with the static method approach is that any subsequent method/object/primitive usages in the static method force those methods/objects/primitives to be static. This defeats the purpose of then being able to have two different instances of the MainActivity object.
After some looking I came across using Broadcast messages, specifically using the LocalBroadcastManager to send an intent to the activity. This code example works for my model, but I want to know: is this the best way for me to go about sending messages to my MainActivity from my Entry object?
If there is a more effective way of doing all this, what would it be?
You're overcomplicating things. Don't override onClick for this. Instead, have your activity call setOnClickHandler on your view, which sets a callback that's called when the view is clicked. Then use the default implementation.
Since you extend view, i guess you want to use it inside a layout. That means you may want to create a Listener for that. Example:
public class Entry extends View implements View.OnClickListener{
private OnClickListener listener;
public void setListener(OnClickListener listener) {
this.listener = listener;
}
#Override
public void onClick(){
if (this.listener != null) this.listener.onClick(this);
}
}
How you can inflate your layout in your Activity and access your custom view.
public class MainActivity extends AppCompatActivity{
public void onCreate( ...) {
Entry entry = findViewById(R.id.entry);
entry.setListener(new OnClickListener(...));
}
}
So I have an Activity. The Activity hosts a ViewPager with tabs, each tab holding a Fragment in it. The Fragments themselves have a RecyclerView each. I need to communicate changes from the RecyclerView's adapter to the activity.
Currently, I am using the listener pattern and communicating using interface between each of the components. i.e I have an interface between the RecyclerView's adapter and the Fragment holding it. Then an interface from the Fragment to the ViewPager's FragmentStatePagerAdapter which is creating all the Fragments. And 1 more interface between the ViewPager's adapter and the Activity hosting the ViewPager. I feel that there are too many interfaces for all the components because of how they are structured.
Currently I am not facing issues as such but I think the listener pattern is acting like an anti-pattern due to all the nested components. Instead of creating independent components I think the hierarchy will make it difficult for making code changes in future.
Am I doing it correctly or is there a better way to do it? Is this a case where I should use an Event Bus or Observer Pattern (If yes can you point me to some examples where someone overcame a similar problems using it)?
NOTE : If it matters, I need it to maintain a global object in the activity, something like a shopping cart where I can add or remove items and these items are present in RecyclerView's adapter from where I can add it to the cart and also increment or decrement the count for a particular item. The ViewPager and Tabs help segregate these items in various categories.
Edit 1 : Some code trying out #LucaNicoletti's approach -
I have skipped one level that is the level with the ViewPager's FragmentStatePagerAdapter. I guess that should not matter and stripped of some other code to keep it small.
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, FoodAdapter.OnFoodItemCountChangeListener {
#Override
public void onFoodItemDecreased(FoodItemModel foodItemModel, int count) {
Log.d("Test", "Dec");
}
#Override
public void onFoodItemIncreased(FoodItemModel foodItemModel, int count) {
Log.d("Test", "Inc");
}
// Other methods here
}
Fragment hosting the Adapter:
public class FoodCategoryListFragment extends Fragment implements FoodAdapter.OnFoodItemCountChangeListener {
// Other boring variables like recyclerview and layout managers
FoodAdapter foodAdapter;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Other boring intializations for recyclerview and stuff
// I set the click listener here directly on the adapter instance
// I don't have this adapter instance in my activity
foodAdapter.setOnFoodItemClickListener(this);
rvFoodList.setAdapter(foodAdapter);
}
}
The adapter class at the lowest level:
public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.FoodViewHolder> {
private OnFoodItemCountChangeListener onFoodItemCountChangeListener;
private List<FoodItemModel> foodItems;
// The interface
public interface OnFoodItemCountChangeListener {
void onFoodItemIncreased(FoodItemModel foodItemModel, int count);
void onFoodItemDecreased(FoodItemModel foodItemModel, int count);
}
// This is called from the fragment since I don't have the adapter instance
// in my activty
public void setOnFoodItemClickListener(OnFoodItemCountChangeListener onFoodItemCountChangeListener) {
this.onFoodItemCountChangeListener = onFoodItemCountChangeListener;
}
// Other boring adapter stuff here
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bMinus:
onFoodItemCountChangeListener.onFoodItemDecreased(foodItems.get(getAdapterPosition()),
Integer.parseInt(etCounter.getText().toString()));
}
break;
case R.id.bPlus:
onFoodItemCountChangeListener.onFoodItemIncreased(foodItems.get(getAdapterPosition()),
Integer.parseInt(etCounter.getText().toString()));
}
break;
}
}
}
my comments were:
what you should/could do it's to have a global data repo which holds the shopping cart and listeners associated with changes to it. Like a singleton, like ShoppingCart.getInstance().addListener(this); and ShoppingCart.getInstance().addItem(new Item(id));
and
Yes. That's what I'm suggesting. Do not forget that this Singleton can never ever holds Context or Activity because u don't want to leak memory, so always call removeListener. On my opinion it would reduce dependency as all your view controllers only interact with the data model
and I'll add some code to exemplify as a proper answer.
Below is a very crude, typed by heart code, but it should give an idea. All the UI elements are only tied to the data, and not to each other.
Similar stuff could be implemented with libraries that provide observable pattern out of the box for data-only objects.
public class ShoppingCart {
private ShoppingCart single;
private static void init(){
.. init single if not null
}
private List<Item> items = new ArrayList<>();
public int numberOfItems;
public long totalPrice;
private static void addItem(Item item){
init()
single.items.add(item);
single.numberOfItems++;
single.totalPrice+=item.price;
dispatchChange();
}
private static void removeItem(Item item){
init();
single.numberOfItems--;
single.totalPrice-=item.price;
dispatchChange();
single.items.remove(item);
}
private void dispatchChange(){
// TODO: write real loop here
for(single.listeners) listener.onCartChanged(single.cart);
}
public interface Listener {
void onCartChanged(ShoppingCart cart);
}
private List<Listener> listeners = new ArrayList<>();
// TODO: addListener and removeListener code
public static class Item {
String id;
String name;
long price;
}
}
To communicate between components (Activity, Fragment) you have to use an event bus.
In android, you could choose between:
RxJava
Otto
Green Robot EventBus
A blog to explain this.
I implemented the situation described in the image below:
The question is:
I have to update the gridView 2 when I update the GridView 1, how's the best way to do this? I could update the GridView 2 only in its Adapter by do a new Volley Request, maybe I could do this by re-set the adapter in the Fragment 2, Could I use a reference to the Fragment 2 in the Adapter GridView 1 ?
Details:
The GridView 1 is updated by a notifyDataSetChanged() on the Adapter performed after a Volley request by the Adapter itself. I can't use the onResume() method of Fragment 2 because it's not called due to this fragment it's showed at the same time of the Fragment 1.
Any Idea?
Thanks.
A listener is just an interface in JAVA. So you can not create an object of an interface ("new" operator)
1) Create an interface
public Interface OnUpdateGridView2Listener{
public void onUpdate() //Add the kind of data you want in the parameters
}
2) Create a member of the interface in the adapter of gridView1 and call its method
public GidViewAdapter1 extends Adapter{
private OnUpdateGridView2Listener myListener;
//Your code
#Override
public void notifyDataSetChanged(){
//Your code
myListener.onUpdate();
}
public void setOnUpdateGridView2Listener(OnUpdateGridView2Listener list){
this.myListener = list;
}
}
3) Implement the interface in your fragment and notify the adapter
public FragmentGridView2 extends Fragment implements OnUpdateGridView2Listener{
#Override
public void onUpdate(){
//Your code
gridView2Adapter.notifyDataSetChanged();
}
}
4) In your activity, set the listener when you create your fragmentGridView1
public YourActivity{
//Your code
FragmentGridView1 fragment = new FragmentGridView1();
fragment.setOnUpdateGridView2Listener(getApplication());
}
You could try to implement a Listener pattern like this:
FragmentTab2 {
private interface DataListener {
public Data getData();
}
private DataListener dataListener = new DataListener();
Data = dataListener.getData();
}
FragmentTab1 implements DataListener {
#Override
public Data getData() {
return Data;
}
}
Or a bit more involved should you need to notify, register, and unregister listeners try this here: Observer Design Pattern
At school we're now learning on how to make fragments more universal by using interfaces.
This technique is still kinda abstract and I don't really know when/how to use it.
Can anybody point me to some resources on how to use that technique (Could it be called interface callbacks?)
All help is very appreciated!
The callback approach, as you would call it, is as simple as Listener interface found in many parts of Java or Android. You may check the Observer pattern if you want to learn about a very general description. But if you already understand how to work with Listener, you will easily get the point about callbacks.
NOTE: Do not mix it with Callback term - these are not the same.
Suppose we have Activity MyActivity and Fragment MyFragment. We want to post some data from Fragment to Activity. Then let us create an interface within MyFragment:
public class MyFragment extends Fragment{
private PostDataCallback mCallback;//our Activity will implement this
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (PostDataCallback) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
public interface PostDataCallback{
public void onPostData(Object data);
}
/*
we trigger this method when we calculated
data or something like that and want to post it*/
public void onSomeEvent(Object data){
mCallback.onPostData(data);
}
}
Our MyActivity will look like this:
public class MyActivity extends Activity implements MyFragment.PostDataCallback{
private Object data;
#Override
protected void onCreate(Bundle savedInstanceState){
getFragmentManager().beginTransaction().add(R.id.some_container_id, new MyFragment(), "my fragment");
}
#Override
public void onPostData(Object data){
this.data = data;
//some operations
}
}
So, MyFragment knows nothing about the implementation of it's callback. But it knows, that it can call the method onPostData(Object o) on the instance of PostDataCallback, which is held in the variable mCallback.
Thus, when MyFragment triggers it's mCallback.onPostData(data), MyActivity get's the result.
Exactly the same approach would work if we wanted to send message from MyActivity to MyFragment, but we would do it do it vice versa: the trigger method, callback interface definition and instance would reside in MyActivity, and MyFragment would implement the interface.
Here are steps:
Download sample data from http://developer.android.com/training/basics/fragments/index.html(given in right side) and also look at url to how to add fragments from xml or dynamically to performing fragment transaction operations..
Then would recommend you to go through with fragment guide..http://developer.android.com/guide/components/fragments.html
Once you understand complete life cycle and its fragment callback methods then would be easy to understand example given by Google as sample.
To defining interface in fragment to calling interface or passing callback to activity..
Let’s say you have two fragments which shows list as article titles and article details.
In your article list extends fragment list public class Fragment1 extends ListFragment
Set your list view using list adapter in oncreateview method.
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, Array);
setListAdapter(adapter);
Now we need to display article details when user click on article, so we need to pass position to activity to it can call back corresponding article details to show in fragment2.
So when user click on article, system call onListItemClick callback method.
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Call interface here and pass article position
Define interface and pass position in method which activity will override.
public interface OnArticleSelectedListener {
public void onArticleSelected(int position);
}
In on attach method instantiates an instance of interface by casting the Activity, If the activity has not implemented the interface, then the fragment throws a ClassCastException. On success.
Override interface method to display article details by passing position as bundle data to Fragment2.
Hope it will help you to understand sample code.
You can simple create new Android Application project in eclipse.
Then create Android Object (Fragment) with callback methods. This will give you an idea for interfaces.
And then the same you can apply for activity to fragment.