I'm trying to create my first Android app that looks like following: there is main activity with multiple fragments initialized by FragmentPagerAdapter. There is another activity (SettingsActivity) where I want to list all the fragment names and allow hiding some of them. To hide them I want to use the following:
FragmentManager fm=getFragmentManager();
Fragment myFragment=fm.findFragmentByTag("tag");
fm.beginTransaction().hide(myFragment).commit();
The problem is that I don't know fragment id or tag, not sure if they exist. How I can get them? Should I switch to XML definition to make it possible?
Adapter:
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index){
case 0:
return new CoverFragment();
case 1:
return new NumbersConverterFragment();
case 2:
return new TempConverterFragment();
case 3:
return new LengthConverterFragment();
case 4:
return new AreaConverterFragment();
case 5:
return new VolumeConverterFragment();
case 6:
return new WeightConverterFragment();
case 7:
return new SpeedConverterFragment();
}
return null;
}
#Override
public int getCount() {
return 8;
}
Main activity:
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter tabsPagerAdapter;
private ActionBar actionBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
String[] tabs={getString(R.string.title_section0), getString(R.string.title_section1),getString(R.string.title_section2)};
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager=(ViewPager) findViewById(R.id.pager);
actionBar=getActionBar();
tabsPagerAdapter=new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(tabsPagerAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for(String tab : tabs){
actionBar.addTab(actionBar.newTab().setText(tab).setTabListener(this));
}
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
...
});
}
Fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#fbfdfb"
>
<TextView android:text="#string/celsius_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtCelsius" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
<TextView android:text="#string/fahrenheit_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtFahrenheit" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
<TextView android:text="#string/kelvin_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtKelvin" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
</LinearLayout>
Fragment class:
public class TempConverterFragment extends Fragment {
EditText txtCelsius, txtFahrenheit, txtKelvin;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.temp_converter_fragment, container, false);
txtCelsius = (EditText) rootView.findViewById(R.id.txtCelsius);
txtFahrenheit = (EditText) rootView.findViewById(R.id.txtFahrenheit);
txtKelvin = (EditText) rootView.findViewById(R.id.txtKelvin);
...
}
...
}
Thanks in advance.
If SettingsActivity is not the Activity holding the FragmentPagerAdapter, then you would have to re-create all the fragments. The nature of a fragment is to be closely tied to it's activity.
If SettingsActivity is the Activity holding the FragmentPagerAdapter, then As I recall, FragmentPagerAdapter will initialize all the 8 fragments as soon as possible to have them ready when you swipe, unlike FragmentStatePagerAdapter. This means that you should (I think) be able to create each fragment in the constructor TabsPagerAdapter and keeping a reference to them, which you could access using getter methods on the TabsPagerAdapter.
Here is an example of how to get easy access to your pageradapter fragments:
public class DisplayPagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = "DisplayPagerAdapter";
SparseArray<DisplayFragment> registeredFragments = new SparseArray<DisplayFragment>();
#Inject DisplayCoreModule display;
public DisplayPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return (display != null && display.getPagesCount() > 0) ? display.getPagesCount() : 1;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem " + position);
return DisplayFragment.newInstance(position);
}
#Override
public CharSequence getPageTitle(int position) {
if (display != null && display.getPagesCount() > 0) {
return "Side " + (position+1);
} else {
return super.getPageTitle(position);
}
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d(TAG, "instantiateItem " + position);
DisplayFragment fragment = (DisplayFragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
public SparseArray<DisplayFragment> getRegisteredFragments() {
return registeredFragments;
}
}
Now if you implement this usage of registeredFragments , you can call tabsPagerAdapter.getRegisteredFragment(2) to get your TempConverterFragment.
SparseArray<DisplayFragment> should be SparseArray<Fragment> in your case
Now this does not solve the your SettingsActivity problem. But if I understand you correctly, then adding the fragments your want directly in the layout XML of SettingsActivity would make sense. Then it would be easy to temporarily hide the fragments or whatever using:
FragmentManager fm=getFragmentManager();
Fragment myFragment=fm.findFragmentById(R.id.frag_tempconverter)
fm.beginTransaction().hide(myFragment).commit();
Notice the use of findFragmentById. The tag is usually used for dynamically added fragments (atleast in my mind). The findFragmentById will surely return a fragment if it is defined in the XML layout but just to be clear, it will be a new instance of the fragment.
To address your questions:
What if I move the fragments to the main activity XML? Won't it make things simpler
Do not think so, the updated answer shows how to easily access the fragments (from within your main activity).
Though not sure I can use FragmentManager in SettingsActivity
Sure you can. You can add new fragments, access available fragments (from predefined XML using findById or dynamically added using findByTag). You cannot, however, access the same instance of the fragment as was kept by your main activity.
To share information between the fragments and the two activities, you need to persist the state of your fragments somehow (which is a different topic).
All in all I think you are on the right path, you just need to combine the right pieces of the puzzle :)
Related
The cheat code app will have a list of games. And when each game is tapped, the cheat codes for that game will be displayed. My question is, do I have to create an activity for each game listed or is there a way to make it work in one activity.
No you don’t need to make individual activity for each game listed rather you can go for a RecyclerView and fragments combination to make it work in one activity.
here i have added a recyclerView for the game list and the when items of the game list will be clicked then the recylerview will be hidden and the frameLayout id(content) will be populated with the fragment and when the back btn is pressed while inside the fragment reverse will happen( hiding the fragment and showing the recyclerView).
though this whole approach can be far more simplified if all the game have similar type of data to show then you can,
in that case you can pass data to the fragment and make it work with only one fragment.
hope this answer the question.
**
XML code
**
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
**
Adapter code
**
public abstract class SettingsAdapter extends RecyclerView.Adapter<SettingsAdapter.MyViewHolder> {
private static final String TAG = "SettingsAdapter";
private LayoutInflater inflater;
private Context context;
private List<SettingData> data;
public SettingsAdapter(Context context , List<SettingData> data){
inflater = LayoutInflater.from(context);
this.data = data;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_settings_row, parent, false);
SettingsAdapter.MyViewHolder holder = new SettingsAdapter.MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
SettingData current = data.get(position);
holder.settinsName.setText(current.settingName);
holder.hostRelativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OnButtonClicked(v, position);
}
});
}
#Override
public int getItemCount() {
return data.size();
}
protected abstract void OnButtonClicked(View v ,int position);
class MyViewHolder extends RecyclerView.ViewHolder {
private final TextView settinsName;
private final RelativeLayout hostRelativeLayout;
public MyViewHolder(View itemView) {
super(itemView);
settinsName = itemView.findViewById(R.id.settings_name);
hostRelativeLayout = itemView.findViewById(R.id.hostRelativeLayout);
}
}
}
**
android activity code inside onCreate
**
SettingsAdapter settingsAdapter = new SettingsAdapter(this, getFinalData()) {
#SuppressLint("RestrictedApi")
#Override
protected void OnButtonClicked(View v, int position) {
switch (position) {
case 0:
settingsContent.setVisibility(View.GONE);
fab.show();
configAppbarTittle(StudentSettingsActivity.this, settingNameArr[position]);
appBarLayout.setExpanded(false);
getSupportFragmentManager().beginTransaction().replace(R.id.content, new SavedPlacesFragment()).commit();
break;
case 1:
//similar
break;
case 2:
//similar
break;
case 3:
//similar
break;
default:
break;
}
}
};
settingsRecycleView.setAdapter(settingsAdapter);
settingsRecycleView.setLayoutManager(new LinearLayoutManager(this));
**
fragment code inside activity
**
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class SavedPlacesFragment extends Fragment {
private StudentSettingsActivity myMainActivity;
private RecyclerView savedPlacesRecycler;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
myMainActivity = (StudentSettingsActivity) getActivity();
}
#SuppressWarnings("unchecked")
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//myMainActivity = (StudentSettingsActivity) getActivity();
View rootView = inflater.inflate(R.layout.stu_setting_saved_places_fragment, container, false);
return rootView;
}
private void flush(String msg) {
Toast.makeText(myMainActivity, msg, Toast.LENGTH_SHORT).show();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
myMainActivity.fab.hide();
startActivity(new Intent(getActivity(), StudentSettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
Please understand the fact that i have copy pasted from my production
code here. Just to demonstrate the way. If you copy paste this code it
won't work. You have to read through the code to understand the way
it's been implemented and then write it in your own way
...You say that the cheat code app have list of games
You create one activity to show the list
....And if u tap on list to get new game
So you have to create fragment or activity to show the game by click on list
You can create a single Activity and have it hold multiple fragments.
Your Activity ( Lets say MainActivity ) will be responsible for displaying fragments.
At a high level, you need 2 fragments:
GameListFragment
for displaying Game List and game item click. You will need a recycler view to display list of games on this fragment.
GameCheatCodeDetailsFragment
for displaying Cheat Code.
Refer to SmartShows to get some code reference.
I keep having an issue with my android app where it is crashing with the following error when swiping between tabs:
09-16 16:19:27.142 4750-4750/com.khackett.runmate E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.khackett.runmate, PID: 4750
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:167)
at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:135)
at com.parse.ParseTaskUtils$2$1.run(ParseTaskUtils.java:115)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
This is the MyRunsFragment:
public class MyRunsFragment extends ListFragment {
protected SwipeRefreshLayout mSwipeRefreshLayout;
// member variable to store the list of routes the user has accepted
protected List<ParseObject> mAcceptedRoutes;
private int MY_STATUS_CODE = 1111;
// Default constructor for MyRunsFragment
public MyRunsFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_my_runs, container, false);
// Set SwipeRefreshLayout component
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
// Set the onRefreshListener
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
mSwipeRefreshLayout.setColorSchemeResources(
R.color.swipeRefresh1,
R.color.swipeRefresh2,
R.color.swipeRefresh3,
R.color.swipeRefresh4);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Retrieve the accepted routes from the Parse backend
retrieveAcceptedRoutes();
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
// create the message object which is set to the message at the current position
ParseObject route = mAcceptedRoutes.get(position);
// String messageType = message.getString(ParseConstants.KEY_FILE_TYPE);
JSONArray parseList = route.getJSONArray(ParseConstants.KEY_LATLNG_POINTS);
JSONArray parseListBounds = route.getJSONArray(ParseConstants.KEY_LATLNG_BOUNDARY_POINTS);
String objectId = route.getObjectId();
String routeName = route.getString(ParseConstants.KEY_ROUTE_NAME);
// JSONArray ids = route.getJSONArray(ParseConstants.KEY_RECIPIENT_IDS);
// Start a map activity to display the route
Intent intent = new Intent(getActivity(), MapsActivityTrackRun.class);
intent.putExtra("parseLatLngList", parseList.toString());
intent.putExtra("parseLatLngBoundsList", parseListBounds.toString());
intent.putExtra("myRunsObjectId", objectId);
intent.putExtra("myRunsRouteName", routeName);
// Start the MapsActivityDisplayRoute activity
startActivityForResult(intent, MY_STATUS_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
private void retrieveAcceptedRoutes() {
// query the routes class/table in parse
// get messages where the logged in user ID is in the list of the recipient ID's (we only want to retrieve the messages sent to us)
// querying the message class is similar to how we have been querying users
ParseQuery<ParseObject> queryRoute = new ParseQuery<ParseObject>(ParseConstants.CLASS_ROUTES);
// use the 'where' clause to search through the messages to find where our user ID is one of the recipients
queryRoute.whereEqualTo(ParseConstants.KEY_ACCEPTED_RECIPIENT_IDS, ParseUser.getCurrentUser().getObjectId());
// order results so that most recent message are at the top of the inbox
queryRoute.addDescendingOrder(ParseConstants.KEY_CREATED_AT);
// query is ready - run it
queryRoute.findInBackground(new FindCallback<ParseObject>() {
// When the retrieval is done from the Parse query, the done() callback method is called
#Override
public void done(List<ParseObject> routes, ParseException e) {
// dismiss the progress indicator here
// getActivity().setProgressBarIndeterminateVisibility(false);
// End refreshing once routes are retrieved
// done() is called from onResume() and the OnRefreshListener
// Need to check that its called from the the OnRefreshListener before ending it
if (mSwipeRefreshLayout.isRefreshing()) {
mSwipeRefreshLayout.setRefreshing(false);
}
// the list being returned is a list of routes
if (e == null) {
// successful - routes found. They are stored as a list in messages
mAcceptedRoutes = routes;
// adapt this data for the list view, showing the senders name
// create an array of strings to store the usernames and set the size equal to that of the list returned
String[] usernames = new String[mAcceptedRoutes.size()];
// enhanced for loop to go through the list of users and create an array of usernames
int i = 0;
for (ParseObject message : mAcceptedRoutes) {
// get the specific key
usernames[i] = message.getString(ParseConstants.KEY_SENDER_NAME);
i++;
}
// Create the adapter once and update its state on each refresh
if (getListView().getAdapter() == null) {
// the above adapter code is now replaced with the following line
RouteMessageAdapter adapter = new RouteMessageAdapter(getListView().getContext(), mAcceptedRoutes);
// Force a refresh of the list once data has changed
adapter.notifyDataSetChanged();
// need to call setListAdapter for this activity. This method is specifically from the ListActivity class
setListAdapter(adapter);
} else {
// refill the adapter
// cast it to RouteMessageAdapter
((RouteMessageAdapter) getListView().getAdapter()).refill(mAcceptedRoutes);
}
}
}
});
}
protected SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
// When list is swiped down to refresh, retrieve the users runs from the Parse backend
retrieveAcceptedRoutes();
}
};
}
And the fragment_my_runs layout file:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity$PlaceholderFragment">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:clipToPadding="false"
android:paddingBottom="#dimen/inbox_vertical_margin"/>
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/empty_inbox_label"
android:textSize="#dimen/default_text_size"/>
</RelativeLayout>
The TabFragmentContainer
public class TabFragmentContainer extends Fragment {
// Create the FragmentPagerAdapter that will provide and manage tabs for each section.
public static MyFragmentPagerAdapter myFragmentPagerAdapter;
public static TabLayout tabLayout;
// The ViewPager is a layout widget in which each child view is a separate tab in the layout.
// It will host the section contents.
public static ViewPager viewPager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate tab_layout_fragment_container view and setup views for the TabLayout and ViewPager items.
View view = inflater.inflate(R.layout.tab_layout_fragment_container, null);
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
// Instantiate the adapter that will return a fragment for each of the three sections of the main activity
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());
// Set up the adapter for the ViewPager
viewPager.setAdapter(myFragmentPagerAdapter);
// Runnable() method required to implement setupWithViewPager() method
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, false);
// tabLayout.getTabAt(1).select();
}
});
// Return the created View
return view;
}
}
The FragmentPagerAdapter:
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
// The context to be passed in when the adapter is created.
private Context mContext;
// The number of tabs in the layout.
public static int numberOfTabs = 3;
/**
* Default constructor that accepts a FragmentManager parameter to add or remove fragments.
*
* #param context the context from the activity using the adapter.
* #param fragmentManager the FragmentManager for managing Fragments inside of the TabFragmentContainer.
*/
public MyFragmentPagerAdapter(Context context, FragmentManager fragmentManager) {
super(fragmentManager);
mContext = context;
}
/**
* Method to return the relevant fragment for the selected tab.
*/
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new MyRunsFragment();
case 1:
return new InboxRouteFragment();
case 2:
return new FriendsFragment();
}
return null;
}
/**
* Method that gets the number of tabs in the layout.
*
* #return the number of tabs in the layout.
*/
#Override
public int getCount() {
return numberOfTabs;
}
/**
* Method that returns the title of each tab in the layout.
*/
#Override
public CharSequence getPageTitle(int position) {
Locale locale = Locale.getDefault();
switch (position) {
case 0:
return mContext.getString(R.string.title_section1).toUpperCase(locale);
case 1:
return mContext.getString(R.string.title_section2).toUpperCase(locale);
case 2:
return mContext.getString(R.string.title_section3).toUpperCase(locale);
}
return null;
}
}
The tab_layout_fragment_container file that contains the ViewPager widget:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/ColorPrimaryPurple"
app:tabGravity="fill"
app:tabIndicatorColor="#color/ColorPrimaryPurple"
app:tabMode="fixed"
app:tabSelectedTextColor="#color/textColorPrimary"
app:tabTextColor="#color/pressedPurpleButton">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</LinearLayout>
The onCreate() method in my MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialise the DrawerLayout and NavigationView views.
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
mNavigationView = (NavigationView) findViewById(R.id.navigationDrawerMenu);
// Inflate the first fragment to be displayed when logged into the app.
mFragmentManager = getSupportFragmentManager();
mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.containerView, new TabFragmentContainer()).commit();
// Setup click events on the NavigationView items.
// When an item is selected, replace the tab fragment container with the requested fragment.
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
mDrawerLayout.closeDrawers();
if (menuItem.getItemId() == R.id.navItemHome) {
FragmentTransaction tabFragmentContainer = mFragmentManager.beginTransaction();
tabFragmentContainer.replace(R.id.containerView, new TabFragmentContainer()).commit();
}
if (menuItem.getItemId() == R.id.navItemRunHistory) {
FragmentTransaction runHistoryFragment = mFragmentManager.beginTransaction();
runHistoryFragment.replace(R.id.containerView, new RunHistoryFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemSettings) {
FragmentTransaction settingsFragment = mFragmentManager.beginTransaction();
settingsFragment.replace(R.id.containerView, new SettingsFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemHelp) {
FragmentTransaction instructionsFragment = mFragmentManager.beginTransaction();
instructionsFragment.replace(R.id.containerView, new InstructionsFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemMyProfile) {
FragmentTransaction myProfileFragment = mFragmentManager.beginTransaction();
myProfileFragment.replace(R.id.containerView, new MyProfileFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemLogOut) {
// User has selected log out option. Log user out and return to login screen.
ParseUser.logOut();
navigateToLogin();
}
return false;
}
});
// Set up the Toolbar.
setupToolbar();
}
I have followed other answers here and added the getListView() functionality to the onViewCreated() method but the problem still persists... Can anyone point out where I might be going wrong?
Based on these facts:
The exception is thrown because there is no root view yet when done() calls getListView().
done() is called when the query made by retrieveAcceptedRoutes() gets a response.
retrieveAcceptedRoutes is called in multiple places, including the OnRefreshListener mOnRefreshListener, which is registered as the refresh listener in onCreateView() before there is a root view (that is, before onCreateView() returns).
...it is possible for getListView() to be called before there is a root view.
Try moving these 3 statements from onCreateView() to onViewCreated(), so that way the refresh listener can only be called when there is a root view.
// Set SwipeRefreshLayout component
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
// Set the onRefreshListener
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
mSwipeRefreshLayout.setColorSchemeResources(
R.color.swipeRefresh1,
R.color.swipeRefresh2,
R.color.swipeRefresh3,
R.color.swipeRefresh4);
onViewCreated is called immediately after onCreateView, but the super.onViewCreated call is missing, perhaps this is root cause of your issue.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); // add this line back in
// Retrieve the accepted routes from the Parse backend
retrieveAcceptedRoutes();
}
I read your question again then I guess that:
Your ListFragment is destroyed while your background task keeps running. So when it's done, your callback would like to update the ListView which is no longer alive.
Actually, viewPager.setOffscreenPageLimit(3); may do the trick, but it's not a good practice. It forces your ViewPager to create and store more Fragments in memory which is not necessary. You can solve this without doing so.
What you should do: one of the following two practice should be fine, or both:
Destroy your task in your onPause or whatever lifecycle method, before your onDestroyView.
Exclude the code where you update your ListView inside your done() method. Make it a local method where you will check your ListView carefully, and there, you should ask your update process to run on UI thread to avoid any threading problem. Make sure to check if your getView() is not null (but not your getListView(), since it throws Exception if getView() returns null).
I recommend you to use both of them to make sure: your view is still useable and you don't waste your resource when running task in invisible fragment. Don't forget that by default, once your fragment is invisible, it is considered to be destroyed (not always, for example ViewPager keep reference of 2 fragments, but keep in mind that case).
remove all these imports:
import com.yourName.runmate.R;
Then resync your gradle and rebuild your project.
Also see here:
"cannot resolve symbol R" in Android Studio
edit
Your first obvious mistake in your Main is
mFragmentManager = getSupportFragmentManager();
should be:
mFragmentManager = getFragmentManager();
or change your Main activity to:
MainActivity extends FragmentActivity to make use of the support fragment manager.
You have a lot of unnecessary code in your question, majority of comments can be removed and imports for the purpose of this question.
What I have come up with is there is no activity, being used. The ListFragment needs to be attached to an Activity or you are trying to call that activity view before it is created.
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
If you are using Main, then you are not pulling them together well, from what I can see.
Firstly:
Take everything out of your onCreate and onCreateView (for all fragments) except the view inflater.
Place all the extra code into either onViewCreated or onActivityCreated. That way no methods can be called on a null view, as these are called after it is created.
Secondly, you need to sort out your activities and with what you're exactly trying to achieve.
You want a page viewer and a fragmentlist. The pageviewer needs to be associated with an activity, or activity fragment, not a fragment. Otherwise there is no view to attach the pageviewer pages to.
Use a FragmentActivity not a Fragment. To be the activity you run your ListFragment from.
public class TabFragmentContainer extends FragmentActivity {
MyPageAdapter pageAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_layout_fragment_container); // change to view layout.
// Instantiate the adapter that will return a fragment for each of the three sections of the main activity
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getFragmentManager(), getFragments());
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
// Set up the adapter for the ViewPager
viewPager.setAdapter(myFragmentPagerAdapter);
}
}
I would suggest putting this into your ListFragment, to ensure your activity is created. You will need to move most of your code from your onCreate methods and put them in onViewCreated or onActivityCreated
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayAdapter adapter = ArrayAdapter.createFromResource(getActivity(), R.layout.my_listview)layout, android.R.layout.simple_list_item_1);
setListAdapter(adapter);
getListView().setOnItemClickListener(this);
}
This code is just a guide, you'll need to tweak it.
Let me know if this helps.
These Q&As are excellent.
Content view not yet created
android Illegal state exception content view not yet create?
Fragment same principles applies to viewpager fragments ViewPager
Try to declare:
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
viewPager.setAdapter(myFragmentPagerAdapter);
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());
before:
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, false);
// tabLayout.getTabAt(1).select();
}
});
// Return the created View
return view;
I have a problem. I have a viewpager with 3 fragments inside. In first fragment i have some ImageViews.
First of all how make that imageviews visible with timer? I used thise code below but i have error which looks like: variable 'mImageView' is accessed from within inner class, needs to be declared class.
mImageView.setVisibility(View.INVISIBLE);
mImageView.postDelayed(new Runnable() {
public void run() {
mImageView.setVisibility(View.VISIBLE);
}
}, 5000);
How can i solve this problem?
Second I tried to move that elements (ImageViews) by X values when user start scrolling from first fragment to next fragment. It works but when i go to last 3-d fragment app crash. So why it happen?!
MainActivity.java
pager.setPageTransformer(false, new ViewPager.PageTransformer() {
#Override
public void transformPage(View page, float position) {
// transformation here
final float normalizedPosition = Math.abs(Math.abs(position) - 1);
page.setAlpha(normalizedPosition);
int pageWidth = page.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.setAlpha(0);
} else if (position <= 1) { // [-1,1]
ImageView mImageView = (ImageView)findViewById(R.id.imageView2);
mImageView.setTranslationX((float) (-(1 - position) * 1.7 * pageWidth));
mImageView.setVisibility(View.INVISIBLE);
mImageView.postDelayed(new Runnable() {
public void run() {
mImageView.setVisibility(View.VISIBLE);
}
}, 5000);
// The 0.5, 1.5 values you see here are what makes the view move in a different speed.
// The bigger the number, the faster the view will translate.
// The result float is preceded by a minus because the views travel in the opposite direction of the movement.
}
else{ // (1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
});
Third: Is it possible to make move elements by circle when user scroll. Need any help!
For your first question, as I said in the comment, you need to make the mImageView variable final
final ImageView mImageView = (ImageView)findViewById(R.id.imageView2);
Then, the null pointer exception, is probably caused (as Blackbelt said), because you're using the activity's findViewById method, and probably the imageView you need is in the fragment view:
final ImageView mImageView = (ImageView) page.findViewById(R.id.imageView2);
And for your 3º question, please explain what you mean by "move by circle", then I'll update my post(if I can) with an answer.
MainActivity.java
// Initialize the ViewPager and set an adapter
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
ViewPagerAdapter.java
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final int PAGES = 3;
private String[] titles={"News", "Organizations", "Map"};
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new TabFragment1();
case 1:
return new TabFragment2();
case 2:
return new TabFragment3();
default:
throw new IllegalArgumentException("The item position should be less or equal to:" + PAGES);
}
}
#Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
#Override
public int getCount() {
return PAGES;
}
}
As you see for each page I have individual fragment documents. Here below one of them:
public class TabFragment1 extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab_1, container, false);
}
}
fragment_tab_1.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/city"
android:id="#+id/imageView2"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
I have a single activity with a navigation drawer (the basic one provided by Eclipse new app wizard). I have a FrameLayout as a container for the different fragments of the app, which are replaced when selecting an item in the navigation drawer. They are also added to the BackStack.
These fragments contain a LinearLayout, which has some EditTexts and a Button. If the button is pressed, a new LinearLayout is created and a couple TextViews are added to it with the content of the EditTexts. The user can repeat this option more than once, so I cannot tell how many LinearLayouts I'll need, therefore I need to add them programmatically.
One of these fragments xml:
<LinearLayout
android:id="#+id/pen_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/new_pen_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:background="#drawable/border"
android:orientation="vertical"
android:paddingBottom="#dimen/home_section_margin_bottom"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/home_section_margin_top" >
<EditText
android:id="#+id/new_pen_round"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="#string/new_pen_round_hint"
android:textSize="#dimen/normal_text_size" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2" >
<Button
android:id="#+id/new_pen_cancel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/new_item_button_margin_right"
android:layout_weight="1"
android:background="#drawable/button_bg"
android:paddingBottom="#dimen/new_item_button_padding_bottom"
android:paddingTop="#dimen/new_item_button_padding_top"
android:text="#string/new_item_cancel_button"
android:textSize="#dimen/normal_text_size" />
<Button
android:id="#+id/new_pen_insert_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/new_item_button_margin_left"
android:layout_weight="1"
android:background="#drawable/button_bg"
android:paddingBottom="#dimen/new_item_button_padding_bottom"
android:paddingTop="#dimen/new_item_button_padding_top"
android:text="#string/new_pen_insert_button"
android:textSize="#dimen/normal_text_size" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
There are actually many other EditTexts but I removed them here to keep it short, the result is the same. It's java file:
public class PenaltiesFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_penalties, container, false);
Button insertNewPen = (Button) view.findViewById(R.id.new_pen_insert_button);
insertNewPen.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
TextView round = (TextView) getActivity().findViewById(R.id.new_pen_round);
LinearLayout layout = (LinearLayout) getActivity().findViewById(R.id.pen_layout);
int numChilds = layout.getChildCount();
CustomPenaltyLayout penalty = new CustomPenaltyLayout(getActivity(), round.getText());
layout.addView(penalty, numChilds - 1);
}
});
return view;
}
}
I removed some useless methods, which are just the default ones. CustomPenaltyLayoutis a subclass of LinearLayout which I created, it just creates some TextViews and adds them to itself.
Everything works fine here. The user inserts data in the EditText, presses the Insert button and a new layout is created and added in the fragment.
What I want to achieve is: say that I open the navigation drawer and select another page, the fragment gets replaced and if I go back to this fragment (via navigation drawer or via Back button) I want the text, that the user added, to be still there.
I do not call PenaltiesFragment.newInstance() everytime I switch back to this fragment, I instead create the PenaltiesFragment object once and keep using that one. This is what I do:
Fragment fragment;
switch (newContent) {
// various cases
case PEN:
if(penFragment == null) // penFragment is a private field of the Main Activity
penFragment = PenaltiesFragment.newInstance();
fragment = penFragment;
break;
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack("fragment back")
.commit();
I understand that onCreateView() is called again when the fragment is reloaded, right? So that is probably why a new, blank fragment is what I see. But how do I get the inserted CustomPenaltyLayout back? I cannot create it in the onCreateView() method.
I found a solution to my problem. I replaced the default FrameLayout that Android automatically created as a container for my fragments, with a ViewPager, then created a FragmentPagerAdapter like this:
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
// ...other cases
case PEN:
fragment = PenaltiesFragment.newInstance();
break;
// ...other cases
}
return fragment;
}
#Override
public int getCount() {
return 6;
}
}
Then the only thing left to do to keep all the views at all times has been to add this line to my activity onCreate method.
mPager.setOffscreenPageLimit(5);
See the documentation for details on how this method works.
This way, though, I had to reimplement all the back button logic, but it's still simple, and this is how I did it: I create a java.util.Stack<Integer> object, add fragment numbers to it (except when you use the back button, see below), and override onBackPressed() to make it pop the last viewed fragment instead of using the back stack, when my history stack is not empty.
You want to avoid pushing elements on the Stack when you press the back button, otherwise you will get stuck between two fragments if you keep using the back button, instead of eventually exiting.
My code:
MyAdapter mAdapter;
ViewPager mPager;
Stack<Integer> pageHistory;
int currentPage;
boolean saveToHistory;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.container);
mPager.setAdapter(mAdapter);
mPager.setOffscreenPageLimit(5);
pageHistory = new Stack<Integer>();
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if(saveToHistory)
pageHistory.push(Integer.valueOf(currentPage));
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
saveToHistory = true;
}
#Override
public void onBackPressed() {
if(pageHistory.empty())
super.onBackPressed();
else {
saveToHistory = false;
mPager.setCurrentItem(pageHistory.pop().intValue());
saveToHistory = true;
}
};
I want to be able to display a button and after that button is clicked for the string typed to appear in a listview. I have that done, but i dont want that to apply to every tab. I just want it to display the send button and text box in a certain tab. I did some research and found that I may have to create a different fragment class for every tab, but I dont know how I am suppose to do that.
Java Code
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView list = (ListView)findViewById(R.id.messageList);
adapter = new MyAdapter(this);
list.setAdapter(adapter);
// Set up the action bar.
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
// When swiping between different sections, select the corresponding
// tab. We can also use ActionBar.Tab#select() to do this if we have
// a reference to the Tab.
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
// Create a tab with text corresponding to the page title defined by
// the adapter. Also specify this Activity object, which implements
// the TabListener interface, as the callback (listener) for when
// this tab is selected.
actionBar.addTab(
actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
private void newGame() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Developed by");
builder.setMessage("Shirwa Mohamed #Team_Shirwa");
builder.setPositiveButton( "ok", null);
builder.create().show();
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a DummySectionFragment (defined as a static inner class
// below) with the page number as its lone argument.
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
return fragment;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}
/**
* A dummy fragment representing a section of the app, but that simply
* displays dummy text.
*/
public static class DummySectionFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
public static final String ARG_SECTION_NUMBER = "section_number";
public DummySectionFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main_dummy, container, false);
TextView dummyTextView = (TextView) rootView.findViewById(R.id.section_label);
dummyTextView.setText(Integer.toString(getArguments().getInt(ARG_SECTION_NUMBER)));
return rootView;
}
}
}
Activity XML :
<TabHost
android1:id="#android:id/tabhost"
android1:layout_width="match_parent"
android1:layout_height="match_parent"
android1:layout_alignParentLeft="true"
android1:layout_alignParentTop="true" >
<LinearLayout
android1:layout_width="match_parent"
android1:layout_height="match_parent"
android1:orientation="vertical" >
<TabWidget
android1:id="#android:id/tabs"
android1:layout_width="match_parent"
android1:layout_height="wrap_content" >
</TabWidget>
<FrameLayout
android1:id="#android:id/tabcontent"
android1:layout_width="match_parent"
android1:layout_height="match_parent" >
<LinearLayout
android1:id="#+id/tab1"
android1:layout_width="match_parent"
android1:layout_height="match_parent" >
</LinearLayout>
<LinearLayout
android1:id="#+id/tab2"
android1:layout_width="match_parent"
android1:layout_height="match_parent"
>
</LinearLayout>
<LinearLayout
android1:id="#+id/tab3"
android1:layout_width="match_parent"
android1:layout_height="match_parent" >
</LinearLayout>
</FrameLayout>
</LinearLayout>
</TabHost>
</RelativeLayout>
Fragment Class?
<TextView
android:id="#+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
You are right, you need to create a fragment for each tab. Look at this link http://developer.android.com/guide/topics/ui/actionbar.html#Tabs it will help you understand how it works and how to implement it.