I've been questioning myself a long time how to solve this issue but didn't find a proper solution. I have the following subclasses of Fragment:
BaseListFragment extends ListFragment
BaseDialogFragment extends DialogFragment
BaseFragment extends Fragment
Each of these BaseFragments override the same methods and implement the same functionality. For example:
public abstract class BaseFragment extends Fragment
{
private BaseActivity baseActivity;
#Override
public void onAttach(Context context)
{
super.onAttach(context);
baseActivity = (BaseActivity) context;
}
#Override
public void onDetach()
{
baseActivity = null;
super.onDetach();
}
// other overridden methods.
}
public abstract class BaseDialogFragment extends DialogFragment
{
private BaseActivity baseActivity;
#Override
public void onAttach(Context context)
{
super.onAttach(context);
baseActivity = (BaseActivity) context;
}
#Override
public void onDetach()
{
baseActivity = null;
super.onDetach();
}
// other overridden methods.
}
public abstract class BaseListFragment extends ListFragment
{
private BaseActivity baseActivity;
#Override
public void onAttach(Context context)
{
super.onAttach(context);
baseActivity = (BaseActivity) context;
}
#Override
public void onDetach()
{
baseActivity = null;
super.onDetach();
}
// other overridden methods.
}
All other fragments in my app then extend any of these Fragments. However, the problem is that I don't want to code everything three times.
Want can I do to solve this problem?
Related
In menu fragment extends base fragment
public class MenuFragment extends BaseFragment{
and onattach methot doesn't work in basefragment
private BaseActivity mActivity ;
#Override
public void onAttach(Context context){
super.onAttach(context);
if(context instanceof BaseActivity){
BaseActivity activity =(BaseActivity) context;
this.mActivity=activity;
activity.onFragmentAttached();
}
}
why mActivity is null?
Solved. Problem's reason is BaseActivity didn't extend to MainActivity.
I want to override a method present in an Activity through a Fragment which is added in the activity.
I have following setup:
BaseActivity
public class BaseActivity extends AppCompatActivity {
// gets triggered whenever I set a toolbar title
// from an Activity or Fragment
public boolean shouldLimitToolbarCharacters () {
return true;
}
}
BaseFragmentActivity
public class BaseFragmentActivity extends BaseActivity implements ToolbarBehaviour {
// override from BaseActivity and from ToolbarBehaviour
#Override
public boolean shouldLimitToolbarCharacters () {
// instead send it to fragment somehow
return true;
}
}
ToolbarBehaviour
// implemented by BaseFragmentActivity above
interface ToolbarBehaviour {
boolean shouldLimitToolbarCharacters();
}
BaseFragment
public class BaseFragment extends Fragment {
#Override
public void onAttach (Context context) {
if (context instanceof ToolbarBehaviour) {
ToolbarBehaviour toolbarBehaviour = (ToolbarBehaviour) context;
// ?? now what
}
}
}
How do I override BaseFragmentActivity's shouldLimitToolbarCharacters() from BaseFragment or its sub-fragments?
Whenever I trigger shouldLimitToolbarCharacters() from a sub-fragment of BaseFragment, it should use sub-fragment's provided overridden method for shouldLimitToolbarCharacters().
Thanks.
Call a method of fragment inside Activity as in
BaseFragmentActivity
public class BaseFragmentActivity extends BaseActivity implements ToolbarBehaviour {
BaseFragment fragment; // init in oncreate or something;
// override from BaseActivity and from ToolbarBehaviour
#Override
public boolean shouldLimitToolbarCharacters () {
if(fragment == null)
return false;
return fragment.shouldLimitToolbarCharacters();
}
}
BaseFragment
public class BaseFragment extends Fragment {
public boolean shouldLimitToolbarCharacters(){
return true; // do what ever you want to do
}
}
you can directly access the activity method through ((BaseFragmentActivity )getActivity()).shouldLimitToolbarCharacters();
and use like this
#Override
public void onAttach (Context context) {
if (context instanceof BaseFragmentActivity) {
((BaseFragmentActivity)getActivity()).shouldLimitToolbarCharacters();
}
}
I am trying to inject my MainActivity into the Fragment. I have an Interface that is implemented in my MainActivity that will listen to events from the Fragment. So I want to Inject the MainActivity and call the event listener on it.
I have tried doing the following but has failed to do so. Just displaying the code snippets.
interface
public interface RecipeItemListener {
void onRecipeItem();
}
MainActivity that implements the interface
public class MainActivity extends AppCompatActivity implements RecipeItemListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_fragment_container, RecipeListView.newInstance(), RecipeListView.TAG)
.commit();
}
}
#Override
public void onRecipeItem() {
Timber.d("onRecipeItem");
}
}
My Module that provides the MainActivity
#Module
public class RecipeListModule {
private MainActivity mainActivity;
public RecipeListModule(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
}
My main Component
#Singleton
#Component(modules = {
AndroidModule.class,
NetworkModule.class})
public interface BusbyBakingComponent {
RecipeListComponent add(RecipeListModule recipeListModule);
}
My SubComponent
#RecipeListScope
#Subcomponent(modules = {RecipeListModule.class})
public interface RecipeListComponent {
void inject(RecipeListView target);
}
My Application class
public class BusbyBakingApplication extends Application {
private BusbyBakingComponent applicationComponent;
private RecipeListComponent recipeListComponent;
#Override
public void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
}
public BusbyBakingComponent createApplicationComponent() {
return DaggerBusbyBakingComponent.builder()
.networkModule(new NetworkModule())
.androidModule(new AndroidModule(BusbyBakingApplication.this))
.build();
}
public BusbyBakingComponent getApplicationComponent() {
return applicationComponent;
}
public RecipeListComponent createRecipeListComponent(MainActivity activity) {
recipeListComponent = applicationComponent.add(new RecipeListModule(activity));
return recipeListComponent;
}
public void releaseRecipeListComponent() {
recipeListComponent = null;
}
}
And in My fragment this is how I am trying to inject:
#Inject MainActivity mainActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent((MainActivity)getActivity())
.inject(RecipeListView.this);
}
I keep getting the following error:
Error:(14, 8) error: [me.androidbox.busbybaking.di.RecipeListComponent.inject(me.androidbox.busbybaking.recipieslist.RecipeListView)] me.androidbox.busbybaking.recipieslist.MainActivity cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
me.androidbox.busbybaking.recipieslist.MainActivity is injected at
me.androidbox.busbybaking.recipieslist.RecipeListView.mainActivity
me.androidbox.busbybaking.recipieslist.RecipeListView is injected at
me.androidbox.busbybaking.di.RecipeListComponent.inject(target)
Many thanks for any suggestions.
If you have a look at your module
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
You provide the interface (which is good) and not MainActivity (the implementation).
Since you request MainActivity
#Inject MainActivity mainActivity;
You receive this error:
MainActivity cannot be provided [...]
because you only provide RecipeItemListener.
Switch your code from requiring MainActivity in RecipeListView to
#Inject RecipeItemListener recipeListener;
and it should work, if the rest of your setup is correct.
You can access activity in Fragment using getActivity() and cast it to your interface listener like this
((RecipeItemListener)getActivity()).doSomethingOnListener()
much simpler, without any unnecessary injections
I have an activity which instantiate a class with an interface. How I can get the android context within MyClass with only the reference to the interface?
public class TestActivity extends Activity implements MyInterface {
#Override
public void onCreate(final Bundle savedInstanceState) {
final MyClass myClass = new MyClass(this);
}
#Override
public void onChange() {
}
}
public interface MyInterface {
void onChange();
}
public class MyClass {
public MyClass(MyInterface myInterface) {
// how to get context from myInterface ???
}
}
public class MyClass {
public MyClass(MyInterface myInterface) {
// Get Context
Context context = null;
if (myInterface instanceOf Context)
context = (Context)myInterface;
}
}
If your Activity (which extends Context) is implementing MyInterface and you pass that to MyClass, you just need to cast it to the appropriate type.
The context is only available in Activity classes or global Context, if you have to use the context, change your code:
public class TestActivity extends Activity implements MyInterface {
#Override
public void onCreate(final Bundle savedInstanceState) {
final MyClass myClass = new MyClass(this);
}
#Override
public void onChange() {
}
}
public interface MyInterface {
void onChange();
}
public class MyClass {
public MyClass(TestActivity activity) {
//now, you can use the context of your activity or do a cast
// to your interface
MyInterface interface = (MyInterface) activity;
}
}
I think you approached it a bit wrong, but I might miss understood your design concept what you want to achieve.
Create your Interface in the Class where you want to obtain the Context from and the use implement like you did in your activity.
YourContextClass() {
private MyInterface interface;
onCreate() {
interface.onChange(this);
}
public interface MyInterface() {
void onChange(Context context);
}
}
Then in your Activity class implement MyInterface, and inside the method, you can obtain the Context.
YourActivity implements MyInterface {
private Context context;
...
void onChange(Context context) {
this.context = context;
}
}
But this is only necessary if you really need the context from Interface... otherwise, in you Activity, I see you are extendingActivity, which gives you access to the Context with getContext();
MAIN ACTIVITY
public class MyActivity() extends Activity
{
onCreate()
{
MyClass myobj=new MyClass();
}
public void Mymethod()
{}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass()
{
MyClass(Context context)
{
}
}
I tried to call Mymethod() from an instance of MyClass.
I would really appreciate any help. Thanks.
Why not just pass the activity to the constructor like
public class MyActivity extends Activity {
onCreate(){
MyClass myobj=new MyClass(MyActivity.this);
}
public void myMethod(){
}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass{
public MyClass(MyActivity act) {
act.myMethod();
}
}
Make that method as static so you can call without creating the class object
public static void Mymethod()
{}
and call like this way
MainActivity.Mymethod();
This is probably the best way to do it. This is how I'm doing it. It's called a Singleton Design Pattern:
public class MyActivity extends Activity {
private static MainActivity instance;
public static MainActivity getInstance() {
if(instance==null){
setInstance(this);
}
return instance;
}
public static void setInstance(MainActivity instance) {
MainActivity.instance = instance;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setInstance(this);
}
}
If I'm understanding you correctly I believe you can solve your problems using an interface as a callback.
////ACTIVITY/////////////////////////////////
public class MyActivity() extends Activity {
onCreate()
{
MyClass myObj=new MyClass();
//Set the listener on the object. Created as anonymous
myObj.setListener(new MyClass.Listener() {
myMethod();
});
}
}
public void myMethod(){
}
//////Custom Class//////////////////
public class MyClass {
Listener mListener;
public interface Listener {
public void onInterestingEvent();
}
public void setListener(Listener listener) {
mListener = listener;
}
public void someUsefulThingTheClassDoes() {
//Do your code here and when you're ready to call the activity's method do this
mListener.onInterestingEvent();
}
}
I had an inner class that I wanted to pull out into a more general library "Helper" class. I had the same issue you do. I got around it by making the helper class abstract, with a single abstract method. Then in my project package I extended the helper class with a constructor call in the specific class.
public class MyActivity extends Activity {
onCreate() {
MyHelperClass = new MyHelperClass(this, "foobar");
}
public void myMethod() {
// Code...
}
}
// In a different file
public class MyHelperClass extends HelperClass {
private MyActivity mInstance;
public MyHelperClass(MyActivity act, String data) {
super();
this.mInstance = act;
this.mActivity = act; // Useful for calling generic Activity methods in the HelperClass
this.mData = data;
}
protected void callMyActivityMethod() {
mInstance.myMethod();
}
}
// In a different file
public abstract class HelperClass {
protected Activity mActivity;
protected String mData;
public HelperClass() {
// Subclass will set variables
}
protected abstract void callMyActivityMethod();
// More code for all the other stuff the class does
}
In this way, I have a helper class that contains the vast majority of the "work", and all I have to do is make a subclass with the constructor and one method in order to get access to the calling activity's method of interest.
You have to pass instance of MainActivity into another class, then you can call everything public (in MainActivity) from everywhere.
MainActivity.java
public class MainActivity extends AppCompatActivity {
// Instance of AnotherClass for future use
private AnotherClass anotherClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Create new instance of AnotherClass and
// pass instance of MainActivity by "this"
anotherClass = new AnotherClass(this);
}
// Method you want to call from another class
public void myMethod(){
...
}
}
AnotherClass.java
public class AnotherClass {
// Main class instance
private MainActivity mainActivity;
// Constructor
public AnotherClass(MainActivity activity) {
// Save instance of main class for future use
mainActivity = activity;
// Call method in MainActivity
mainActivity.myMethod();
}
}
In MainActivity.class file
You have to pass MainActivity context from MainActivity Class. Then in MyClass you have to Get MainActivity context. Remember Context and MyActivity are two different reference.
public class MyActivity extends Activity
{
onCreate(){
MyClass myobj=new MyClass(MyActivity context);
}
public void Mymethod(){}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass()
{
MyActivity context;
MyClass(MyActivity context)
{
this.context = context;
this.context.Mymethod();
//Or you can directly use activity context
context.Mymethod();
}
}
I decided to write the HelperClass MyClass as an inner class of MyActivity class. This allows it full access to parent class but the bad thing is now MyClass is restricted to MyActivity class only.
public class MyActivity() extends Activity
{
onCreate()
{
MyClass myobj=new MyClass();
}
public void myMethod()
{
}
}
//INNER CLASS
public class MyClass
{
public MyClass()
{
}
//I can directly access the MyMethod
myMethod();
}