I have been using this example as a base for my project.
I have changed the project to use an ArrayAdapter for the titles-fragment's ListItems and changed the DetailsFragment's View to display a custom layout, which as a Button that is supposed to add an entry into a database.
Instead of generating widgets like in the example, I just inflate a custom XML into the FrameLayout besides the TitlesFragment in the 'layout-land' version of the layout.
My problem stems from the fact that DetailsFragment is plugged into the MainActivity when in Landscape mode but gets it's own DetailsActivty if it is in Portrait mode.
The Button I have in my custom layout for the details-fragment calls a function in its onClick() that is called AddNewItem.
So when the Button was clicked in Landscape mode, it crashed, because there was no AddNewItem in the MainActivity. I solved this by using a BroadcastReceiver, so when the button is clicked, a method named AddNewItem in MainActivity.java instead broadcasts a custom event and i have a BroadcastReceiver that calls the AddNewItem in DetailsFragment.java.
It looks like this:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Intent Detected.", Toast.LENGTH_LONG).show();
DetailsActivity m = (DetailsActivity) DetailsActivity.getActivityInstance(); // see below for what getActivityInstance does
LayoutInflater mInf = LayoutInflater.from(context);
View myView = mInf.inflate(R.layout.customlayout, null);
((DetailsActivity) m).AddNewItem(myView);
}
}
But this gives me:
08-11 13:37:50.687: E/AndroidRuntime(6766): java.lang.RuntimeException: Unable to start receiver in.falkeninc.umt_v0_9_8_1.MyReceiver: java.lang.NullPointerException
I am not sure what is happening. Because I am also using a static variable in DetailsActivity.java to be able to reach it inside the BroadcastReceiver. The code looks like this:
public class DetailsActivity extends SherlockFragmentActivity {
...
public static SherlockFragmentActivity activityInstance;
...
activityInstance = this; // in the onCreate
...
public static SherlockFragmentActivity getActivityInstance(){
return activityInstance;
}
}
My problem stems from the fact that DetailsFragment is plugged into
the MainActivity when in Landscape mode but gets it's own
DetailsActivty if it is in Portrait mode.
This shouldn't be a problem. Although you have two activities you have the same DetailsFragment class in both of them. If a Button from that fragment is doing something then you should keep it at that fragment's level(that would be an ideal fragment, one that is self contained, a fragment that doesn't know or care where is put). If you do require the activity for the work in that method, you should add extra details.
Also, if the behavior is common to both activities you could make a base activity holding that method and let the two current activities inherit from that, so it will be available to the fragment no matter what.
public static SherlockFragmentActivity activityInstance;
Don't keep static references to activities, you risk leaking them.
Also, don't access activities from outside their own context. When an activity is not the one interacting with the user(it's onPause() has been called) that activity could as well be completely destroyed and trying to access it in another activity could bring you lots of problems.
Related
I am more familiar with iOS development than Android and I am wondering if all code should be written in an Activity rather than having a "model" class.
I have a couple screens each with a few checkboxes and I want them all to behave the same on click, I am trying to figure out how I would do this without writing repeating code in each activity. Thanks!
No you should not. If you are familiar with java, think of an activity as a extension of main with OO added.
In your particular example you can create a class with a method like:
<MethodName>(View <checkboxClickedName>){ //your code here }. and then add this to the checkbox in the XML android:onClick="<MethodName>", you may need the full package path (e.g. com.example.app.)
Note: if some of the commands/objects you need are only available within an activity you should create this in an calss that extends Activity or preferably within the running activity.
You could have a base class that extends activity that contains the methods that you want executed on click (either implemented or abstract). Use this new base class instead of activity when making new activities. In the layout xml, you can set the onclick of each checkbox to be the method in the base activity you want executed.
The best practice would be to use a single activity and switch fragments as if they were your screens. Then, the activity could simply implement the listener interface that the fragments would re-use.
Since you have multiple activities this becomes a little bit harder. To really re-use a single listener, I can think of a single (not so beautiful) option. Create a static listener and lazy load it:
public class MainActivity extends Activity {
private static View.OnClickListener sCheckboxClickListener;
public static View.OnClickListener getCheckboxClickListener() {
if (sCheckboxClickListener == null) {
sCheckboxClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
// Work with v
}
};
}
return sCheckboxClickListener;
}
}
And in each of your activities call:
findViewById(R.id.checkbox1)
.setOnClickListener(MainActivity.getCheckboxClickListener());
in my App I created a BaseActivity, from which I extend all my Activities.
Inside the BaseActivity I recognize the activity that is running through the following statement: this.getClass().getSimpleName() (ex: ActivityA)
Until I open new Activity (ActivityB, ActivityC, etc ...) everything works correctly.
The problem occurs when I use the back button of the phone (I get back from activityC to ActivityB). In this case the ActivityB is properly resumed from the Stack, but in BaseTable (of ActivityB) the value of this.getClass().getSimpleName() remains ActivityC.
How can I avoid this problem? You know you help me?
All you need to do is create a protected final String in your BaseActivity as follows...
public class MyBaseActivity extends Activity {
protected final String TAG = getClass().getSimpleName();
// Any other code here
}
Any Activity which extends MyBaseActivity will inherit the TAG field and it will be instantiated with the correct name at the time it's created.
I put a TAG field in all of my Android base classes so I can use it with Log to identify different objects when they log to logcat.
I have seen a few versions of this question before, but the reasons for this exception were different than my own it seems.
What I am trying to do:
-Main Activity class has a toolbar at the bottom, clicking the buttons will display a series of fragments, one after another.
- A class EditItemFragmentManager, which is instatiated on a button click, and has methods that display specific fragments based on the toolbar button clicked.
I would like to use this manager class I created because it cleans my code up significantly and will make adding more features later helpful.
Here is my EditItemFragmentManager class, I am not sure if extending Activity is a good idea or not, I think that it will put my MainActivity on pause
public class EditItemFragmentManager extends Activity{
//instance variables
public EditItemFragmentManager(){
// initialization of some variables
}
public void editItem(){
editItemSequence();
}
private void editItemSequence(){
EditNameFragment enf = new EditNameFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(editNameFragment, EDIT_FRAG_TAG);
fragmentTransaction.addToBackStack(EDIT_FRAG_TAG);
fragmentTransaction.commit();
}
}
So it blows up when commit(); is called, giving me
java.lang.IllegalStateException: Activity has been destroyed
This is how I am trying to get this fragment from my MainActivity,
#Override
public void onClick(View view) {
EditIteFragmetManager manager = new EditIteFragmetManager();
manager.editItem();
}
I am still learning about the Acvtivity lifecycle in Android. I think my problem is something due to this class extending Activity, which puts my Main on pause, and the FragmentTransaction has nothing to commit to? If so, I need to get the existing instance of my main activity and call it on that? This is where I'm a bit lost, if anyone who understands the lifecycle of Activities/Fragments explain how I could go about implementing this while still having a helper class such as this?
If you're using the SupportFragmentManager, then you need to extend from FragmentActivity, and not just Activity. Also make sure that you imported the Fragment from the v4 support library, and not android.app.
Other than that, you seem to be instantiating a subclass of Activity with "new", which is terrible. Create activities only using Intents.
I solved this issue by moving my manager class to become a private inner class of my main, since they are so tightly coupled. No fragment issues now.
This is a problem I didn't forsee when designing the structure of my applicaton.
Basically i have 3 classes.
ProfileActivity //The main activity for the application.
ProfileView //Which extends Linear Layout so that I can tinker with my own view. Note that this is just part of the whole activity and not the main layout
ProfileData //This is just a class for storing data.
The activity contains multiple ProfileViews which each contain one profileData.
Heres where I'm stuck. My profile View has an on click method assigned which needs to call a populate method in the activity. Unfortunately ````
//I'm inside the activity
public void populateProfileDataForm(ProfileData pd)
{
//edit some text fields and other widgets from here
}
Is there any way to call the activity method from the profileView class?
If not, then the error is in my design and can anyone point me towards a better solution for binding data, view and activitys?
When you create a view, you always need a context, and always pass a activity to it.
So try use below code:
If(m_context instanceof ProfileActivity)
{
ProfileActivity activity = (ProfileActivity)m_context;
// Then call the method in the activity.
}
Or write an interface onProfileViewClickListener such as view.onclickListener. then the ProfileActivity implements it and set it to the ProfileView.
What about if you assign an OnClickListener or OnTouchListener in your ProfileActivity Class and in this Listener you cann call the populate Method. In the OnTouchListener you can get the Position of the TouchEvent so you can assign it to a specific Profile.
In my application there are 14 activities. Out of that 9 activity contains custom title bar and tab pane. so here I need to write this common code at one place instead of redundant code in each activity that contain custom title bar and tab pane code (i.e layout and it's activity specific code)
What are the possible ways to do this?
The common way is:
Create a super class called, for instance, CommonActivity which extends Activity
Put the boilerplate code inside that class
Then make your activities extend CommonActivity instead of Activity:
Here a simple example:
public class CommonActivity extends Activity{
public void onCreate(Bundle b){
super.onCreate(b);
// code that is repeated
}
protected void moreRepeatitiveCode(){
}
}
And your current activities:
public class AnActivity extends CommonActivity{
public void onCreate(Bundle b){
super.onCreate(b);
// specific code
}
}
Hmm.. Common code doesn't always need to be in Activity class but just regular class. Than we could call those methods according to our needs referring to the common code class.
Am I right with this example?
Of course in case we need it like Activity, above proposal would work perfectly if we take care of Activity lifecycle and we don't forget to add it to manifest file.
In general Activities should just create UI, handle events occurrences and delegate business logic and/or other actions to the other components in our App.
Cheers