I have this simple problem:
An activity contains a fragment. Both want to communicate with each other.
The fragment wants to inform the activity about certain interactions. Therefore it defines the OnFragmentInteractionListener interface which is implemented by the activity. This is already part of the Android Studio fragment template, so nothing new here.
The activity wants to inform the fragment about the back button being pressed. Therefore it defines the OnBackPressedListener interface which is implemented by the fragment.
class MainActivity extends AppCompatActivity implements MyFragment.OnFragmentInteractionListener {
...
#Override
public void onFragmentInteraction(Uri uri) {
...
}
interface OnBackPressedListener {
boolean onBackPressed();
}
}
class MyFragment extends Fragment implements MainActivity.OnBackPressedListener {
...
#Override
public boolean onBackPressed() {
...
}
interface OnFragmentInteractionListener {
void onFragmentInteraction(Uri uri);
}
}
IMHO a simple design that, unfortunatley, doesn't work. "Cyclic inheritance involving ..." is the message from Android Studio.
I would have expected that compilation error if I used nested classes instead of interfaces as the classes' static initializers might be cyclic dependent on each other. I don't see that problem with interfaces but I'm sure there's a good reason why it's not allowed.
The easiest way to resolve this is making one of the interfaces a package-level interface. But I wonder if this is the way to go. And if so: which one should be package-level.
You can have a BroadcastListener attached to the Fragment using a register call.
Please do not forget to unregister the listener in onStop or onPause if you plan to interact with the UI or it will cause exceptions.
Now the simple way is, override the onBackPressed inside your activity and when it gets hit, broadcast an event and let your fragment know. Simple and shouldn't be too problematic to handle.
I personally prefer this approach to send messages to all my Fragments. Another approach as you yourself said is declaring a package wide interface and doing the stuff that you wanted as you already wrote. Nothing wrong with this either, it's just Java being Java.
Related
I'm using a PreferenceFragment and some headers to manage my application settings like in this official tutorial: http://developer.android.com/guide/topics/ui/settings.html#Fragment
You can call Preferences by the ActionBar in several Activities.
Now I want to detect, if a user enters oder leaves the preferences. How can I do that?
My approach is using the lifecycle methods like: onCreate, onResume, onPause ....
But is there another solution like a listener or something else?
You can override onBackPressed on PreferenceFragment to call a interface method that is implemented on your caller/s activity.
That way you can notify what you need to your activitys.
public interface PreferenceInterface
{
public abstract void preferencesClosed();
public abstract void preferencesOpened();
}
I hope this is what you are looking for.
I'm not PRO in JAVA , but I found myself of getting crazy with Android activities getting too large.
After a few pages of code length I found myself of permanently scrolling back and forth!
Not only OnClickhandlers can grow huge, just a lot of code tends to sum up in a bigger activity.
Fragments were no solution for me.
In general Java does not allow to split up a class into several files so a hack is needed.
I browsed through a lot of stackoverflow threads regarding that, not one had a solution.
I solved it anyway, maybe this helps others.
This is a bit Android focused as I got anoyed by my huge onClick Handlers for all the menus and buttons but I guess it will work for other general Java problems as well the same way.
A simple approach that I follow is to move the View concerns into a separate class (let's call it ViewManager) and make the Activity/Fragment work only as a controller.
A ViewManager is generally responsible for -
Inflating the layout
Getting references to all the views
Displaying data in the views
Handling click/touch events
Animations
The Activity/Fragment is only responsible for -
Fetching data and passing it to the ViewManager to display it
Handling navigation
Posting data to a server/DB
For UI controls that trigger an action that the controller is responsible for, say launching a new activity when a button is clicked, the ViewManager receives the click and calls a method in the controller that takes care of the navigation.
If you want to further eliminate boilerplate code (click handlers, findViewById() calls, etc), consider using libraries like ButterKnife.
One solution is simple, you can make the main class fields public
However that will mess up your code, you should keep the fields private when possible.
Aside from better code completion of your IDE it's also protecting the classes from illegal outside manipulation.
Now the trick for me was inner classes.
An inner class can access the private fields of it's parent, however Java does also not allow to put an inner class into another file.
It has to be defined INSIDE the parent class.
This is where I started to dig and found a solution which might be considered to be acceptible.
If this hurts java experts, I'd appreciate not to be downvoted ;)
The trick is to create an "abstract class" in an own java file.
I named it a bit outside conventions to make it stand out: InnerMainActivity_onClickHandlers.java
MainActivity is my main class (the parent of the new inner class)
In my Parentclass I have this field defined, a normal inner class but extending the new class:
private class inner extends InnerMainActivity_onClickHandlers{
public inner(MainActivity mainActivity)
{
super(mainActivity);
}
};
In my MainActivity.onCreate:
Button.setOnClickListener(new inner(this));
As you can see, as you can see it passes the MainActivity (Parent) to the Inner class.
Now the Inner class iteself:
public abstract class InnerMainActivity_onClickHandlers implements View.OnClickListener
{
private final MainActivity main;
public InnerMainActivity_onClickHandlers(MainActivity mainActivity)
{
this.main = mainActivity;
}
#Override
public void onClick(View view)
{
// here implement the listener
}
}
To make this work as advertised you need to change the private properties of your MainActivity/Parent to protected.
Now the extended inner class has access rights to the parent fields, however any external class can still not access it.
In this case I have a dedicated class for all onclick listeners, a second class could be used for other things.
Consider the following scenario:
I have an Activity with UI elements that launches a DialogFragment when clicked
The DialogFragment has a listener interface that the Activity provides an implementation of. Say for example, the Activity is an image editor and the DialogFragment selects a contrast - the dialog would have a OnContrastChangedListener that the Activity implements
The Activity has implemented this interface to update views in its UI. Continuing the image editor example, the Activity implements the OnContrastChangedListener to update its preview view - something like this:
contrastDialog.setOnContrastChangedListener(new OnContrastChangedListener {
#Override
public void OnContrastChanged(int newContrast) {
getPreviewView().updateWithContrast(newContrast);
}
});
The orientation is changed, and everything is recreated and the listener saved and restored correctly using the methods recommended here (listener is saved in a Fragment and restored when the lifecycle is restoring state).
The problem is the listener interface now does not work. The function getPreviewView() is now returning null even though when called anywhere else in the Activity it returns the correct value
Excuse the poor terminology (my knowledge in compiling and bytecode is limited), but I can grasp what has happened. The interface has been compiled with the getPreviewView() version that returned the preview view that was destroyed on the orientation change, and this has since been released / garbage collected / is now null.
My question is, is there a way in Java to make the interface compile expecting the values / functions to change - much like the volatile keyword in C (I am expecting there isn't)? In that case, what's the best approach for getting around this type of situation? I have considered the following:
Create the DialogFragment (and its interface) in the code that is rerun when the Activity is recreated. This is fine for things like OnClickListeners for Buttons as they are definitely created. But this DialogFragment is only created when a button is pressed, so this approach means every dialog for the screen is created each time the Activity is - this seems wasteful given they may not even be run
Create all possible interfaces for the Activity every time and save them in member variables, then use these interfaces when the DialogFragments are requested to be created by the event. Same comments as above - seems wasteful creating every possible interface just in case it is run.
Keep some hacky "open dialog state" member variables in the Activity that guide the recreation of the interfaces. Hacky and creates a tie between the Activity and the DialogFragment which isn't great practice.
As you can see, all options involve recreation that is wasteful to some extent - is there a way to reuse the existing interface implementation?
EDIT: Options 1 and 2 won't work because they need a link to the existing Dialog. This is all doable but it is leaning more and more towards the hacked together option of having 'current Dialog' variables, getting the DialogFragment with FragmentManager when the activity is restarted, casting it appropriately based on the 'current Dialog' variable, recreating the listener. Is there a less messy way?
the onAttach onDetach method is good and I like using it, sometimes, when I know there will be more developers in the code I don't even cast it blindly, but I do a check like this:
if(activity instanceof MyInterface){
interface = (MyInterface) activity;
} else{
thrown new RuntimeException("Dear colleague, this fragment was meant to have the activity implementing MyInterface or else a bunch of other stuff won't work, please go back to your code and add the interface");
}
but as a different resort, you can also re-set the interface when the fragment is recreated. For example, on the activity onCreate
if(savedInstanceState != null){
mDialogFrag = getSupportFragmentManager().findFragmentByTag(MyDialogFrag.TAG);
if(mDialogFrag != null)
mDialogFrag.setListener(... the interface ...);
}
I know that's also not the best separation of objects, but the fact is that getPreviewView() NEEDS the current activity to proper operate, so you NEED to pass this reference again when everybody gets destroyed n rebuilt.
Those are just different ways of doing it.
If the activity will always have the implementation of the interface, would it be possible to set your listener in the DialogFragment's onAttach()? This will ensure that when it is destroyed and recreated, it will have the most up-to-date reference, like:
public void onAttach(Activity activity) {
super.onAttach(activity);
contrastChangedListener = (OnContrastChangedListener)activity;
}
public void onDetach() {
super.onDetach();
contrastChangedListener = null;
}
When is Activity.onBackPressed called in my Android application? I'm not looking for the obvious answer of when the user presses the back button. I wan't the answer in relation to other "callback" functions.
Is is possible to be called during the execution of another function within the Activity class?
What is the case if I have my Activity class implement some typical interfaces used for your typical game? For example GLSurfaceView.Rendered? I'm having the feeling onBackPressed is called during GlSurfaceView.Renderer.onDrawFrame but I'm not 100 % sure yet. Even if this isn't the case, I want to know how it works. (It seems difficult to find this kind of simple information anywhere.)
Finally, below is a code example for the layout of my Activity class. The question is, however, not limited to this particular setup.
class MainActivity extends Activity implements Renderer {
onCreate(...) {
layout = new FrameLayout(this);
GLSurfaceHolder glsurface = new GLSurfaceHolder(this, this);
glsurface.setRenderer(this);
layout.addView(glsurface);
setContentView(layout);
GLSurfaceHolder is just a simple dummy class that extends GLSurfaceView. It has the onTouchEvent overloaded and simple passes the data over to the MainActivity class. (The design philosophy in this very, very simple app is just to focus all the sensory and other data to one place and then "make things happen"..)
onbackpressed will be called when you pressed back button. Default behaviour will be destroying the activity. To avoid override the onbackkeypressed or onkeypressed.
I have a custom View class that extends Spinner. I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection. I see that the OnItemSelected listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow, or if I should just talk directly to the context (for some reason that doesn't feel safe, even though I can't think of a way in which it might fail, offhand).
the right way to do that, is to "listen" to your custom view by exposing an interface which your view holding a reference to instance of him, and you hosting activity should implement. exactly like the OnItemSelected interface and any events which android views are exposing is been implemented. this is the observer design pattern.
for example:
public class MyCustomSpinner extends Spinner {
public MyCustomSpinner(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public interface IMyEventListener {
public void onEventOccurred();
}
private IMyEventListener mEventListener;
public void setEventListener(IMyEventListener mEventListener) {
this.mEventListener = mEventListener;
}
protected void someMethodWhichDoingSomthingAndShouldRaiseAlsoTheEvent() {
/*
* Some Code which the function doing //more code...
*/
if (mEventListener != null) {
mEventListener.onEventOccurred();
}
}
}
this is how you will use it from your activity:
mMyCustomSpinner.setEventListener(new IMyEventListener() {
#Override
public void onEventOccurred() {
// TODO Auto-generated method stub
}
});
I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection.
You don't want to "talk to the Activity that it's embedded in". You want to talk to the controller responsible for the View. Today, that might be an Activity. Tomorrow, that might be a Fragment.
I see that the OnItemSelected listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow
That implies that the View knows the specific type of Adapter, since the Adapter interface does not have any sort of getContext() method. Moreover, it ties you to talking to the Activity, which is not a good plan at this point, as mentioned above.
Personally, I'm a bit dubious about having a custom Spinner subclass in the first place. But, assuming there's a good reason for it, you should follow Tal Kanel's advice (posted while I was writing this) and design a custom listener interface for this custom event that is being declared by your custom View. Have the controller (Activity or Fragment) supply an implementation of that interface -- this could be directly implemented on the controller, or implemented as an anonymous inner class (as in Tal Kanel's answer), etc. Have your custom View call method(s) on the listener interface as needed.
The correct way is using a listener of some sort. I think you can make direct reference, your code would just not be reusable for another project then...
A simple solution -
((ParentClass) context).functionToRun();
Where ParentClass is the class name of the activity.