I want to return a callback from a custom adapter to my fragment. However the adapter won't let me pass the reference of the Fragment implementing the interface defined in custom adapter. How can I receive callbacks from adapter to fragment?
public class MyFrag extends Fragment implements MyInterface
{
#Override
public void onCreateView()
{
MyAdapter adapter = new MyAdapter(this); // error here: How to resolve
}
#Override
public void mySignal()
{
}
}
public class MyAdapter extends RecyclerViewAdapter
{
MyInterface listener;
public MyAdapter(MyInterface listener)
{
this.listener = listener;
}
public interface MyInterface
{
public void mySignal();
}
}
Just for a clear definition and SOC you could try this instead,
public class MyFrag extends Fragment
{
#Override
public void onCreateView()
{
MyAdapter adapter = new MyAdapter(new MyInterface() {
#Override
public void mySignal()
{
//do stuff here
}
});
}
}
As I know, Fragment has this method
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Sorry for my question! What is onCreateView() method without any params?
This way will work fine if there is not any different things more.
Related
I would like to call activity function from Adapter. I am trying something like this:
((MainActivity)mCtx).SampleVoid();
But i've got the error: java.lang.ClassCastException: android.app.Application cannot be cast to
public void SampleVoid(){
Toast.makeText(this,"Testowa metoda",Toast.LENGTH_LONG).show();
}
That error is happening because you are trying to cast the Application context to the Activity context. First of all, I wouldn't recommend you sending and storing context inside the adapter. I would recommend you to create an interface for communication back to the Activity at specific action from the adapter. For example:
Activity code:
SomeAdapter adapter = new SomeAdapter();
adapter.setOnAdapterActionListener(new OnAdapterActionListener() {
#Override
public void onSomeAdapterAction() {
Toast.makeText(this,"Testowa metoda",Toast.LENGTH_LONG).show();
}
});
Adapter code:
public class SomeAdapter extends ArrayAdapter<String> { // extend from the same adapter that you are extending now.
private OnAdapterActionListener onAdapterActionListener;
public SomeAdapter(#NonNull Context context, int resource, #NonNull List<String> objects) {
super(context, resource, objects);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View view = super.getView(position, convertView, parent);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onAdapterActionListener != null) {
onAdapterActionListener.onSomeAdapterAction();
}
}
});
return view;
}
public void setOnAdapterActionListener(OnAdapterActionListener onAdapterActionListener) {
this.onAdapterActionListener = onAdapterActionListener;
}
}
interface OnAdapterActionListener {
void onSomeAdapterAction();
}
You can pass the activity through the Adapter constructor.
Generally you do this through interfaces
public class MainActivity extends AppCompatActivity implements MAdapter.Listener {
private MAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = new MAdapter(this);
}
#Override
public void onClick(int position) { }
}
Then
public class MAdapter {
private Listener listener;
MAdapter(Listener listener){
this.listener = listener;
}
interface Listener {
void onClick(int position);
}
}
Adaptor class that provides an interface:
public class Adaptor{
private ItemCLickCallback itemCLickCallback;
public interface ItemCLickCallback {
void onItemClick(int p);
void onSecItemClick(int p);
}
public void setItemCLickCallback(final ItemCLickCallback itemCLickCallback){
this.itemCLickCallback = itemCLickCallback;
}}
public class Overview extends Fragment implements Adaptor.ItemCLickCallback{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_overview, container, false);
Adaptor adaptor = new Adaptor(mijnwinkels, this.getActivity());
adaptor.setItemCLickCallback(this);
return view;
}
#Override
public void onItemClick(int p) {
}
#Override
public void onSecItemClick(int p) {
// On click: send data to database, remove data from database
// Needs methods that should be implemented in my activity, cant be linked to the activity
}}
So. The methods that are overwritten in my fragment need methods that should be located in my activity, those methods need the data from the fragment. Usually I would use an interface to do this, but these methods are already part of an interface from another class, so how can I put them in an interface so I can do everything from my activity? Thank you
Your question seems to mostly deal with removing duplicate code from the interface, so here's some suggestions.
You don't need to add a whole new Adaptor when you get your data in the onSuccess.
private ArrayList<Winkel> mijnwinkels;
private Adaptor mAdaptor;
onCreateView() {
//... Other code
mijnwinkels = new ArrayList<Winkel>();
// mRecyclerView =...
mAdaptor = new Adaptor(mijnwinkels, this.getActivity());
mRecyclerView.setLayoutManager(new LinearLayoutManager(this.getActivity()));
mRecyclerView.addItemDecoration(new VerticalSpace(30));
mRecyclerView.setAdapter(adaptor);
adaptor.setItemCLickCallback(this);
// etc...
}
#Override
public void onSuccess(ArrayList<Winkel> winkels) {
mijnwinkels.clear();
mijnwinkels.addAll(winkels);
mAdaptor.notifyDatasetChanged(); // something like this... Not sure how RecyclerView does it
}
If you make Winkel implements Parcelable and implement the necessary code for that, you remove some lines there.
#Override
public void onItemClick(int p) {
Winkel winkel = (Winkel) mijnwinkels.get(p);
Bundle detailsBundle = new Bundle();
detailsBundle.putExtra(EXTRA_WINKEL,winkel);
Details detail= new Details();
detail.setArguments(detailsBundle);
this.getFragmentManager().beginTransaction()
.replace(R.id.mycontainer,detail,null)
.addToBackStack(null)
.commit();
}
Though, really, all this can be written as its own method.
public static void showWinkel(Winkel winkel, FragmentManager fm) {
Bundle detailsBundle = new Bundle();
detailsBundle.putExtra(EXTRA_WINKEL,winkel);
Details detail= new Details();
detail.setArguments(detailsBundle);
fm.beginTransaction()
.replace(R.id.mycontainer,detail)
.addToBackStack(null)
.commit();
}
And called from anywhere
#Override
public void onItemClick(int p) {
showWinkel(mijnwinkels.get(p), this.getFragmentManager());
}
instead of
adaptor.setItemCLickCallback(this);
Once use below code for your fragment:
adaptor.setItemCLickCallback(new Adaptor.ItemCLickCallback () {
#Override
public void onItemClick(int position) {
}
});
I have an adapter which create fragments this way :
public class FooAdapter<T extends Fragment> extends FragmentPagerAdapter {
private Class<T> c;
public FooAdapter(FragmentManager fm,Class<T> c) {
super(fm);
this.c = c;
}
#Override
public Fragment getItem(int position) {
try {
T fragment = c.newInstance();
return fragment;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
instantiation of the adapter:
new FooAdapter<BarFragment>(getSupportFragmentManager(),BarFragment.class)
I put logs in onCreate(and onAttach,onCreateView) of BarFragment but nothing logged !
public class BarFragment extends Fragment{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "on create view");
}
}
For starters, your onCreate method is wrong, you should use the onCreateView method and forget onCreate.
Something like this:
public class BarFragment extends Fragment{
public BarFragment(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(LAYOUT YOU WANT TO INFLATE, container, false);
return rootView;
}
}
I'l look more deeply into your adapter code now.
Just for reference I am been trying to follow the answer to this question
Basic Communication between two fragments
I have 2 Fragments within a ViewPager Adapter along with an Actionbar.
What I have is one fragment produces some data which can (if chosen) inserted to an SQLite table.
The second Fragment simply displays all data in the table, however I am trying to make some communication that as soon as Fragment 1 inserts data into the SQLite table. Fragment 2 is called to refresh its select query (as in do the query again) to automatically show the latest data. At the moment this is manually done with a button which I feel is not great.
This is my interface in Fragment 1
onNumbersSavedListener mCallback;
public interface onNumbersSavedListener
{
public void RequestQueryRefresh();
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try
{
mCallback = (onNumbersSavedListener) activity;
}
catch(ClassCastException e)
{
e.printStackTrace();
}
}
This is the main Activity which contains the ViewPager and implements the interface
public class MainActivity extends FragmentActivity implements TabListener, GenerateFragment.onNumbersSavedListener
This is the main problem I am having which I do not have IDs for the fragments which answer referred in the link stated above does so.
#Override
public void RequestQueryRefresh() {
// TODO Auto-generated method stub
}
TLDR: I am just looking for an easy and clean way for as soon as Fragment 1 saves into DB, fragment 2 updates its list view by re-running its query.
see more about otto lib here : http://square.github.io/otto/
Edited:
public class FragmentA extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
public void saveData(){
//save datas before
BusProvider.getInstance().post(new EventUpdateOtto());
}
}
public class EventUpdateOtto{
public EventUpdateOtto(){
}
}
public class FragmentB extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
#Subscribe
public void subUpdateList(EventUpdateOtto mEventUpdateOtto){
//update yout list here
}
#Override
public void onResume() {
BusProvider.getInstance().register(this);
super.onResume();
}
#Override
public void onPause() {
BusProvider.getInstance().unregister(this);
super.onPause();
}
}
public class BusProvider {
private static final Bus BUS = new Bus();
public static Bus getInstance() {
return BUS;
}
private BusProvider() {
// No instances.
}
}
In your case you can improve your interface:
public interface onNumbersSavedListener
{
public void RequestQueryRefresh(Bunde bundle/*or something other*/);
}
If you are using a cursor loader, the change should automatically be reflected in the fragment displaying it. However, the fragment that wants immediate updates whenever the table is changed can register as an observer to that table:
// observer to the table
class MyObserver extends ContentObserver {
final Handler mHandler;
public MyObserver(Handler handler) {
super(handler);
// I used a handler to get back to my UI thread
mHandler = handler;
}
#Override
public void onChange(boolean selfChange) {
this.onChange(selfChange, null);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
Log.i(TAG, "MyObserver: onChange");
// do what you want to do - this is what I implemented
mHandler.post(myRunnable);
}
}
Then, register it:
mHandler = new Handler();
mObserver = new MyObserver(mHandler);
ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(uri, false, mEventLogObserver);
The other fragment should then do a notify:
getContext().getContentResolver().notifyChange(uri, null);
The key is the uri - one watches it, the other notifies.
I'm trying out Otto on Android and i'm trying to send back a message from my Fragment to the Activity. Here's the basics of my code:
My Bus provider:
public final class BusProvider {
private static final Bus mInstance = new Bus();
private BusProvider() {}
public static Bus getBusProviderInstance() {
return mInstance;
}
}
My Activity has the following code:
public class MyActivity
extends BaseActivity {
// ....
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BusProvider.getBusProviderInstance().register(this);
// ....
}
#OnClick(R.id.profile_image)
public void onImageClicked() {
// ...
MyFragment fragment = MyFragment.newInstance(choices);
fragment.show(getFragmentManager(), "myChoices");
}
#Subscribe
public void onButtonChoicePicked(MyFragment.ChoicePickedEvent event) {
Toast.makeText(this, "reaching here", Toast.LENGTH_SHORT).show();
}
#Override
protected void onStop() {
super.onStop();
BusProvider.getBusProviderInstance().unregister(this);
}
// ...
}
and these are the important bits of code from my Fragment:
public class MyFragment
extends BaseDialogFragment {
// ...
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.dialog_choices,
container,
false);
setupDialogButtons(inflater, layout);
return layout;
}
private void setupDialogButtons(LayoutInflater inflater, LinearLayout parentView) {
ChoiceButtonViewHolder holder;
holder = new ChoiceButtonViewHolder(inflater, parentView);
holder.populateContent("First Choice", 1);
parentView.addView(holder.mChoiceTextView);
}
class ChoiceButtonViewHolder {
#InjectView(R.id.item_dialog_choice_desc) TextView mChoiceTextView;
private int mPosition;
ChoiceButtonViewHolder(LayoutInflater inflater, ViewGroup container) {
TextView mChoiceTextView = (TextView) inflater.inflate(R.layout.item_dialog_choice, container, false);
ButterKnife.inject(this, mChoiceTextView);
}
public void populateContent(String choiceDesc, int position) {
mChoiceTextView.setText(choiceDesc);
mPosition = position;
}
#OnClick(R.id.item_dialog_choice_desc)
public void onChoiceClicked() {
MyFragment.this.mDialog.dismiss();
BusProvider.getBusProviderInstance().post(new ChoicePickedEvent(1));
}
}
public static class ChoicePickedEvent {
public int mPositionClicked;
ChoicePickedEvent(int position) {
mPositionClicked = position;
}
}
}
I don't get any errors. But when i click my button from the fragment, the event onButtonChoicePicked doesn't get called.
Am I doing something wrong?
Am i misunderstanding how Otto works?
Is it a weird combination of ButterKnife and Otto that makes it not work?
Make sure you are importing "com.squareup.otto.Subscribe" not "com.google.common.eventbus.Subscribe"
The example code works without any issues independently. The reason i was facing this problem initially (as was rightly pointed out by #powerj1984): There was a misconfiguration in my project, where the bus that was being injected (via Dagger) was different from the bus instance that was being subscribed to for updates :P.
Lesson learnt: make sure the bus you use, is the same instance in both cases.