Android: Fragments using a method from MainActivity not defined in the interface? - java

So I'm having some design pattern confusion/issues. I have a Fragment that needs to call methods from the MainActivity that's not defined in the Fragment's interface. An obvious solution would be just to cast the attached Activity as a MainActivity, but doesn't that defeat the purpose of the interface?
This is my MainActivity has methods a(), b(), and c():
public class MainActivity extends Activity implements AInterface, CInterface {
#Override
public void a(){//method A body}
public void b(){//method b body} //no Override
#Override
public void c(){//method C body}
}
This is my Fragment Class and NEEDS a(), b() and c().
Method c() is from another Fragment's interface:
public class SomeFragment extends Fragment{
private AInterface mActivity;
#Override
public void onAttach(Activity activity) {
mActivity = (AInterface)activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.my_layout, container, false);
mActivity.a();
(MainActivity) mActivity.b(); //is it ok to just call it like this?
(MainActivity) mActivity.c(); //is it ok to just call it like this?
return v;
}
public interface AInterface{
public void a();
}
}
Is this the correct way of calling those methods? Thanks.

I would not do it like this, since your are creating a tight coupling between your Activity and your fragment.
A better approach would be to use a callback.
public class MenuFragment extends Fragment {
public interface Interface1{
public void b();
}
public interface Interface2 extends Interface1{
public void a();
}
public interface Interface3 extends Interface1{
public void c();
}
Interface2 mCallback;
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try {
mCallback = (Interface2 ) activity;
}
catch (ClassCastException e) {
throw new ClassCastException(activity.toString()+ " must implement ItemselectedCallback");
}
}
#Override
public void onDetach(){
mCallback = null;
super.onDetach();
}
//somewhere
mCallback.a();
}
Your activity would then implement the callback interface and handle the callbacks.
public class TaskActivity extends Activity implements MenuFragment.Inferface2, MenuFragment.Interface3{
public void a() {
//do something
}
public void b() {
//do something
}
public void c() {
//do something
}
}

Yes you can do that and it will work, BUT you will also need to be careful when the casting failed (solveable with instanceof)
It will also make your SomeFragment class coupled to your MainActivity class which is not a best practice.
I think the better and safer approach is to use interface.

Related

Implementing generic type interface

Let's say I have the following interfaces:
public interface MvpView { }
public interface MvpPresenter<V extends MvpView> {
void attach(V view);
}
Then a base class that implements MvpPresenter
public class BasePresenter<V extends MvpView> implements MvpPresenter<V> {
private V view;
#Override
public void attach(V view) {
this.view = view;
}
}
And finally the following class that extends BasePresenter
public abstract class BaseFragment<P extends MvpPresenter> implements MvpView {
P presenter;
public void someMethod() {
presenter.attach(this);
}
}
While this compiles it's not safe because there is no guarantee that implementations of the class will implement the correct MvpView (the one that P presenter uses). In order to be safe I've left out the code of someMethod and every implementation must fill it with the exact same code presenter.attach(this)
What I'd like to do but I don't know how is something like the following:
public abstract class BaseFragment<V extends MvpView, P extends MvpPresenter<V>> implements V {
P presenter;
public void someMethod() {
presenter.attach(this);
}
}
Is there any way to achieve this?
Declare BaseFragment like this.
abstract class BaseFragment<P extends MvpPresenter<R>, R extends BaseFragment<P,R>> implements MvpView {
P presenter;
public void someMethod() {
presenter.attach(getThis());
}
abstract R getThis();
}
}
and use the extensions like this,
class MyFragment extends BaseFragment<MvpPresenter<MyFragment>, MyFragment> {
#Override
MyFragment getThis() {
return this;
}
}
This compiles:
public abstract class BaseFragment<P extends MvpPresenter<BaseFragment>> implements MvpView {
P presenter;
public void someMethod() {
presenter.attach(this);
}
}
But I haven't found a way to actually subclass it. I am not sure if MvpView is actually required for anything. Maybe something like this will be useful:
public interface MvpPresenter<V> {
void attach(V view);
}
public class BasePresenter<V> implements MvpPresenter<V> {
private V view;
#Override
public void attach(V view) {
this.view = view;
}
}
public abstract class BaseFragment<R extends BaseFragment> {
MvpPresenter<R> presenter;
public void someMethod() {
presenter.attach(getThis());
}
abstract R getThis();
}
public class MyBaseFragment extends BaseFragment<MyBaseFragment> {
#Override
MyBaseFragment getThis() {
return this;
}
}
Based on boobalan gnanasekaran and user158037 answers, I did it like this:
public interface MvpView { }
public interface MvpPresenter<V extends MvpView> {
void attach(V view);
}
public class BasePresenter<V extends MvpView> implements MvpPresenter<V> {
private V view;
#Override
public void attach(V view) {
this.view = view;
}
public V getView() {
return view;
}
}
public abstract class BaseFragment<V extends MvpView, P extends MvpPresenter<V>> {
P presenter;
protected abstract V getThis();
public void someMethod() {
presenter.attach(getThis());
}
public P getPresenter() { return presenter; }
}
And an example implementation
public interface MoviesContract {
interface Presenter extends MvpPresenter<MoviesContract.View> {
void loadMovies();
}
interface View extends MvpView {
void onMoviesLoaded();
}
}
public class MoviesPresenter extends BasePresenter<MoviesContract.View>
implements MoviesContract.Presenter {
#Override
public void loadMovies() {
getView.onMoviesLoaded();
}
}
public class MoviesFragment extends BaseFragment<MoviesContract.View, MoviesContract.Presenter>
implements MoviesContract.View {
#Override
public MoviesContract.View getThis() {
return this;
}
public loadMovies() {
getPresenter().loadMovies();
}
#Override
public void onMoviesLoaded() {
//
}
}

I am not able to figure out how to pass Bundle in mapview.onCreate() in custom viewholder class

i have a generic viewholder class for recyclerview which uses MapView inside that row, and the viewholder class is extending my base viewholder abstract class which has populateData(TO to), i am initializing map in the constructor of my viewholder class and passing null to mapview.onCreate(null).
my classes are look like this
public class MyViewHolder extends BaseViewHolder implements OnReadyMapCallBack{
private MapView mapView;
public MyViewHolder(View view){
mapView = view.findViewById(R.id.map);
mapView.onCreate(null); //here i should pass Bundle instead
}
// this method is declared in parent class abstract class
#override
public void populateData(TO to){
mapView.getAsync(this);
}
public void onMapReady(GoogleMap googleMap) {
}
}
You trued this:
Bundle args = new Bundle();
args.putString("key",value);
mapView.onCreate(args);
?

onBindViewHolder must be declared abstract

I am getting the error : class "FlickrRecyclerViewAdapter" must either be declared abstract or implement method abstract method onBindViewHolder(VH, int) in 'Adapter'
While this error should be self explanitory, I have implemended onBindViewHolder and the error is still present.
Relvant Code -
public class FlickrRecyclerViewAdapter extends RecyclerView.Adapter {
private List<Photo> mPhotosList;
private Context mContext;
public FlickrRecyclerViewAdapter(Context context, List<Photo> photosList) {
mContext = context;
this.mPhotosList = photosList;
}
#Override
public void onBindViewHolder(FlickrImageViewHolder flickrImageViewHolder, int i) {
Photo photoItem = mPhotosList.get(i);
Picasso.with(mContext).load(photoItem.getmImage())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(flickrImageViewHolder.thumbnail);
}
}
Thanks
The definition of The adapter is
RecyclerView.Adapter<VH extends android.support.v7.widget.RecyclerView.ViewHolder>
where VH is the a subclass of ViewHolder. So you can either change the signature of you onBindViewHolder like
#Override
public void onBindViewHolder(RecyclerView.ViewHolder flickrImageViewHolder, int i) {
or change
extends RecyclerView.Adapter {
with
extends RecyclerView.Adapter<FlickrImageViewHolder> {
RecyclerView.Adapter that you have extended has a method :
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
that you have to override. With the method signature that your are using you have to extend like this :
public class FlickrRecyclerViewAdapter extends RecyclerView.Adapter<FlickrImageViewHolder>

Set button in custom view (canvas)

I would like to set up a button that would allow me to move to another activity while I finished drawing my painting.
The question is I don't know how to set up this button in the view class because it doesn't listen to any listeners.
In your drawing view, define your interface
public class MyDrawingView extends View
{
protected MyPaintingListener m_paintingListener;
public interface MyPaintingListener
{
// you can define any parameter as per your requirement
public void paintingEnded();
}
public void onCreateView()
{
// Create your view
}
public void draw()
{
// Draw your painting
// then
if(m_paintingListener != null)
m_paintingListener.paintingEnded();
}
public void setListener(MyPaintingListener p_listener)
{
m_paintingListener = p_listener;
}
}
In your current Fragment or Activity:
public class MyActivity extends Activity
implements MyDrawingView.MyPaintingListener
{
protected MyDrawingView m_drawingView;
public void OnActivityCreated(Bundle savedInstanceState)
{
// In this method or another, create your drawingView
m_drawingView = new MyDrawingView();
m_drawingView.setListener(this);
m_drawingView.paint();
}
#Override
public void paintingEnded()
{
// Set up your button;
}
}

How to create interface between Fragment and adapter?

I have fragment with ListView, say MyListFragment, and custom CursorAdapter.
I'm setting onClickListener in this adapter for the button in the list row.
public class MyListAdapter extends CursorAdapter {
public interface AdapterInterface {
public void buttonPressed();
}
...
#Override
public void bindView(final View view, final Context context, final Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
...
holder.button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// some action
// need to notify MyListFragment
}
});
}
}
public MyListFragment extends Fragment implements AdapterInterface {
#Override
public void buttonPressed() {
// some action
}
}
I need to notify fragment when the button is pressed. How to invoke this interface?
Help, please.
Make a new constructor and an instance variable:
AdapterInterface buttonListener;
public MyListAdapter (Context context, Cursor c, int flags, AdapterInterface buttonListener)
{
super(context,c,flags);
this.buttonListener = buttonListener;
}
When the Adapter is made, the instance variable will be given the proper reference to hold.
To call the Fragment from the click:
public void onClick(View v) {
buttonListener.buttonPressed();
}
When making the Adapter, you will have to also pass your Fragment off to the Adapter. For example
MyListAdapter adapter = new MyListAdapter (getActivity(), myCursor, myFlags, this);
since this will refer to your Fragment, which is now an AdapterInterface.
Keep in mind that on orientation of the Fragment changes, it will most likely be recreated. If your Adapter isn't recreated, it can potentially keep a reference to a nonexistent object, causing errors.
Using Eventbus:
Examples:
https://github.com/kaushikgopal/RxJava-Android-Samples/tree/master/app/src/main/java/com/morihacky/android/rxjava/rxbus
or
https://github.com/greenrobot/EventBus
Using Interfaces:
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 a Fragment.
Create Callback Interface:
public interface AdapterCallback {
void onMethodCallback();
}
Passing in Callback/Fragment:
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.
In your Fragment:
public class MyFragment extends Fragment implements AdapterCallback {
private MyAdapter mMyAdapter;
#Override
public void onMethodCallback() {
// do something
}
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mMyAdapter = new MyAdapter(this); // this class implements callback
}
}
Use the Callback in your Adapter:
In the Fragment, 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 mAdapterCallback;
public MyAdapter(AdapterCallback callback) {
this.mAdapterCallback = callback;
}
#Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
// simple example, call interface here
// not complete
viewHolder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mAdapterCallback.onMethodCallback();
}
});
}
}
or Use the Fragment in your Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private AdapterCallback mAdapterCallback;
public MyAdapter(Fragment fragment) {
try {
this.mAdapterCallback = ((AdapterCallback) fragment);
} catch (ClassCastException e) {
throw new ClassCastException("Fragment must implement AdapterCallback.");
}
}
#Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
// simple example, call interface here
// not complete
viewHolder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
try {
mAdapterCallback.onMethodCallback();
} catch (ClassCastException exception) {
// do something
}
}
});
}
}
Follow the 2 steps below for receive callback from Adapter in Fragment (or Activity)
First: In your Adapter
public class ListAdapter extends RecyclerView.Adapter < RecyclerListAdapter.ItemViewHolder > {
...
private ListAdapterListener mListener;
public interface ListAdapterListener { // create an interface
void onClickAtOKButton(int position); // create callback function
}
public RecyclerListAdapter(Context mContext, ArrayList < Items > listItems, ListAdapterListener mListener) { // add the interface to your adapter constructor
...
this.mListener = mListener; // receive mListener from Fragment (or Activity)
}
...
public void onBindViewHolder(final ItemViewHolder holder, final int position) {
holder.btnOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// use callback function in the place you want
mListener.onClickAtOKButton(position);
}
});
...
}
...
}
Second: In your Fragment (or Activity), there are 2 ways for implement callback method
Way 1
public MyListFragment extends Fragment {
...
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
...
ListAdapter adapter = new ListAdapter(getActivity(), listItems, new ListAdapter.ListAdapterListener() {
#Override
public void onClickAtOKButton(int position) {
Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();
}
});
...
}
}
Way 2
public MyListFragment extends Fragment implements ListAdapter.ListAdapterListener {
...
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
ListAdapter adapter = new ListAdapter (getActivity(), listItems, this);
...
}
#Override
public void onClickAtOKButton(int position) {
Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();
}
}
This is very similar to the way an activity and a fragment should communicate. In the constructor of your adapter, pass a reference of your fragment, cast it to your interface and just call yourReference.buttonPressed() on your onClick method.
a solution for NPE is first to make conctractor in your Fragment like that
public MyFragment MyFragment(){
return this;
}
then initialize your listener is adapter like that
Lisener lisener = new MyFragment();
Make a constructor like that:
public MyAdapter(Activity activity,AlertMessageBoxOk alertMessageBoxOk) {
this.mActivity = activity;
mAlertMessageBoxOk = alertMessageBoxOk;
}
call the interface from adapter use any event
mAlertMessageBoxOk.onOkClick(5);
after that implement AlertMessageBoxOk interface to your fragment like this,
class MyFragment extends Fragment implements AlertMessageBoxOk {
#Override
public void onOkClick(int resultCode) {
if(resultCode==5){
enter code here
}
}
}

Categories