My fragment:
public class FragmentSort extends Fragment {
#BindView(R.id.sortRecyclerView)
RecyclerView sortRecyclerView;
protected RecyclerView.Adapter adapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(layoutResId, container, false);
adapter = new StoreListItemAdapter(getActivity(), collection);
sortRecyclerView.setAdapter((RecyclerView.Adapter) adapter);
return rootView;
}
#Subscribe
public void onStoreClickEvent(Store store) {
Debug.d(TAG, "onStoreClickEvent: store = " + store);
handleFilterItemSelect(store.getAddress());
}
}
Here my custom adapter:
public class StoreListItemAdapter extends RecyclerView.Adapter {
private Context context;
private List<?> data = new ArrayList<>();
public DataBindingRecyclerViewAdapter(Context context, List<?> data) {
this.context = context;
this.data = data;
}
public void onClick(Store store) {
EventBus.getDefault().post(store);
}
}
When click on some item in list then call method onClick().
So I need my fragment to handle click of item.
To to do this I use EventBus.
After click I call
EventBus.getDefault().post(store);
And as result in fragment call method:
onStoreClickEvent(Store store)
So this is my model to communicate between my custom fragment and my custom adapter.
It's work. Fine.
The quesion is: Is this a best approach for communicate between fragment and adapter?
P.S. My custom adapter can use by fragment, activity or custom view.
An alternative would be to create a listener interface like this:
public interface OnStoreItemClickListener {
public void onStoreItemClicked(Store item);
}
Then, in your Adapter, you declare a field of type OnStoreItemClickListener and you create a setter method for it.
When you detect a click, you simply check if your listener is set and call the onStoreItemClicked() method.
You can register a listener via the setter from wherever you need.
Related
I have MainActivity,which contain viewpager,which contain 2 fragments:
MainActivity
ViewPager viewPager = findViewById(R.id.viewPager);
MyPagerAdapter adapter = new MyPagerAdapter(this,
getSupportFragmentManager());
viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.TabLayout);
tabLayout.setupWithViewPager(viewPager);
Fragment fragmentHour=adapter.getItem(1);
onChangeDayListener2= (OnChangeDayListener) fragmentHour;
I am also have interface for send data from main activity in fragment:
public interface OnChangeDayListener{
void OnChange(WeatherList list,int pos);
}
but when i try to work with this callback in interface my recycler adapter always is null:
HourWeatherFragment:
public class HourWeatherFragment extends Fragment implements MainScreen.OnChangeDayListener{
private RecyclerView recyclerView;
private AdapterHour adapter;
private ArrayList<Weather> days;
Context context;
static HttpClient httpClient;
public HourWeatherFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
days=new ArrayList<>();
adapter = new AdapterHour(days,getActivity());
LinearLayoutManager LayoutManager = new LinearLayoutManager(getActivity().getBaseContext(),
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(LayoutManager);
recyclerView.setAdapter(adapter);
days.add(new Weather());
recyclerView.getAdapter().notifyDataSetChanged();
Log.d("recycler","adapter seted!");
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_hour_weather, container, false);
recyclerView = rootView.findViewById(R.id.hour_list);
httpClient=new HttpClient();
MyAsyncTask task=new MyAsyncTask();
// task.execute("lviv");
return rootView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context=context;
}
#Override
public void OnChange(WeatherList list,int pos) {
String date=list.getAverageWeather().get(pos).getDate();
ArrayList<Weather> weather=list.getByDate(date);
Log.d("ldata","size of weather for adding="+weather.size());
// days.add(new Weather());
if(adapter!=null){
adapter.notifyDataSetChanged();
Log.d("tag","first null!");}
else{
try{
recyclerView.getAdapter();}
catch (NullPointerException ex){
Log.d("tag","null!");//THERE ALWAYS NULL
}}
So,firstly creating activity,then i start load data and when data loaded i send data from MainActivity to HourWeatherFragment throught callback,
but when i receive data in fragment,my adapter is null,how i can fix it?
You can create a static singleton class to hold data and retrieve that data in the fragment.
public class DataHolder {
//create static variables here.
}
In your fragment simply retrieve the data and make it null after that.
Alternative solution
Another solution is to use Bundles. Follow the bundle solution here.
You must cast RecyclerView.
in your fragment and onCreateView method :
recyclerView = (RecyclerView) rootView.findViewById(R.id.hour_list);
I know this is an old question, but contrary to the accepted answer I think the issue is that the handle to the fragment's widget such as recyclerView is not available in the OnCreateView function.
The handles (or pointers if you wish) to the widgets are only guaranteed to exist in the OnViewCreated function.
This documentation has good description of the lifecycle of fragments:
https://guides.codepath.com/android/Creating-and-Using-Fragments
an extract is:
// This event is triggered soon after onCreateView().
// onViewCreated() is only called if the view returned from onCreateView() is non-null.
// Any view setup should occur here. E.g., view lookups and attaching view listeners.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListView lv = (ListView) view.findViewById(R.id.lvSome);
lv.setAdapter(adapter);
}
Hope this helps.
I am working on a app that controls tasks. I have to choose a person from a list to be the handler of the task. I have made a TextView that when clicked opens a DialogFragment that contains a recyclerview. How do I select a person from the recyclerView and send it back to the activity. I have tried working with interfaces but that hasn't worked yet.
Below are DialogFragment and the MainActivity:
AlertDialogFragment
RecyclerView recyclerViewPersons;
List<PERSON> personList = new ArrayList<>();
PersonsAdapter personsAdapter;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_persons_list, container);
recyclerViewPersons = v.findViewById(R.id.recyclerViewPersons);
recyclerViewPersons.setLayoutManager(new LinearLayoutManager(this.getActivity()));
personsAdapter = new PersonsAdapter(getActivity());
personsAdapter.setData(personList);
recyclerViewPersons.setAdapter(personsAdapter);
return v;
}
public void setAdapterData(List<PERSON> personList) {
this.personList = personList;
}
}
MainActivity
PersonsAdapter personsAdapter;
final FragmentManager fragmentManager = getSupportFragmentManager();
final AlertDialogFragment alertDialogFragment = new AlertDialogFragment();
protected void onCreate(Bundle savedInstanceState) {
txtPersons.setOnClickListener(v -> {
alertDialogFragment.setAdapterData(personList);
alertDialogFragment.show(fragmentManager, "dialog recycler");
});
}
You said that you managed to send the data from Adapter to DialogFragment. Now you can send this data from DialogFragment to Activity.
For this you can create another interface inside or outside the DialogFragment. Then you can implements this fragment with the activity and override this interface with its body.
Now, inside the DialogFragment override the onAttach method and instantiate this inferface instance.
Interface
public interface OnMyInterface {
public void onMyData(your data);
}
DialogFragment
private OnMyInterface onMyInterface;
public ForgotAndResetPasswordFragment() {}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_your_layout, container, false);
}
#Override
public View onViewCreated(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//send the data to activity using on onMyInterface
onMyInterface.onMyData(your data);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
onMyInterface = (OnMyInterface) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString());
}
}
Activity
public class MyActivity implements OnMyInterface {
#Override
public void onMyData(your data) {
//get this data
}
}
Or you can try this approach also:
https://camposha.info/source/android-data-passing-fragment-activity-via-intent
First define an interface(which sends Person data) in Fragment & let it be implemented in the Activity.
Then, define one more interface in Recycler Adapter & let it be implemented in the Fragment. This interface will get the information after clicking on Recycler item. Invoke this interface in Adapter's layout on click.
Then finally whatever data you get in fragment, pass it back to activity via first interface.
Hope this helps?
My question is similar to this one.
Best approach to communicate between Fragment/Activity and RecyclerView.Adapter?
I know how to implement an onClickListener, an Interface and an OnItemSelected method in the corresponding activity when the user clicks on a button in a Fragment. I have a fragment with an array list. By clicking on each item in the array list in the fragment, an ExoPlayer will open in a new activity or a fragment. As I understand it, onItemClickListener doesn't work with Recyclerview. How do I set the onClick method to the items in the list? Also, do I set onClick outside of RecyclerView? This is my RecyclerView adapter class. Should the interface take additional parameters? Thank you in advance.
public class StepsAdapter extends RecyclerView.Adapter {
private static final String TAG = StepsAdapter.class.getSimpleName();
private ArrayList<Steps> stepsList = new ArrayList<Steps>();
private StepsAdapter.StepsAdapterOnClickHandler mClickHandler;
/**
* The interface that receives onClick messages.
*/
public interface StepsAdapterOnClickHandler {
void onClick(Steps stepClick);
}
/**
* Creates a StepsAdapter.
*
* #param clickHandler The on-click handler for this adapter. This single handler is called
* when an item is clicked.
*/
public StepsAdapter(StepsAdapterOnClickHandler clickHandler,ArrayList<Steps> stepsList) {
mClickHandler = clickHandler;
this.stepsList = stepsList;
}
/**
* Cache of the children views for a steps list item.
*/
public class StepsAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
#BindView(R.id.step_short_desc)
public TextView stepShortDescription;
#BindView(R.id.step_description)
public TextView stepDescription;
public StepsAdapterViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
view.setOnClickListener(this);
}
/**
* This gets called by the child views during a click.
*
* #param v The View that was clicked
*/
#Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
Steps stepClick = stepsList.get(adapterPosition);
mClickHandler.onClick(stepClick);
}
}
#Override
public StepsAdapter.StepsAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.steps_list_item;
LayoutInflater inflater = LayoutInflater.from(context);
boolean shouldAttachToParentImmediately = false;
View view = inflater.inflate(layoutIdForListItem, viewGroup, shouldAttachToParentImmediately);
return new StepsAdapter.StepsAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(StepsAdapter.StepsAdapterViewHolder holder, int position) {
//Binding data
final Steps stepsView = stepsList.get(position);
holder.stepShortDescription.setText(stepsView.getStepShortDescription());
holder.stepDescription.setText(stepsView.getStepDescription());
}
#Override
public int getItemCount() {
return stepsList.size();
}
public void setStepsList(ArrayList<Steps> mStepsList) {
this.stepsList = mStepsList;
notifyDataSetChanged();
}
}
The corresponding fragment. Is the click method implemented correctly?
public class StepsListFragment extends Fragment implements StepsAdapter.StepsAdapterOnClickListener {
// Tag for logging
private final String TAG = StepsListFragment.class.getSimpleName();
#BindView(R.id.recyclerview_steps)
RecyclerView mRecyclerView;
ArrayList<Steps> stepsArrayList;
Recipes recipes;
// Final Strings to store state information about the list of steps and list index
public static final String STEPS_LIST_INDEX = "list_index";
// Define a new interface OnStepsClickListener that triggers a callback in the host activity
OnStepClickListener mCallback;
// OnStepsClickListener interface, calls a method in the host activity named onStepSelected
public interface OnStepClickListener {
void onClick(Steps stepClick);
}
// Override onAttach to make sure that the container activity has implemented the callback
#Override
public void onAttach(Context context) {
super.onAttach(context);
// // This makes sure that the host activity has implemented the callback interface
// // If not, it throws an exception
try {
mCallback = (OnStepClickListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnStepSelectedListener");
}
}
/**
* Mandatory empty constructor for the fragment manager to instantiate the fragment
*/
public StepsListFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// //Inflate the Steps fragment layout
View rootView = inflater.inflate(R.layout.fragment_steps, container, false);
// // Bind the views
ButterKnife.bind(this, rootView);
Bundle bundle = this.getArguments();
if (bundle != null) {
recipes = getArguments().getParcelable("Recipes");
stepsArrayList = new ArrayList<>();
stepsArrayList = recipes.getRecipeSteps();
}
if (savedInstanceState != null) {
//Restore the fragment's state here
stepsArrayList = savedInstanceState.getParcelableArrayList(STEPS_LIST_INDEX);
}
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(mLayoutManager);
Log.i("listSteps", stepsArrayList.size() + "");
StepsAdapter stepsAdapter = new StepsAdapter(this, stepsArrayList);
mRecyclerView.setAdapter(stepsAdapter);
// Return the root view
return rootView;
}
public void onClick(Steps stepClick){
mCallback.onClick(stepClick);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveIn`enter code here`stanceState(outState);
//Save the fragment's state here
outState.putParcelableArrayList(STEPS_LIST_INDEX, stepsArrayList);
super.onSaveInstanceState(outState);
} }
The best way to implement click function on each item of recyclerview is initialise onClickListener when the view is populated inside in the recyclerview viewholder. Then in the onClick method, use a custom interface/listener to catch the click activity in your parent fragment/activity.
eg: create a custom interface like this;
public interface RecyclerviewOnClickListener{
void recyclerviewClick(int position);
}
Now implement this interface in your parent activity/fragment containing the recyclerview. Suppose your fragment name is ChatFragment. Then,
public class ChatFragment extends Fragment implements RecyclerviewOnClickListener{
.
.
}
This will implement the function onClick(int position) in your fragment. In your adapter constructor, you should create a field for the RecyclerviewOnClickListener. Suppose your adapter name is ChatAdapter, then
Adapter Constructor.
public ChatAdapter(RecyclerviewClickListener listener, .....<other params>){
this.listener = listener;
}
In your fragment, you can initialise your adapter like the following
ChatAdapter adapter = new ChatAdapter(this, <any additional params>);
You should pass the same instance of 'listener' to your viewholder also, and initialise the listener there also
Now in your recyclerview ViewHolder, you can set view.setOnClickListener(new OnClickListener{
this.listener.recyclerviewClick(getAdapterPosition())
});
The getAdapterPosition() function returns the position of the click in the recyclerview, which will get a callback in the recyclerviewClick() function in your fragment.
About the number of parameter you are passing, you can use as much as you want, but for a click function in recyclerview, the ideal way is to use only one param which the position. Now you can modify the contents in the list that you are passing from your fragment to adapter and call notifyDataSetChanged() which will update the recyclerview. Hope it's clear.
Try this inside OnBindviewHolder
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//code here
}
My fragment:
public class FragmentSort extends Fragment {
#BindView(R.id.sortRecyclerView)
RecyclerView sortRecyclerView;
protected RecyclerView.Adapter adapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(layoutResId, container, false);
adapter = new StoreListItemAdapter(getActivity(), collection);
sortRecyclerView.setAdapter((RecyclerView.Adapter) adapter);
return rootView;
}
#Subscribe
public void onStoreClickEvent(Store store) {
Debug.d(TAG, "onStoreClickEvent: store = " + store);
handleFilterItemSelect(store.getAddress());
}
}
Here my custom adapter:
public class StoreListItemAdapter extends RecyclerView.Adapter {
private Context context;
private List<?> data = new ArrayList<>();
public DataBindingRecyclerViewAdapter(Context context, List<?> data) {
this.context = context;
this.data = data;
}
public void onClick(Store store) {
EventBus.getDefault().post(store);
}
}
When click on some item in list then call method onClick().
So I need my fragment to handle click of item.
To to do this I use EventBus.
After click I call
EventBus.getDefault().post(store);
And as result in fragment call method:
onStoreClickEvent(Store store)
So this is my model to communicate between my custom fragment and my custom adapter.
It's work. Fine.
The quesion is: Is this a best approach for communicate between fragment and adapter?
P.S. My custom adapter can use by fragment, activity or custom view.
An alternative would be to create a listener interface like this:
public interface OnStoreItemClickListener {
public void onStoreItemClicked(Store item);
}
Then, in your Adapter, you declare a field of type OnStoreItemClickListener and you create a setter method for it.
When you detect a click, you simply check if your listener is set and call the onStoreItemClicked() method.
You can register a listener via the setter from wherever you need.
My question is a design question. I have two custom fragments CustomFrag1 and CustomFrag2. Both these fragments have a method swapCursor.
CustomFrag1:
public class CustomFrag1 extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
// .... code
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// .... code
MyAdapter mAdapter = new MyAdapter();
// .... code
}
public static void swapCursor(final Cursor cursor, Activity ctx){
// .... code
}
}
CustomFrag2:
public class CustomFrag1 extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
// .... code
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// .... code
MyAdapter mAdapter = new MyAdapter();
// .... code
}
public static void swapCursor(final Cursor cursor, Activity ctx){
// .... code
}
}
I don't want to make a seperate adapter class for each fragment. I do however, want to call swapCursor in the adapter class. If the adapter has been instantied from CustomeFrag1 I want swapCurosr to swap the cursor in CustomFrag1. If the adapter has been instantiated from CustomeFrag2 I want swapCurosr to swap the cursor in CustomFrag2.
Is it possible to pass in an instance of the Fragment...
MyAdapter mAdapter = new MyAdapter(this);
...and somehow represent that instance with a generic variable that has swapCursor defined as a method? If not, what strategy can I use? Interfaces? Generics? Something?
edit
I tried inheriting swapCursor but that would just swap out the cursor out on the parent and not the child.
swapCursor shouldn't be static at the first place, because there's no inheritance for static methods.
You could create an interface (SwappableCursor as a name hint) which has a method swapCursor, then all of your fragments can implement this interface. So that you'll have to implement swapCursor.
In MyAdapter's constructor you can add a parameter which has the type of your interface (SwappableCursor). You'll be able to invoke swapCursor method.
Use interfaces.
public interface SwapFrag {
public swapCursor(final Cursor cursor, Activity ctx);
}
your fragments need to implement SwapFrag:
public class CustomFrag1 extends Fragment implements SwapFrag
and also need to implements the swapCursor method.
#Override
public static void swapCursor(final Cursor cursor, Activity ctx) {
...
}