Skip to content

Instantly share code, notes, and snippets.

@zeroarst
Last active February 18, 2022 20:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeroarst/3b3f32092d58698a4568cdb0919c9a93 to your computer and use it in GitHub Desktop.
Save zeroarst/3b3f32092d58698a4568cdb0919c9a93 to your computer and use it in GitHub Desktop.
Using annotation to autmatically cast Android fragment callback. This automatically handles attach&detach fragment listeners for reducing Boilerplate code.
public abstract class BaseCallbackFragment extends Fragment {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FragmentCallback {
/**
* Determine if throws excpetion if target does not implement the callback.
* @return true: throws an exception if not implemented.
*/
boolean mandatory() default true;
}
private List<Field> mFragmentCallbackFields = new ArrayList<>();
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
castFragmentCallback(activity);
}
private void castFragmentCallback(Context context) {
for (Class<?> cls : this.getClass().getDeclaredClasses()) {
FragmentCallback callback = cls.getAnnotation(FragmentCallback.class);
if (callback == null)
continue;
for (Field f : this.getClass().getDeclaredFields()) {
if (f.getType() != cls)
continue;
String formatter = "%s must implement " + cls.toString();
try {
if (getTargetFragment() != null) {
if (cls.isAssignableFrom(getTargetFragment().getClass())) {
if (!f.isAccessible())
f.setAccessible(true);
f.set(this, getTargetFragment());
mFragmentCallbackFields.add(f);
} else if (callback.mandatory())
throw new ClassCastException(String.format(formatter, getTargetFragment().toString()));
} else if (getParentFragment() != null) {
if (cls.isAssignableFrom(getParentFragment().getClass())) {
if (!f.isAccessible())
f.setAccessible(true);
f.set(this, getParentFragment());
mFragmentCallbackFields.add(f);
} else if (callback.mandatory())
throw new ClassCastException(String.format(formatter, getParentFragment().toString()));
} else {
if (cls.isAssignableFrom(context.getClass())) {
if (!f.isAccessible())
f.setAccessible(true);
f.set(this, context);
mFragmentCallbackFields.add(f);
} else if (callback.mandatory())
throw new ClassCastException(String.format(formatter, context.toString()));
}
} catch (IllegalAccessException e) {
Logger.e(TAG, e.getMessage(), e);
}
}
}
}
@Override
public void onDetach() {
super.onDetach();
for (Field f : mFragmentCallbackFields) {
try {
f.set(this, null);
} catch (IllegalAccessException e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
}
@zeroarst
Copy link
Author

zeroarst commented Oct 31, 2016

Example:

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

    @FragmentCallback
    public interface FragmentInteractionListener {
        void onEcho(EchoFragment fragment, String echo);
    }
}
public class MainFragment implements EchoFragment.FragmentInteractionListener  {

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        EchoFragment fg = new EchoFragment();
        fg.setTargetFragment(this, 0);
        getChildFragmentManager().beginTransaction().add(R.id.lo_fg_container, fg).commit();
    }

    @Override
    public void onEcho(EchoFragment fragment, String echo) {
         Log.d("MainFragment", echo);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment