Android with only one activity and multiple fragments - android

Sorry for my english,
I've read that between activity or fragment, we should use fragment as soon as we can. Because of that, I was developing my entire app with a lot of fragment and just one activity, switching from one to another with the beginTransaction().replace() method. But it seems that I have to implement all my event methods (like onClick) in the activity and not in the fragment. So if I develope everything in only one activity, I will have all my event method on just on class, wich will be unreadable...
I would like to know what is the best thing to do : one activity with a lot of fragment, or multiple activities... how ?
Thanks.

An entire app with only one activity and a lot of fragments might not be a very good idea.
If your app functionalities requires it, you can have multiple fragments managed by one activity : supporting tablets and handset / navigation drawer / ViewPager etc.
Anyway, using fragments doesn't force you to code all your event methods in the main activity. Your main activity handles the fragments creation/swaps but most of the actions that you do on a fragment's screen can be coded in the fragment class.
If you want to re-use a fragment in multiple activities it has to be this way.
As said in the google doc :
"You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a "sub activity" that you can reuse in different activities)".
The specific code you put in your main activity should be easily refactored in the fragments.
Did you look the official documentation ? There might be some good examples.
http://developer.android.com/guide/components/fragments.html
Coding actions in the fragment is very similar to coding them in the activity :
You can handle action bar actions in the fragment :
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Inflate your menu
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.option1:
// Handle option 1
return true;
case R.id.option2:
// Handle option 2
return true;
default:
return super.onOptionsItemSelected(item);
}
}
You can handle buttons actions in the fragment :
Button yourButton = (Button) view.findViewById(R.id.yourbutton);
btnUpload.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Handle button click
}
});
You can code asynctask in the fragment.
If you have a list, you can code list context menu actions in the fragment :
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
// Inflate your context menu
}
#Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.option1:
// Handle option 1
return true;
case R.id.option2:
// Handle option 2
return true;
default:
return super.onContextItemSelected(item);
}
}
AlertDialog work in a fragment.
Etc. Etc.
Do you have a specific action that gives you trouble refactoring in a fragment ?

Related

LayoutInflater Onclick Not working

I have 2 xml one main xml and one menu xml. I want to get the click event from menu xml to main xml activity. is there is any way to solve this problem.
Main Home Page
Menu Page
Now click on the menu page button event i want in my main home activity page.
I done Like this
View otherLayout = LayoutInflater.from(this).inflate(R.layout.menu_layout,null);
Button tstclick = (Button) otherLayout.findViewById(R.id.textclick);
tstclick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Your thing
System.exit(0);
}
});
The Activity class provides us two methods to inflate/show menu items and work with them or assign some tasks to them. They are:
onCreateOptionsMenu(Menu menu)
onOptionsItemSelected(MenuItem item)
The onCreateOptionsMenu() method is responsible for creating and inflating the menu by help of MenuInflater class.
The onOptionsItemSelected() method is responsible for assigning tasks to each menu item. Each menu item is identified by help of its unique ID.
In order to show and work with menu's in any Activity, both the methods needs to be overridden as shown below:
public class MainActivity extends AppCompatActivity {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
// All your menu items will come here. Each menu item ID will become a separate case in the Switch case
default:
return super.onOptionsItemSelected(menuItem);
}
}
}
As soon as the user clicks or taps on any menu item, the onOptionsItemSelected() method is called by the Android OS and then the ID for that particular menu item will be matched in the method. The group of statements specified in the respective case will then be executed.
For more info visit the following links:
http://developer.android.com/guide/topics/ui/menus.html
http://developer.android.com/reference/android/view/MenuInflater.html

Action bar back button from un activity to previous activity fragment

I'm using a navigation drawer in a main activity with many fragments
For example :
I have fragment 1 for presentation
And fragment2 that contains a listView.
If I click an item in the listView it will launch another activity
When i click the action bar back button i want to go to the main activity fragment2
But the fragment1 is used
How can I use the second fragment
Thanks for your answers
In your second activity's onCreate, put(although i think you already have) :
getActionBar().setDisplayHomeAsUpEnabled(true);
and inside second activity's options handling(i think you've done this too) :
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home:
//perhaps use intent if needed but i'm sure there's a specific intent action for up you can use to handle
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
and inside your first activity's onCreate's intent handler for example :
if(Intent.ACTION_VIEW.equals(action))
you set the current page of the viewpager with viewpager.setCurrentItem(position). If you want to change the page for different items you could use intent.putExtra() in your second activity and then retrieve it in your first activity.
I have added the code below in my second activity :
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
onBackPressed();
return true;
}
It's working as I expected
is it a clean solution ?
Thanks

Menu appearing on other menu android

I am developing an app using actionbar sherlock. The problem I have is that I have the menus setup and all my xml files are correct since I get no compiler and logcat error.
THe problem I am having is that when I load a fragment A with its own specific menu everything is good. But when I then move to another fragment B, fragment A menu appears on fragment B. Any ideas as to whats causing this.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.product_allergy, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch(item.getItemId()){
case R.id.productClear:
addtoList();
break;
}
return true;
}
The menu is belonged to Activty rather than fragments. Fragment A and B are in same activity, so when A adds some items into activity's menu and then it is replaced by Fragment B, the activity does not change, i.e., the menu is still there.
If you want your Fragment B to have different menu items, override the onCreateOptionsMenu() method, remember to setHasOptionsMenu(true) in Fragment's onCreate() method.
FYI, if you simply want to clear prior fragment's menu item, call menu.clear() in onCreateOptionsMenu().
EDIT:
When you are trying to handle the action bar callback in fragments, you should do something like below:
#override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.fragmentActionBarButton:
//your code
return true;
default:
return super.onOptionsItemSelected(item);
}
}

Re-using Options menu code

Is there a convenient way of showing the same Options menu options in multiple Activities?
Example: In my app, I display a TV Guide in one of three ways.
Seven day guide (TabActivity with 7 tabs)
All channels 'Now showing' (ListActivity)
All shows today by start time (Activity - could be changed easily to ListActivity)
For the Options menu in the TabActivity, the code is quite simple...
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
menu.clear();
inflater.inflate(R.menu.gv_options_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.view:
...
...
}
}
...but at the moment it seems I need to copy/paste it to the other two Activities which I don't like doing. If I change the Options menu code for one I'll need to do it for the other two also.
The only alternative I can think of is I have a 'helper' class (POJO) to which I could add a method and pass the context into to allow use of the getMenuInflator() method and another method I could pass the result of item.getItemId() into to process with the switch-case.
What is the normal way of having multiple Activities with the same Options menu?
Create a simple separate class with these two methods:
public class MyMenuHandler {
private Activity mActivity;
public MyMenuHandler(Activity activity) {
mActivity = activity;
}
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = mActivity.getMenuInflater();
menu.clear();
inflater.inflate(R.menu.gv_options_menu, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.view:
...
}
}
}
In your activities override those callback methods and redirect the call to an instance of your MyMenuHandler class:
public class MyActivity1 extends TabActivity {
private MyMenuHandler mMenuHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
...
mMenuHandler = new MyMenuHandler(this);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// you may also add here some items which are specific
// for one activity, not for the others
...
return mMenuHandler.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// handle selection of your specific items here,
// if none of them has been selected call mMenuHandler method
...
return mMenuHandler.onOptionsItemSelected(item);
}
}
This will let you hold in one place the code which respond to selection of your basic menu items, so there will be no need to worry about copy-pasting it to all activities which are to have the same menu.
One approach is to use inheritance with your Activities. Create a base Activity that implements the options menu methods and then each child Activity will gain that functionality. This is the recommended approach on the Android developer site:
Tip: If your application contains multiple activities and some of them provide the same Options Menu, consider creating an activity that implements nothing except the onCreateOptionsMenu() and onOptionsItemSelected() methods. Then extend this class for each activity that should share the same Options Menu. This way, you have to manage only one set of code for handling menu actions and each descendant class inherits the menu behaviors.
Unfortunately this won't work for you as you are not inheriting from Activity itself but differing subclasses of it, but that is the 'normal' way to do it.
You can encapsulate your action menu in a fragment. In this way you only need to add the fragment in the onCreate menu of your activity.
You need to call setHasOptionsMenu once the fragment is created.
To add the add fragment use a tag instead of a layout id.

How to handle onContextItemSelected in a multi fragment activity?

I'm currently trying to adapt my application to use the "Compatibility Libraries for Android v4" to provide the benefits of the usage of fragments even to Android 1.6 users.
The implementation of a context menu seems to be tricky:
The main activity of the application
is extending the FragmentActivity
class.
The fragments are all based on one
class which extends the Fragment class.
The fragment class is calling
registerForContextMenu() in its onCreateView() method and overrides the methods
onCreateContextMenu() and onContextItemSelected().
For onCreateContextMenu() this works pretty well. The context menu is inflated from a resource file and slightly modified based on the selected item (which is based on a listView... even if the fragment is not an ListFragment).
The problem occurs when a context menu entry is selected.
onContextItemSelected() is called for all currently existing fragments starting with the first added one.
In my case the fragments are used to show the content of a folder structure. When the context menu of a subfolder fragment is opened and a menu item is selected, onContextItemSelected() is first called on the upper levels (depending on how many fragments are allowed/visible in this moment).
Right now, I use a workaround by a field on activity level which holds the tag of last fragment calling its onCreateContextMenu(). This way I can call "return super.onContextItemSelected(item)" in the begin of onContextItemSelected() when the stored tag is not the same as getTag().
But this approach looks a bit dirty to me.
Why is onContextItemSelected() called on all fragments? and not just one the one that was calling onCreateContextMenu()?
What is the most elegant way to handle this?
I'll post an answer even though you found a workaround because I just dealt with a similar issue. When you inflate the context menu for a specific fragment, assign each menu item a groupId that is unique to the fragment. Then test for the groupId in 'onContextItemSelected.' For Example:
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_1, 0, R.string.src1);
menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_2, 0, R.string.src2);
}
public boolean onContextItemSelected(MenuItem item) {
//only this fragment's context menus have group ID of -1
if (item.getGroupId() == UNIQUE_FRAGMENT_GROUP_ID) {
switch(item.getItemId()) {
case MENU_OPTION_1: doSomething(); break;
case MENU_OPTION_2: doSomethingElse(); break;
}
}
This way all of your fragments will still receive calls to 'onContextItemSelected,' but only the correct one will respond, thus avoiding the need to write activity-level code. I assume a modified version of this technique could work even though you aren't using 'menu.add(...)'
Another one solution:
#Override
public boolean onContextItemSelected(MenuItem item) {
if (getUserVisibleHint()) {
// context menu logic
return true;
}
return false;
}
Based upon this patch from Jake Wharton.
I liked the simple solution by Sergei G (based on Jake Wharton fix), but inverted because it is easier to add to several fragments:
public boolean onContextItemSelected(android.view.MenuItem item)
{
if( getUserVisibleHint() == false )
{
return false;
}
// The rest of your onConextItemSelect code
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
}
After that, the code same as it was before.
I found out a very easy solution. As onCreateContextMenu() is called every time the ContextMenu is created I set a boolean variable to true.
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.film_menu, menu);
bMenu=true;
}
The only other thing I have to do is ask for that variable OnContextItemSelected()
public boolean onContextItemSelected(MenuItem item) {
if (bMenu) {
bMenu=false;
if (item.getItemId() == R.id.filmProperties) {
///Your code
return true;
} else {
return super.onContextItemSelected(item);
}
} else {
return super.onContextItemSelected(item);
}
}
That's it.
I found an alternative. It does not change anything on my problem above, but it makes it pointless.
I have remove the context menu completely from my application. Instead I capture the longclick on a list item and change the visible buttons of the action bar in this moment.
From the user point of view this is much more tablet like as a context menu.
In backward compatible applications the actionbar does not exist. So I've decided to build my own (kind of toolbar on top) for the devices pre Honeycomb.
If you would like to stay with the context menu, I did not find a better solution as the workaround I've mentioned above.
In my first fragment, i have set all my menu id > 5000 so, as first line of code of onContextItemSelected of first fragment i have
if (item.getItemId() < 5000) return false;
and the second fragment will be invoked.
If you are using adapters with listviews in your fragment this might help.
public boolean onContextItemSelected(final MenuItem item) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
//Check if the context menu call came from the list in this fragment (needed for support for multiple fragments in one screen)
if (info.targetView.getParent() != getView().findViewById(android.R.id.list))
return super.onContextItemSelected(item);
//Handle context menu item call
switch (item.getItemId()) {
...
}
}
Just change
#Override
public boolean onContextItemSelected(MenuItem item) {
return true;
}
to
#Override
public boolean onContextItemSelected(MenuItem item) {
return super.onContextItemSelected(item);
}
and will work great!!!
IMHO we may just check if target view is child of fragment listview. It is very simple and work for me well. I just added to all my fragments:if (getListView.getPositionForView(info.targetView) == -1)
return false when migrate from older API
This is example from one of my parent fragments. This is Scala, but I hope you got an idea.
#Loggable
override def onContextItemSelected(menuItem: MenuItem): Boolean = {
for {
filterBlock <- TabContent.filterBlock
optionBlock <- TabContent.optionBlock
environmentBlock <- TabContent.environmentBlock
componentBlock <- TabContent.componentBlock
} yield menuItem.getMenuInfo match {
case info: AdapterContextMenuInfo =>
if (getListView.getPositionForView(info.targetView) == -1)
return false
TabContent.adapter.getItem(info.position) match {
case item: FilterBlock.Item =>
filterBlock.onContextItemSelected(menuItem, item)
case item: OptionBlock.Item =>
optionBlock.onContextItemSelected(menuItem, item)
case item: EnvironmentBlock.Item =>
environmentBlock.onContextItemSelected(menuItem, item)
case item: ComponentBlock.Item =>
componentBlock.onContextItemSelected(menuItem, item)
case item =>
log.debug("skip unknown context menu item " + info.targetView)
false
}
case info =>
log.fatal("unsupported menu info " + info)
false
}
} getOrElse false
P.S. If you trace calls of onContextItemSelected(...) you may notify that super.onContextItemSelected(item) return always false. Valid onContextItemSelected invoked AFTER, not WITHIN. So super.onContextItemSelected(item) is useless and I replaced it with false.
I found an easier solution than the exposed:
public boolean onContextItemSelected(MenuItem item) {
ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList);
if (!yourList.hasFocus())
return false;
switch(item.getItemId()) {
...
}
}
In the method changed return true; to return super.onContextItemSelected(item); in my onContextItemSelected() override and everything started working.

Categories

Resources