Not sure what's wrong with my observer pattern:
I have defined my interface as:
public interface OnBackFilterPressListener {
public ArrayList<FoodType> filterFoodType ();
}
When I press the back button, I want the listener to be activated - all this code is in my activity class:
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackFilterPressListener.filterFoodType();
}
}
In my adapter class, I have the following code:
public class RandomRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements SearchActivity.OnBackFilterPressListener {
#Override
public ArrayList<FoodType> filterFoodType() {
return foodTypes;
}
}
When I press the back button in my activity, I just want the array of data in my adapter to be returned in my activity class.
But I'm getting this error:
java.lang.NullPointerException: Attempt to invoke interface method
'java.util.ArrayList
com.example.simon..SearchActivity$OnBackFilterPressListener.filterFoodType()'
on a null object reference
foodType definitely exists - the adapter loads the data into activity and I can see it being displayed on the android emulator. I get this error when I click the back button.
I got it to work by following this thread and being a little creative:
How to create our own Listener interface in android?
I wrote a custom object to prevent the null object reference as to use the interface, I would first have to create the object:
public class BackFilterEvent implements Serializable {
private OnEventListener mOnEventListener;
public void setOnEventListener(OnEventListener listener) {
mOnEventListener = listener;
}
public ArrayList<FoodType> doEvent() {
if (mOnEventListener != null) {
return mOnEventListener.onEvent();
}
return null;
}
public interface OnEventListener {
ArrayList<FoodType> onEvent();
}
}
Then in my Activity class, I created the object:
BackFilterEvent backFilterEvent = new BackFilterEvent();
And I pass it as far as it needs to go in my code. In my case, I had an activity that feed a fragment with an recyclerview adapter within the fragment. I passed this object until it got to the recyclerview adapter.
Passing object from Activity to Fragment:
FilterSearchFragment filterSearchFragment = new FilterSearchFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("listener", backFilterEvent);
filterSearchFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, filterSearchFragment)
.commit();
Passing from Fragment to Adapter:
Bundle bundle = getArguments();
BackFilterEvent backFilterEvent = (BackFilterEvent) bundle.getSerializable("listener");
RandomRecyclerViewAdapter randomRecyclerViewAdapter = new RandomRecyclerViewAdapter(getContext(), backFilterEvent);
In my adapter constructor, I set it to do the work once I call it from the activity class:
this.backFilterEvent = backFilterEvent;
backFilterEvent.setOnEventListener(new BackFilterEvent.OnEventListener() {
#Override
public ArrayList<FoodType> onEvent() {
return foodTypes;
}
});
And in my back button in my activity class, I call the listener, which will trigger the work in the adapter class:
back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ArrayList<FoodType> foodTypes = backFilterEvent.doEvent(); }
Related
I have a fragment (FragmentSearchResults) that contains results retrieved from a database, in which there is a button "filters". When the user taps on such a button, a class (FiltersDialog) extending a BottomSheetDialogFragment is instantiated, so that the user can set his filters. When the user closes the FiltersDialog activity, the values are passed from FiltersDialog to FragmentSearchResults.
public class FragmentSearchResults extends Fragment implements FiltersDialog.FilterListener {
/* code */
ImageButton btnFilter = myFragment.findViewById(R.id.btn_filters);
btnFilter.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showFilters();
}
});
}
private void showFilters() {
FiltersDialog filtersDialog = new FiltersDialog();
filtersDialog.show(((FragmentActivity) mContext).getSupportFragmentManager(), "argument");
}
#Override
public void onAttach(#NotNull Context context) {
super.onAttach(context);
mContext = context;
}
#Override
public void onFiltersSet(Map filters) {
// apply filters selected by user
}
public interface FilterListener {
void onFiltersSet(Map filters);
}
}
public class FiltersDialog extends BottomSheetDialogFragment {
private FilterListener mListener;
private Map<String, Object> mFilters;
public FiltersDialog() {
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.layout_filters_dialog, container, false);
TextView txtSave = v.findViewById(R.id.txt_save_filters);
mTxtSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mListener.onFiltersSet(mFilters);
}
});
return v;
}
public interface FilterListener {
void onFiltersSet(Map filters);
}
#Override
public void onAttach(#NotNull Context context) {
super.onAttach(context);
if (context instanceof FilterListener) {
mListener = (FilterListener) context;
}
else {
// Here's the error, as the activity Home.java containing FragmentSearchResults
// does not implement FilterListener, FragmentSearchResults does
throw new RuntimeException(context.toString() + " must implement FilterListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
}
The problem is that FilterListener needs to be implemented in FragmentSearchResults, but I am passing the activity Home context.
How can I implement FilterListener in the fragment?
Why don't you create method inside your FiltersDialog, like
public void setFiltersListener(FiltersDialog.FilterListener listener) {
mListener = listener;
}
and simply call it after you instantiate the dialog.
FiltersDialog filtersDialog = new FiltersDialog();
filtersDialog.setFiltersListener(this);
filtersDialog.show(((FragmentActivity) mContext).getSupportFragmentManager(), "argument");
Then you can use the listener inside dialog. something like this
if (mListener != null) {
mListener.onFiltersSet(mFilters);
}
How can I setup listener to the dialog?
parameter of onAttach in Fragment is FragmentHost(Activity). thus, it can't typecast to FilterListener.
I suggest a simple way to implement FilterListener setter in FragmentDialog as below code.
... in FiltersDialog
public void setListener(FilterListener listener) {
mListener = listener;
}
...
... in FragmentSearchResults
private void showFilters() {
FiltersDialog filtersDialog = new FiltersDialog();
filtersDialog.setListener(this);
filtersDialog.show(((FragmentActivity) mContext).getSupportFragmentManager(), "argument");
}
...
//When FragmentSearchResults recreated, FiltersDialog must also need to be recreated.
A better approach will be to use LiveData, ViewModel in this case. Use Shared ViewModel Approach, An Activity Level ViewModel can be accessed via all the fragments lying in its environment.
Make an Activity Level ViewModel
Define a LiveData in ViewModel
When your "FragmentSearchResults" opens for the first time, start
observing it.
When You open "FiltersDialog" screen and click save button, Then post
to LiveData changes in the filter (You have activity context here,
You can fetch ActivityViewModel here, get LiveData from it, post
changes to this LiveData)
Now As "FragmentSearchResults" is already observing changes in the
LiveData, You will get callback here, make changes accordingly. This way your code will be completely decoupled. You will be escaped from
hustles of Interfaces.
I wanted to PASS A MODEL OBJECT from Fragment1 to Fragment2using interface (Both fragments in the same activity).
I received that object in a public class of Fragment2. Now I want to save the received object as a Global variable of Fragment2. So that I can use it other methods of the same Fragment2.
But... But...
when I try to use the global object in onCreateView(),
getting NULL POINTER EXCEPTION.
In Fragment1,
1. Interface declaration
interface CommunicatePricePlanCheckOutInterface {
void sendDataToCheckout( SinglePricePlanModel singleModel );
}
2. Object of Interface
CommunicatePricePlanCheckOutInterface communicationObject;
3. passing data using method of interface in, onCreateView()
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_price_plan,
container,false);
allSampleData = new ArrayList<>();
mPricePlanContinueButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v) {
if (getActivity() != null) {
communicationObject.sendDataToCheckout(
allSampleData.get(0));
}
}
});
return view;
}
In activity,
4. implementing the interface
public class NavDrawerActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener,
Fragment1.CommunicatePricePlanCheckOutInterface
5. overriding interface method and in the method calling a public method of Fragment2.
#Override
public void sendDataToCheckout(SinglePricePlanModel pricePlanModel ) {
Fragment2 frag2 = new Fragment2();
frag2.receivePlanData(pricePlanModel, mUserBasicInfo);
}
In Fragment2,
6. Received 2 objects from Activity, I can use them in this method, but can not access those mSinglePricePlanModel, mUserBasicInfo objects outside this method, like onCreateView or onViewCreated. Getting Null Pointer Exception if I try to
access.
public void receivePlanData(SinglePricePlanModel mSinglePricePlanModel , UserBasicInfo mUserBasicInfo ){
this.mSinglePricePlanModel = mSinglePricePlanModel;
this.mUserBasicInfo = mUserBasicInfo;
}
Have you initialized your interface object? I don't see any initialization in your fragment, that could be causing the null pointer exception. Initialize it inside onAttach method in your fragment like this-
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof CommunicatePricePlanCheckOutInterface) {
communicationObject = (CommunicatePricePlanCheckOutInterface) context;
}
}
It should not give null pointer exception then. Refer to this link for more details.
Im trying to listen or pass data from an BotomSheetDialogFragment into Fragment to change something on the Fragment (Just like a picker).
I've tried with getTargetFragment to instantiate the listener but getting a compiler error Found: 'MyFragment', required: 'android.support.v4.app.Fragment' less..
Any ideas or i'm takin the wrong approach?
public class MyBottomSheetDialogFragment extends BottomSheetDialogFragment implements View.OnClickListener {
ReportType reportType;
public interface OnChooseReasonListener {
void onChooseReason(ReportType reportType);
}
OnChooseReasonListener listener;
#Override
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(), R.layout.picker_bottom_sheet_, null);
dialog.setContentView(contentView);
CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
//get null here!!!:
listener = (OnChooseReasonListener) getParentFragment();// or with getTargetFragment();
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.cool_button:
this.reportType = ReportType.ME;
//trying to execute the lisstener on null
listener.onChooseReason(this.reportType);
dismiss();
break;
}
}}
And the fragment:
public class MyFragment extends Fragment
implements View.OnClickListener,
MyBottomSheetDialogFragment.OnChooseReasonListener {
//....code here
public void showPicker() {
//getting and compiler error Wrong 1st argument type.
// picker. setTargetFragment(MyFragment.this , 300);
picker.show(fm, picker.getTag());
}
#Override
public void onChooseReason(ReportType reportType) {
//not getting here
Log(TAG, "You choose something" + reportType.getValue());
}
}
Besides that it's not working, that code smells a little since you're coupling MyBottomSheetDialogFragment with the object that created it.
The correct approach would be to have a method void setOnChooseReasonListener(OnChooseReasonListener listener) on MyBottomSheetDialogFragment and call it when you create the instance.
myBottomSheetDialogFragment.setOnChooseReasonListener(this);
You can approach this by using the interface
First
Create an interface class
interface CustomInterfaceClass {
public void callbackMethod(String date);
}
Second,
Initialize the interface class in Activity or fragment
As I am using in the fragments class
//interface for callback
private CustomInterface callback;
Third, Make sure you have initialized the callback interface object within the onCreateView or OnCreate method.
//if you facing an error while initializing such as this keyword
not assigned to the callback method that means you didn't implement
the interface fragmentAclass.
callback=this;
Fourth,
Don't forget to implement the override method within the FragmentAClass
#Override
public void callbackMethod(String date) {
Toast.makeText(getContext(), "Yes"+date, Toast.LENGTH_SHORT).show();
}
Fifth,
Now move to BottomSheetDialogFragment or FragmentBclass
Add callback method constructor such as this
private CustomInterface callback;
public Disconnect_fragment( CustomInterface callback) {
this.callback=callback;
}
public Disconnect_fragment( ) {
}
Lastly Now you can pass the value by using this method and will receive in the FragmentAclass
callback.callbackMethod("your passing value");
I am getting the data from JSON using volley, there is 3 Fragment in Activity which I have to populate these fragments with the data that I have received from JSON in MainActivity. I would like to get data from JSON once and use it in Activity and in all those 3 Fragment. I put the received data in a List like this:
List<Display_Activity_Model> videoDetailList;
and I send this list from activity to other fragments using an Interface like:
In activity I have this method:
#Override
public List<Display_Activity_Model> getDataList() {
return videoDetailList;
}
In one of fragments I have:
public interface GetDataInterface {
List<Display_Activity_Model> getDataList();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
sGetDataInterface= (GetDataInterface) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement GetDataInterface Interface");
}
}
#Override
public void onResume() {
super.onResume();
if(sGetDataInterface != null){
dataListFromDispAct = sGetDataInterface.getDataList();
}
}
When I debug the code, I see that the method getDataList in MainActivity is called before the method for fetching json with volley. So all the time I receive empty list.
My question is that: What is the best way to fetch data with volley once and use it in other fragments?
Update1:
#Keita Junichiro:
I defined "update" method in fragment like:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.video_info,container,false);
initComponents(view);
if (dataListFromDispAct != null) {
txt_view_count.setText(dataListFromDispAct.get(0).getView_count());
}
return view;
}
public void update(List<Display_Activity_Model> videoDetailList){
dataListFromDispAct=videoDetailList;
}
and I am calling "update" method in activity after json loaded in "getItemMenuGson" method:
getItemMenuGson(new CallBack() {
#Override
public void onSuccess(List<Display_Activity_Model> itemMenuGsonList) {
videoDetailList=itemMenuGsonList;
new Video_Info().update(videoDetailList);
}
#Override
public void onFail(String msg) {
}
});
The problem is method "onCreateView" in fragment is called before update method. When I declare update method static it is also the same and variable "dataListFromDispAct" is null all the time.
Update2:
Update 3:
#Piyush
I implemented your respond, but it is not working in this problem. I am getting empty list because method onCreateView executing earlier than getItemMenuGson method in Activity. I declared :
SharedApplication mapp = SharedApplication.getInstance();
ArrayList<String> myList = mapp.getArrayListData();
in onActivityCreated in fragment but it is also running before getItemMenuGson method in Activity. The order in which methods are calling:
what should I do? How can I call getItemMenuGson method in Activity to fetch JSON and then load fragment layout to populate those loaded data to the fragment?
Solution: The key for the problem was Static method which runs first:
Define below codes in Fragment:
public class Video_Info extends Fragment {
static TextView txt_view_count;
List<Display_Activity_Model> dataListFromDispAct;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view=inflater.inflate(R.layout.video_info,container,false);
txt_view_count= (TextView) view.findViewById(R.id.txt_view_count);
return view;
}
public static void update(List<Display_Activity_Model> videoDetailList)
{
txt_view_count.setText(videoDetailList.get(0).getView_count());
}
}
In Activity after fetching data from JSON to list call update method:
getItemMenuGson(new CallBack() {
#Override
public void onSuccess(List<Display_Activity_Model> itemMenuGsonList) {
videoDetailList = itemMenuGsonList;
//////////////////
Video_Info.update(videoDetailList);
///////////////////
Uri vidUri = Uri.parse(itemMenuGsonList.get(0).getMedia().getURL());
vidView.setVideoURI(vidUri);
}
In order to send data from activity to fragment after call API, you can create a method update() in fragment to receive data and call fragment.update() in activity when loadData finish().
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//Parse resonse to *videoDetailList*
fragment.update(videoDetailList);
}
}
First make one singletone application class
public class SharedApplication extends Application{
ArrayList<String> arraylist;
public static SharedApplication instance=null;
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
}
public static SharedApplication getInstance()
{
if(instance==null)
{
instance=new SharedApplication();
}
return instance;
}
public void setArrayListData(ArrayList<String> setData)
{
arraylist=setData;
}
public ArrayList<String> getArrayListData()
{
return arraylist;
}
}
Now in your activity initialize your application class using
SharedApplication mApp; // declare global
initialize in onCreate() method
mApp = SharedApplication.getInstance();
Now after parse your data from server set data in arraylist
mApp.setArrayListData(yourArrayList);
Now whenever you want to get data any of fragment you should same initialize your application class in each fragment in which u want to get arraylist.
Like,
SharedApplication mapp = SharedApplication.getInstance();
ArrayList<String> myList = mapp.getArrayListData();
Note : You must add SharedApplication class in your manifest to application tag
Make an interface with a method onDownloadComplete.
public interface DelegateClass {
public static onDownloadComplete(String jsonData);
}
Now fetch data with Volley in Activity and call this method. Implement this interface in all your fragments and override onDownloadComplete. This method will be called when your Activity finishes fetching data. Like
public class YourFragment implements DelegateClass {
...
...
#Override
public static onDownloadComplete(String jsonData){
//do what you want with data
}
...
...
}
Hope it helps
Is it possible to call method that is defined in Activity from ListAdapter?
(I want to make a Button in list's row and when this button is clicked, it should perform the method, that is defined in corresponding Activity. I tried to set onClickListener in my ListAdapter but I don't know how to call this method, what's its path...)
when I used Activity.this.method() I get the following error:
No enclosing instance of the type Activity is accessible in scope
Any Idea ?
Yes you can.
In the adapter Add a new Field :
private Context mContext;
In the adapter Constructor add the following code :
public AdapterName(......, Context context) {
//your code.
this.mContext = context;
}
In the getView(...) of Adapter:
Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
if (mContext instanceof YourActivityName) {
((YourActivityName)mContext).yourDesiredMethod();
}
}
});
replace with your own class names where you see your code, your activity etc.
If you need to use this same adapter for more than one activity then :
Create an Interface
public interface IMethodCaller {
void yourDesiredMethod();
}
Implement this interface in activities you require to have this method calling functionality.
Then in Adapter getView(), call like:
Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
if (mContext instanceof IMethodCaller) {
((IMethodCaller) mContext).yourDesiredMethod();
}
}
});
You are done. If you need to use this adapter for activities which does not require this calling mechanism, the code will not execute (If check fails).
You can do it this way:
Declare interface:
public interface MyInterface{
public void foo();
}
Let your Activity imlement it:
public class MyActivity extends Activity implements MyInterface{
public void foo(){
//do stuff
}
public onCreate(){
//your code
MyAdapter adapter = new MyAdapter(this); //this will work as your
//MyInterface listener
}
}
Then pass your activity to ListAdater:
public MyAdapter extends BaseAdater{
private MyInterface listener;
public MyAdapter(MyInterface listener){
this.listener = listener;
}
}
And somewhere in adapter, when you need to call that Activity method:
listener.foo();
Original:
I understand the current answer but needed a more clear example. Here is an example of what I used with an Adapter(RecyclerView.Adapter) and an Activity.
In your Activity:
This will implement the interface that we have in our Adapter. In this example, it will be called when the user clicks on an item in the RecyclerView.
public class MyActivity extends Activity implements AdapterCallback {
private MyAdapter myAdapter;
#Override
public void onMethodCallback() {
// do something
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myAdapter = new MyAdapter(this);
}
}
In your Adapter:
In the Activity, we initiated our Adapter and passed this as an argument to the constructer. This will initiate our interface for our callback method. You can see that we use our callback method for user clicks.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private AdapterCallback adapterCallback;
public MyAdapter(Context context) {
try {
adapterCallback = ((AdapterCallback) context);
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement AdapterCallback.", e);
}
}
#Override
public void onBindViewHolder(MyAdapter.ViewHolder viewHolder, int position) {
// simple example, call interface here
// not complete
viewHolder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
try {
adapterCallback.onMethodCallback();
} catch (ClassCastException e) {
// do something
}
}
});
}
public static interface AdapterCallback {
void onMethodCallback();
}
}
Basic and simple.
In your adapter simply use this.
((YourParentClass) context).functionToRun();
For Kotlin:
In your adapter, simply call
(context as Your_Activity_Name).yourMethod()
One more way is::
Write a method in your adapter lets say
public void callBack(){}.
Now while creating an object for adapter in activity override this method.
Override method will be called when you call the method in adapter.
Myadapter adapter = new Myadapter() {
#Override
public void callBack() {
// dosomething
}
};
In Kotlin there is now a cleaner way by using lambda functions, no need for interfaces:
class MyAdapter(val adapterOnClick: (Any) -> Unit) {
fun setItem(item: Any) {
myButton.setOnClickListener { adapterOnClick(item) }
}
}
class MyActivity {
override fun onCreate(savedInstanceState: Bundle?) {
var myAdapter = MyAdapter { item -> doOnClick(item) }
}
fun doOnClick(item: Any) {
}
}
For kotlin you could do something like :
if(context is MainActivity){ context.functionToCall(values) }
if (parent.getContext() instanceof yourActivity) {
//execute code
}
this condition will enable you to execute something if the Activity which has the GroupView that requesting views from the getView() method of your adapter is yourActivity
NOTE : parent is that GroupView