Fragment inherit from another one with atribute initialization - android

public class CartMain extends BasicFragment implements EditMenuInterface {...}
public abstract class BasicFragment extends Fragment {
protected ArrayList<Integer> selectedElements = new ArrayList<>();
protected RecyclerView listView;
protected ObservableArrayList<?> list;
protected boolean editMode = false;;
protected View managementMenu
... methods...
}
I would like to initialize the parameters in BasicFragments in the CartMain class, but since i cant use constructrs i dont know how is the right way to do it.
I'm doing like this:
CartMain Class
#Override
public View onCreateView...{
mainView = inflater.inflate(R.layout.fragment_cart_main, container, false);
listView = view.findById...
list = cartDAO.getAll();
listView = mainView.findViewById(R.id.cart_list_itemCarts);
}
But i dont know if its correct, because when we use constructors its obrigatory initializate the super class atributes, but using onCreateView its not obrigatory.

There are a few points about your fragment:
be sure to declare an empty public constructor with no arguments:
public CartMain(){}
without that public empty constructor, Android will crash if you put your calling activity onPause and if it gets garbag-collected after some time.
Android will look for this empty public constructor to reinstantiate your fragment in onResume from the main thread.
The variables values initialized in the onCreateView will be used as default values after onStart, onPause etc. if the app return back to work after being stopped and garbage collected. Not variables, in the class constructor.
Therefore, do all your default initialization in the onCreateView.
You can do it in the super class's onCreateView.
If some initialization is done in the superclass, then in the subclass add the super method right at the start on your onCreateView.
#Override
public View onCreateView(LayoutInflator li...{
super(li... // This will call onCeateView in your super class
... // where you can initialize 'common-for-all' values, setup listeners
...
// do everything else
return mainView;
}
And the end of your onCreateView place return mainView; that you have inflated. The view that you return is the view that will appear on the screen.
Don't use initialization through the constructor outside onCreateView, because if, due to the app going to pause it gets garbage collected, it won't be able to re-initialize.
To verify that your fragment works correctly, use Developer mode (with the settings: Restriction - No background processes, Do not keep activities in memory - or similar settings).

Related

Notify Activity from onClick of View object

I seem to be stuck with a problem with an object communicating with my activity class. The object is a view object with an onClick method that when called I would like it to notify my activity class so that it can perform said action. Below is some example code of my situation (assume all conventional setup operations have already been made):
public class MainActivity extends AppCompatActivity{
//...other global methods and objects
//Does not have access to instantiated Entry object(s)
public void entryObjectWasClicked(){
//perform said action
}
}
public class Entry extends View implements View.OnClickListener{
//...other global methods and objects
//Does not have access to the MainActivity object
#Override
public void onClick(View v){
//send a message to the MainActivity to
//somehow call the entryObjectWasClicked() method
}
}
The only way (off the top of my head) that I could think about dealing with this problem is by creating a static method in MainActivity and then calling it from an anonymous MainActivity object in the onClick method of Entry. The problem with the static method approach is that any subsequent method/object/primitive usages in the static method force those methods/objects/primitives to be static. This defeats the purpose of then being able to have two different instances of the MainActivity object.
After some looking I came across using Broadcast messages, specifically using the LocalBroadcastManager to send an intent to the activity. This code example works for my model, but I want to know: is this the best way for me to go about sending messages to my MainActivity from my Entry object?
If there is a more effective way of doing all this, what would it be?
You're overcomplicating things. Don't override onClick for this. Instead, have your activity call setOnClickHandler on your view, which sets a callback that's called when the view is clicked. Then use the default implementation.
Since you extend view, i guess you want to use it inside a layout. That means you may want to create a Listener for that. Example:
public class Entry extends View implements View.OnClickListener{
private OnClickListener listener;
public void setListener(OnClickListener listener) {
this.listener = listener;
}
#Override
public void onClick(){
if (this.listener != null) this.listener.onClick(this);
}
}
How you can inflate your layout in your Activity and access your custom view.
public class MainActivity extends AppCompatActivity{
public void onCreate( ...) {
Entry entry = findViewById(R.id.entry);
entry.setListener(new OnClickListener(...));
}
}

What happens to static members of an Activity when it is recreated because of config changes

I want to create a static presenter object in my Activity, so that when the Activity is recreated because of config changes, it will retain the presenter instance and my business logic will not be affected.
The code of my Activity is:
public class HomeActivity extends AppCompatActivity {
public static HomePresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
if (presenter == null){
presenter = new HomePresenter();
}
}
}
Nothing will happen to the static instance. But doing this could leak memory (see Avoiding memory leaks) if you do not delete the reference to the static presenter.
I would suggest another approach. Override onRetainNonConfigurationInstance() to keep an object when the Activity is destroyed because of an configuration change (e.g. rotation). And use getLastNonConfigurationInstance() to get the very same object after the configuration change.
public class HomeActivity extends AppCompatActivity {
public HomePresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
presenter = (HomePresenter)getLastNonConfigurationInstance();
if (presenter == null){
presenter = new HomePresenter();
}
}
#Override
public Object onRetainNonConfigurationInstance() {
return presenter;
}
}
You can also use a Fragment to keep objects alive during a configuration change, see RetainingAnObject.
Your code will work, presenter will be alive, but, please, don't do this.
Keyword 'static' means that value of this field will be attached to class, not to instance of it. So if you for example will have your HomeActivity, then you go to the SomeElseActivity and then go to new HomeActivity (you will have back stack HomeActivity -> SomeElseActivity -> HomeActivity) for new HomeActivity you will have same presenter as for old one. Thus you will have one share presenter for 2 independent instances of HomeActivity. Moreover, if you have some state in presenter, you will have a lot of problems with your application in this case.
I recommend you to remove 'static' keyword. And if your presenter have state, that's needed to be saved during config changes, try one of 2 alternatives:
1) Create onSaveInstanceState and onRestoreInstance state in your presenter and call them in appropriate activity's methods
2) Create fragment without ui, but with flag 'retain instace' (setRetainInstance method), and this fragment will keep reference to your presenter.
In short, the static object remains, and it gets its birth when you class is loaded into memory, and never goes away until your app dies, or when the class is unloaded.
In JVM languages, the compiler optimizes static fields by embedding the value in the bytecode instead of computing the value at runtime.
When you fire up a JVM and load a class for the first time (this is done by the classloader when the class is first referenced in any way) any static blocks or fields are 'loaded' into the JVM and become accessible.
So the static variable lives in the circle in the snapshot, and it is ignorant of whatever config changes, it is there, no matter what happens and as long as as the class is loaded.

How to set a TextView from another class (not extending to Activity.class)

I have a problem about setting a TextView from a class which is not a child class of Activity. This class is basically used for handling registration and REST request with 3rd party server.
After getting textfield info from 3rd Party server, it is too late to set TextView in the Main Activity.
I can't use SharedPreferences to set this info, because MainActivity has already started.
I can't pass this info with Bundle since my java class is not an activity class.
How can I pass this info and set the TextView in the MainActivity? Is there any way to do this?
The proper way of doing this this is to create a listener.
Create an interface :
public interface OperationCompletedListener{
void onOperationCompleted(String resultValue);
}
Then in your class which calls Rest services, create a variable for this listener and a method to set it.
private OperationCompletedListener mListener;
public void setOperationCompletedListener(OperationCompletedListener listener){
mListener=listener;
}
Then when the your rest service completed call like below :
if(mListener!=null){
mListener.onOperationCompleted("your value to be passed");
}
Then in your activity class which contains the TextView, create an object of OperationCompletedListener and set it to the other class using the set method that we created earlier. Then in the onOperationCompleted method, set the text view with your value and you are done.
private OperationCompletedListener mOperationCompletedListener=new OperationCompletedListener() {
#Override
public void onOperationCompleted(String resultValue) {
yourTextView.setText(resultValue);
}
};
restServiceClassObject.setOperationCompletedListener(mOperationCompletedListener);
You can create an static method which update textview in your activity class . Then call this method from your other class whenever you want.
Try to pass the Activity to the non-Activity class when you instantiate it. For example:
public class NonActivityClass {
private Activity parentActivity;
public NonActivity(Activity parentActivity) {
this.parentActivity = parentActivity;
}
}
Or you can just pass the Activity to a static method in your NonActivityClass if you don't want to instantiate it (it's abstract). Then, you can inflate the TextView or do a findViewById from the parent and set the text.
From my experience, you should never use a static non-final variable to maintain a reference across activities. When you restart the app or the phone, or when Android kills your app's process, the reference and state of the variable becomes lost and may cause your app to crash.

Call Activity Method From Fragment

I'm dealing with fragments.
I have an Activity and different fragments.
Each fragment need the access to a Class(call it X) that allow it to access a database, but, because I have a lot of fragments, I don't want to create a different instance of the Class X in every fragment as I think it will require lots of memory.
So how can I do?
I wrote something like this (with a getter), but it doesn't work!
public class MyActivity {
private ClassX classx;
.....
public ClassX getClassX() {
return classx;
}
.....
}
But than, how can I call it from the fragment?
From the fragment call your activity's method
((MyActivity ) getActivity()).getClassX() ;
This is a little bit more of a Java question and android.
If you looking at accessing the database, look at creating a database singleton.
So something like:
public class Database {
// This starts off null
private static Database mInstance;
/**
* Singleton method, will return the same object each time.
*/
public static final Database getInstance() {
// First time this method is called by Database.getInstance() from anywhere
// in your App. It will create this Object once.
if(mInstance == null) mInstance = new Database();
// Returns the created object from a statically assigned field so its never
// destroyed until you do it manually.
return mInstance;
}
//Private constructor to stop you from creating this object by accident
private Database(){
//Init db object
}
}
So then from your fragments and activities you can then place the following field in your class's (Better use use a base activity and fragment to save you repeating code).
public abstract class BaseFragment extends Fragment {
protected final Database mDatabase = Database.getInstance();
}
Then your concrete fragments can extend your BaseFragment e.g. SearchListFragment extends BaseFragment
Hope this helps.
Worth reading about singletons and database
Regards,
Chris
Define an interface called Callbacks (or something else if you want). In it, have a public method called getClassX(). Then make your Activity implement the Callbacks interface.
In your Fragments, in onAttach, store a reference to a Callbacks object (i.e. your activity via something like:
if(activity instanceof Callbacks)
mCallbacks = (Callbacks)activity;
This will guarantee that the Fragments are able to call the function. (in case you want to reuse the fragments later in another app)
Then in your Activity, in onCreate(), create an instance of ClassX. In your getClassX() method, just return a reference to it.
When you want a reference to it from your Fragments, call mCallbacks.getClassX() and you should be sorted.
You can use a static object in your activity, and use it from the fragment, or call the getActivity() method in your fragment to access the whole activity objects/methods

Calling findViewById() from outside an activity

Is there any way to access a layout's view from a non-Activity-derived class? I'm creating an Accordion class and need to access some of the activity's UI elements. I'm passing in the activity's context to my accordion class's constructor, but the findViewById API is only available from the Activity class. I also don't want to pass in an instance of my activity since that seems to be frowned upon due to potential memory leaks.
I'm pretty sure you can just pass an activity as a parameter, e.g.
public void initSouthViews(Activity activity) {
for (int i = 0; i < southScores_.length; ++i) {
southScores_[i] = (EditText) activity.findViewById(10);
}
}
Here is something that might be helpful.
public interface IViewRequest {
public View requestViewByID(int id);
}
public class MyActivity extends Activity {
private IViewRequest viewRequest = new IViewRequest(){
public View requestViewByID(int id){
return findViewById(id);
});
}
public class Accordion(){
private IViewRequest viewRequest;
public Accordion(IViewRequest viewRequest){
this.viewRequest = viewRequest;
}
private View findViewById(int id){
return viewRequest.requestViewByID(id);
}
}
I have never tried something like this. I also don't know if it won't cuase any memory leaks. But it does what you asked :) "Calling findViewById() from outside an activity"
Activity's context is in fact the Activity class itself. Assuming that this object will live inside only one Activity, it should be safe to pass object of type Activity to it. Otherwise, think about reengineering your Accordion class.
I passed in an instance of one of the Views into the class's constructor.

Categories

Resources