I have a supported fragment activity which will load diff fragments. The fragment has some textView with id = "score" and I want to get its handle but findViewById for score's textView returns null. Why so?
textView is placed in fragment
public class MyActivity extends extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks{
private TextView scoreBoardTextView = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
scoreBoardTextView = (TextView) findViewById(R.id.score); //this returns null
}
#Override
public void onNavigationDrawerItemSelected(int position) {
//set fragment
}
}
Note:
Directly accessing fragment's views outside fragment is not a good idea. You should use fragment callback interfaces to handle such cases and avoid bugs. The following way works but it is not recommended as it is not a good practice.
If you want to access the TextView of Fragment inside its parent Activity then you should define a method inside your Fragment class like this:
public class MyFragment extends Fragment {
TextView mTextView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_main, container, false);
mTextView = (TextView) view.findViewById(R.id.textView1);
return view;
}
public void setTextViewText(String value){
mTextView.setText(value);
}
}
Now you can use this inside your Activity like this:
myFragment.setTextViewText("foo");
here myFragment is of type MyFragment.
If you want to access the whole TextView then you can define a method like this inside MyFragment.java:
public TextView getTextView1(){
return mTextView;
}
By this you can access the TextView itself.
Hope this Helps. :)
It is possible with following way:
Keep reference of inflated view in the Fragment like this :
public class MyFragment extends SherlockFragment{
MainMenuActivity activity;
public View view;
public MyFragment(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if ( getActivity() instanceof MainMenuActivity){
activity = (MainMenuActivity) getActivity();
}
view = inflater.inflate(R.layout.aboutus, container, false);
return view;
}
}
Create a function in the Activity, like this:
public class MainMenuActivity extends SherlockFragmentActivity {
SherlockFragment fragment = null;
public void switchContent(SherlockFragment fragment) {
this.fragment = fragment;
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.mainmenu, fragment)
.commit();
invalidateOptionsMenu();
}
Its purpose is to keep reference of current fragment. Whenever you wanna switch fragment, you call above function, like this (from fragment):
activity.switchContent( new MyFragment_2());
Now you've current fragment reference. So you can directly access Fragment's views in Activity like this: this.fragment.view
You have no need of reference of Fragment view to get its components in Activity. As you can directly access layout components of a Fragment in parent Activity.
Simply you can access any component by this
findViewById(R.id.child_of_fragment_layout);
In order to access the TextView or Button or whatever in your fragment you need to do the following:
public class BlankFragment extends Fragment {
public View view;
public TextView textView;
public Button button;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view =inflater.inflate(R.layout.fragment_blank, container, false);
textView = (TextView)view.getRootView().findViewById(R.id.textView_fragment1);
return view;
}
public void changeTextOfFragment(String text){
textView.setText(text);
view.setBackgroundResource(R.color.colorPrimaryDark);
}
Once that is done in your MainActivity or any other where you want to access your TextView from your Fragment you should make sure to set up the fragment in your OnCreate() method other ways it will most likely throw nullPointer. So your activity where you want to change the TextView should look smth like this:
public class MainActivity extends AppCompatActivity {
private Button button1;
private FragmentManager fragmentManager;
private FragmentTransaction fragmentTransaction;
BlankFragment blankFragment = new BlankFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1 = (Button)findViewById(R.id.button1);
changeFragment();
fragmentManager = getFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment1,blankFragment);
fragmentTransaction.commit();
}
private void changeFragment(){
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
blankFragment.changeTextOfFragment("Enter here the text which you want to be displayed on your Updated Fragment");
}
});
}
Hope this helps :)
You can access with getView method of Fragment class.
For example You have a TextView in Your MyFragment with id of "text_view" In Your Activity make a Fragment of Yours:
MyFragment myFragment = new MyFragment();
And when You need a child just call getView and then find Your childView.
View view = myFragment.getView();
if (view !=null) {
view.findViewById(R.id.text_view).setText("Child Accessed :D");
}
Note: if you want the root view of your fragment, then myFragment.getView(); is simply enough.
Just put in fragment instead of putting in activity:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_new_work_order,
container, false);
TextView scoreBoardTextView = (TextView) rootView.findViewById(R.id.score);
return rootView;
}
Only doing this:
((Your_Activity) this.getActivity()).YouyActivityElements;
If your TextView placed inside Fragment that case you cannot access TextView inside your Fragment Parent Activity you can set the interface for intercommunication between Fragment and Activity and send Data when you click on TextView or anyother thing which you want to happend
You can't access Fragment element in Parent Activity, But You can pass values to your Fragment by following way.
in your onNavigationDrawerItemSelected method of MyActivity do the following
int myScore = 100;
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager
.beginTransaction()
.replace(R.id.container,
MyFragment.newInstance(myScore)).commit();
}
And in MyFragment class create a method called newInstance like following
private static final String SCORE = "score";
public static MyFragment newInstance(int score) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putInt(SCORE, score);
fragment.setArguments(args);
return fragment;
}
And in MyFragment's onCreateView() method
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
TextView textView = (TextView) rootView
.findViewById(R.id.score);
textView.setText(Integer.toString(getArguments().getInt(
SCORE)));
return rootView;
}
That's All, I hope this will help you. If not please let me know.
The score textView is in the layout of fragment, it's not in the layout of the MyActivity, i.e. R.layout.activity_home. So you could find the score textview in that fragment once you inflate the corresponding layout file.
It returns null cause the TextView is an element of the Fragment, not the Activity.
Please note that the idea of using Fragment is to encapsulate a module inside the Fragment, which means the Activity should not have direct access to it's properties. Consider moving your logic where you get the TextView reference inside the Fragment
Simply declare TextView as public in fragment, initialize it by findViewById() in fragment's onCreateView(). Now by using the Fragment Object which you added in activity you can access TextView.
You need to call method findViewById from your fragment view.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
scoreBoardTextView = (TextView) mNavigationDrawerFragment.getView().findViewById(R.id.score);
}
This way works for me.
I suggest you to make the textview part of your activity layout. Alternately you can have the textview as a separete fragment. Have a look at my question here. Its similar to yours but in reverse direction. Here's a stripped down version of code I used in my project. The explanation are along the code.
The Activity Class
public class MainActivity extends ActionBarActivity {
PlaceFragment fragment;
TextView fragmentsTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Bundle bundle = new Bundle();
bundle.putString("score", "1000");
fragment = PlaceFragment.newInstance(bundle);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, fragment);
ft.addToBackStack(null);
ft.commit();
// method 1
// fragment is added some ways to access views
// get the reference of fragment's textview
if (fragment.getTextView() != null) {
fragmentsTextView = fragment.getTextView();
}
// method 2
// using static method dont use in production code
// PlaceFragment.textViewInFragment.setText("2000");
// method 3
// let the fragment handle update its own text this is the recommended
// way wait until fragment transaction is complete before calling
//fragment.updateText("2000");
}
}
The fragment class:
public class PlaceFragment extends Fragment {
public TextView textViewInFragment;// to access via object.field same to
// string.length
// public static TextView textViewInFragment;//to access via
// PlaceFragment.textView dont try this in production code
public PlaceFragment() {
}
public static PlaceFragment newInstance(Bundle bundle) {
PlaceFragment fragment = new PlaceFragment();
fragment.setArguments(bundle);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment_place, container, false);
textViewInFragment = (TextView) view
.findViewById(R.id.textViewInFragment);
return view;
}
#Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
if (getArguments() != null) {
textViewInFragment.setText(getArguments().getString("score"));
}
}
public TextView getTextView() {
if (textViewInFragment != null) {
return textViewInFragment;// returns instance of inflated textview
}
return null;// return null and check null
}
public void updateText(String text) {
textViewInFragment.setText(text);// this is recommended way to alter
// view property of fragment in
// activity
}
}
Communication from activity to fragment is straight forward. This is because activity contains fragment. Keep the fragment object and access its property via setters and getters or the public fields inside it. But communication from fragment to activity requires an interface.
why you don't access it directly from your FragmentPagerAdapter,
SubAccountFragment subAccountFragment = (SubAccountFragment) mSectionsPagerAdapter.getItem(1);
subAccountFragment.requestConnectPressed(view);
and here is the full example:
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.Locale;
public class TabsActivity extends ActionBarActivity implements ActionBar.TabListener {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {#link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {#link android.support.v4.app.FragmentStatePagerAdapter}.
*/
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_tabs);
// Set up the action bar.
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
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.Tab tab = actionBar.newTab();
View tabView = this.getLayoutInflater().inflate(R.layout.activity_tab, null);
ImageView icon = (ImageView) tabView.findViewById(R.id.tab_icon);
icon.setImageDrawable(getResources().getDrawable(mSectionsPagerAdapter.getPageIcon(i)));
TextView title = (TextView) tabView.findViewById(R.id.tab_title);
title.setText(mSectionsPagerAdapter.getPageTitle(i));
tab.setCustomView(tabView);
tab.setTabListener(this);
actionBar.addTab(tab);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_tabs, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_logout) {
finish();
gotoLogin();
return true;
}
return super.onOptionsItemSelected(item);
}
#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 ProfileFragment profileFragment;
public SubAccountFragment subAccountFragment;
public ChatFragment chatFragment;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
profileFragment = new ProfileFragment();
subAccountFragment = new SubAccountFragment();
chatFragment = new ChatFragment();
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return profileFragment;
case 1:
return subAccountFragment;
case 2:
return chatFragment;
}
return null;
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#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;
}
public int getPageIcon(int position) {
switch (position) {
case 0:
return R.drawable.tab_icon_0;
case 1:
return R.drawable.tab_icon_1;
case 2:
return R.drawable.tab_icon_2;
}
return 0;
}
}
public void gotoLogin() {
Intent intent = new Intent(this, LoginActivity.class);
this.startActivity(intent);
}
public void requestConnectPressed(View view){
SubAccountFragment subAccountFragment = (SubAccountFragment) mSectionsPagerAdapter.getItem(1);
subAccountFragment.requestConnectPressed(view);
}
}
If the view is already inflated (e.g. visible) on the screen then you can just use findViewById(R.id.yourTextView) within the activity as normal and it will return the handle to the text view or null if the view was not found.
I just use methods to access fragment views from parent activity, because we create a new fragment class object to insert the fragment. So I do like this.
class BrowserFragment : Fragment(), Serializable {
private lateinit var webView: NestedScrollWebView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
webView = view.findViewById(R.id.web_view)
}
fun getWebView(): WebView {
return webView
}
}
In MainActivity
val browserFragment = BrowserFragment()
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.browser_fragment_placeholder, browserFragment)
fragmentTransaction.commit()
val webView = browserFragment.getWebView()
Related
I have a tablayout (with 3 tabs) with viewpager and fragments.
I m trying to send the parsed Json data from MainActivity( When searchview data submitted ) to show in the textview of tabs fragments
See this Image link
The data is succesfully parsing but textview with data(in first tab) is not showing unless scrolled to 3rd tab
//Passing data from MainActivity
public String getMyData() {
return meaning;
}
//Setting value to textview from Fragment
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v=inflater.inflate(R.layout.fragment_meaning, container, false);
MainActivity mainActivity= (MainActivity) getActivity();
assert mainActivity != null;
String data= mainActivity.getMyData();
TextView textView=v.findViewById(R.id.textVIew);
textView.setText(data);
return v;
}
Want to able to show data changes instantly as it is parsed, instead of scrolling to 3rd tab to see changes
Here are some steps that might help you.
On the ViewPager adaptor you have created, make the fragment objects. like below
FragmentOne fragOne; // this should be global
On the viewPager adaptor, do some thing like this,
fragOne = new FragmentOne() // whatever your implementation is.
Then after fetching the data from the server,
if ( fragOne != null ) {
fragOne.setValueOnView( " your data to be passed" );
}
and on the FragmentOne, create a function called setValueOnView
void setValueOnView(String yourString) {
v.findViewById(R.id.textVIew).setText(yourString);
}
And one more thing, while initializing the fragment onCreateView, create an object of View
View v; // global variable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragment_meaning, container, false);
Use this approach for other fragments as well
Inside getItem() method in ViewPager class use Fragment constructors with String parameter
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
FragmentOne tab1 = new FragmentOne("string parameter");
return tab1;
case 1:
FragmentTwo tab2 = new FragmentTwo("string parameter");
return tab2;
case 2:
FragmentThree tab3 = new FragmentThree("string parameter");
return tab3;
default:
return null;
}
}
Inside your Fragment:
public FragmentOne(String stringParameter) {
yourLocalVariable = stringParameter; // yourLocalVariable is declared inside Fragment class;
//now you can setText() for your TextView inside onViewCreated()
}
Of course you pass your String from MainActivity to ViewPager like you did earlier.
Use Observer
public class FragmentObserver extends Observable {
#Override
public void notifyObservers() {
setChanged(); // Set the changed flag to true, otherwise observers won't be notified.
super.notifyObservers();
}
}
Activity:
public class MyActivity extends Activity {
private MyAdapter mPagerAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.my_activity);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new MyAdapter();
pager.setAdapter(mPagerAdapter);
}
private void updateFragments() {
mPagerAdapter.updateFragments();
}
}
Viewpager adapter
public class MyAdapter extends FragmentPagerAdapter {
private Observable mObservers = new FragmentObserver();
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
mObservers.deleteObservers(); // Clear existing observers.
Fragment fragment = new MyFragment();
if(fragment instanceof Observer)
mObservers.addObserver((Observer) fragment);
return fragment;
}
public void updateFragments() {
mObservers.notifyObservers();
}
}
Your Fragment
public class MyFragment extends Fragment implements Observer {
/* Fragment related stuff... */
#Override
public void update(Observable observable, Object data) {
View root = getView();
// Update your views here.
}
}
You will get data to update method even your fragment already loaded
I'm trying to make an Android app with a tabbed form. One tab for Autonomous, and the other for TeleOp.
The TeleOp tab needs to be able to read data from the Autonomous tab, but I'm having trouble passing data from one to the other, while I'm switching from the first tab to the next.
They're both fragments, with one parent, called the Match Form. I'm not entirely sure what to do, so here is my code:
MatchForm.java
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
public TabLayout tabLayout;
public static String startingPos;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_match_form);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
tabLayout = (TabLayout) findViewById(R.id.tabs);
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_match_form, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
public PlaceholderFragment() {
}
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_match_form, container, false);
TextView textView = (TextView) rootView.findViewById(R.id.section_label);
textView.setText(getString(R.string.section_format, getArguments().getInt(ARG_SECTION_NUMBER)));
return rootView;
}
}
/**
* 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) {
switch(position){
case 0:
AutonomousFragment autonomousFragment = new AutonomousFragment();
return autonomousFragment;
case 1:
TeleopFragment teleopFragment = new TeleopFragment();
return teleopFragment;
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
public void easyToast(String text){
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
AutonomousFragment.java
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
// Make sure that we are currently visible
if (this.isVisible()) {
// If we are becoming invisible, then...
if (!isVisibleToUser) {
sendData();
}
}
}
public void sendData(){
FragmentTransaction ft = getFragmentManager().beginTransaction();
TeleopFragment teleopFragment = new TeleopFragment();
ft.add(R.id.container, teleopFragment);
final Bundle args = new Bundle();
args.putString("startingPos", startingPos);
args.putString("switchPos", switchPos);
args.putString("scalePos", scalePos);
args.putString("autoRun", autoRun);
args.putString("allianceColor", selectedAllianceColor);
teleopFragment.setArguments(args);
ft.commit();
}
TeleopFragment.java
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.teleop_fragment, container, false);
final Bundle bundle = getArguments();
button = (Button)view.findViewById(R.id.submitButton);
if(bundle != null && bundle.containsKey("startingPos")){
startingPos = bundle.getString("startingPos");
easyToast(startingPos);
}
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
easyToast(startingPos);
}
});
return view;
}
There are many ways to pass the data.One easy and efficient way to implement.create a public class in your package.In that class declare your values as static.
public class MyDataClass {
public static String value1;
public static String value2;
}
Now you can access these values from anywhere either in the fragment or activity.
you can pass the values like this
MyDataClass myobj=new MyDataClass();
myobj.value1="Hello";
To fetch the value in another class use
String val=myobj.value1;
You can pass the data from Autonomous Fragment to the parent activity first and then pass it to the Teleop Fragment.
You can use Intents for this.
How to pass values between Fragments
Or use a custom listener to notify the other fragment once the data is sent.
1. Do you really need ViewPager here?
ViewPager is needed if you want to display multiple fragments at the same time. On my opinion, the fragments in ViewPager must be equal and independent. If you want to keep communication between Fragments in ViewPager you can:
Use EventBus or LocalBroadcastManager, etc.;
Cache Fragment inside ViewPager in this way
2. Maybe you need flow?
If you want implement some fragment flow, for example "PickGoods" -> "GoodsCheckout", it is better to use fragment transactions and pass arguments with Bundle. For example, pass selected goods ids from "PickGoods" to "GoodsCheckout".
Note. You can't pass really big amount of data. But it is enough for large set of ids.
3. One more solution.
If your flow belongs to separate activity, which is going to be killed, after final action in flow (it is important to avoid memory leaks) you can use ViewModel attached to activity and store data in it. You can get ViewModels attached to activity from its fragments:
ViewModelProviders.of(getActivity()).get(DataViewModel.class);
This is giving me headache. Lets say my app has a ViewPager which contains 4 tabs. I want to have multiple sub-fragments within each tab. that means each tab would have more than just one view. I totally have no clue how to achieve this....
I have a FragmentActivity which gets load first and set my ViewPager
public class FragActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frag_main);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
PagerAdapter adapter = new FFNPagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
pager.setOffscreenPageLimit(3);
tabs.setViewPager(pager);
}
}
I then have my FragmentPagerAdapter
public class PagerAdapter extends FragmentPagerAdapter implements TextAndIconTabProvider {
private final String[] TITLES = { "FIRST", "SECOND", "THIRD", "FORTH" };
public PagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment frag = new Fragment();
switch (position) {
case 1:
frag = new FirstFrag();
break;
case 2:
frag = new SecondFrag();
break;
case 3:
frag = new ThirdFrag();
break;
case 4:
frag = new ForthFrag();
break;
}
return frag;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return TITLES.length;
}
}
the above would give me a working ViewPager with 4 active tabs. Then I need to do all 4 individual Fragments. I'm giving out one example below.
public class FirstFrag extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.firstfrag, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
Button btn1 = (Button) getActivity().findViewById(R.id.btn1);
btn1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
// what should I put in this block??????
Fragment fragment = new FirstFragSub1();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.pager, fragment); //<<------what should i replace???????
transaction.commitAllowingStateLoss();
}
});
}
}
The OnCreateView in FirstFragSub1 gets called but I don't see anything displayed on the screen. Basically just a blank. I know by replacing R.id.pager should not be the correct way, I'm just giving out an example of what I initial thought was.
Here is my FirstFragSub1
public class FirstFragSub1 extends ListFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.i("FirstFragSub1","onCreateView is called");
return inflater.inflate(R.layout.subone, container, false);
}
}
Can someone please help me? I really out of idea...Thanks
there is a great tutorial and github here that covers this in detail.
http://tausiq.wordpress.com/2014/06/06/android-multiple-fragments-stack-in-each-viewpager-tab/
https://github.com/tausiq/ViewPagerMultipleFragmentDemo
this covers handling the backstack and multiple tabs with multiple views per tab. hope this help I was searching for a similar solution when i read your question.
Have FrameLayouts in the view that is being inflated by the Fragment. And onCreateView, replace the FrameLayout by the sub fragments that you want.
Also, be careful not to use the Activity's FragmentManager. getFragmentManager returns the Activity's FragmentManager and that will lead to an undesirable result (Only one fragment inside your ViewPager would have the sub fragments loaded). Use the getChildFragmentManager function instead.
I have 2 fragments (tabs) that share some data. When one changes the data, I'd like to have that reflected on the other tab. I researched this on stackOverflow and I think the relevant answer has to do with a .notifyDataSetChanged() call, but I can't make it work. Here's the relevant code...
public class EnterCourseData extends FragmentActivity implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private ActionBar actionBar;
// Tab titles
private String[] tabs = { "Pars", "Handicaps" };
private int courseNumber, teeNumber;
private Tee tee;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_enter_tees);
// Initilization
Intent mIntent = getIntent();
courseNumber = mIntent.getIntExtra("courseNumber",0);
Course course = Global.getCourse(courseNumber);
teeNumber = mIntent.getIntExtra("teeNumber",0);
tee = course.getTee(teeNumber);
viewPager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
mAdapter = new TabsPagerAdapter(getSupportFragmentManager(), courseNumber, teeNumber);
viewPager.setAdapter(mAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Adding Tabs
for (String tab_name : tabs) {
actionBar.addTab(actionBar.newTab().setText(tab_name)
.setTabListener(this));
}
/**
* on swiping the viewpager make respective tab selected
* */
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
and further down, here is the onClick method that necessitates the refresh...
public void savePars(View view){
tee.setSlope(Integer.parseInt(((EditText) findViewById(R.id.enter_tee_slope)).getText().toString()));
tee.setRating(Double.parseDouble(((EditText) findViewById(R.id.enter_tee_rating)).getText().toString()));
mAdapter.notifyDataSetChanged();
}
Here is the TabsPagerAdapter...
public class TabsPagerAdapter extends FragmentPagerAdapter {
int courseNumber, teeNumber;
public TabsPagerAdapter(FragmentManager fm, int courseNumber, int teeNumber) {
super(fm);
this.courseNumber = courseNumber;
this.teeNumber = teeNumber;
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
// Par Entry activity
Fragment parFragment = new ParFragment();
Bundle args = new Bundle();
args.putInt(ParFragment.ARG_COURSE_NUMBER, courseNumber);
args.putInt(ParFragment.ARG_TEE_NUMBER, teeNumber);
parFragment.setArguments(args);
return parFragment;
case 1:
// Handicap Entry fragment activity
Fragment hcpFragment = new HandicapFragment();
args = new Bundle();
args.putInt(HandicapFragment.ARG_COURSE_NUMBER, courseNumber);
args.putInt(HandicapFragment.ARG_TEE_NUMBER, teeNumber);
hcpFragment.setArguments(args);
return hcpFragment;
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 2;
}
}
Here is one Fragment...
public class ParFragment extends Fragment {
public static final String ARG_COURSE_NUMBER = "courseNumber", ARG_TEE_NUMBER = "teeNumber";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_par, container, false);
Bundle args = getArguments();
Course course = Global.getCourse(args.getInt(ARG_COURSE_NUMBER));
((TextView) rootView.findViewById(R.id.display_course_name)).setText(course.getName());
Tee tee = course.getTee(args.getInt(ARG_TEE_NUMBER));
((TextView) rootView.findViewById(R.id.display_tee_name)).setText(tee.getTeeName());
((TextView) rootView.findViewById(R.id.enter_tee_slope)).setText(Integer.toString(tee.getSlope()));
((TextView) rootView.findViewById(R.id.enter_tee_rating)).setText(Double.toString(tee.getRating()));
return rootView;
}
}
And here is the other...
public class HandicapFragment extends Fragment {
public static final String ARG_COURSE_NUMBER = "courseNumber", ARG_TEE_NUMBER = "teeNumber";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_handicap, container, false);
Bundle args = getArguments();
Course course = Global.getCourse(args.getInt(ARG_COURSE_NUMBER));
((TextView) rootView.findViewById(R.id.display_course_name)).setText(course.getName());
Tee tee = course.getTee(args.getInt(ARG_TEE_NUMBER));
((TextView) rootView.findViewById(R.id.display_tee_name)).setText(tee.getTeeName());
((TextView) rootView.findViewById(R.id.enter_tee_slope)).setText(Integer.toString(tee.getSlope()));
((TextView) rootView.findViewById(R.id.enter_tee_rating)).setText(Double.toString(tee.getRating()));
return rootView;
}
}
When the button is clicked, I want to save the values and I want these values to show up on the other fragment.
Help a noob out.
Thanks
You need to communicate between fragments, but a fragment cannot directly communicate with other fragment, all the communication should be done through the activity which holds these fragments.
The steps to follow are :
Define an Interface in the fragment where you have implemented the onClickListener (let it be Fragment A)
Implement the Interface in the activity which holds these fragments
In the method overridden, retrieve the fragment instance from the viewpager adapter and deliver a message to Fragment B by calling it's public methods.
refer this answer to retrieve fragment instance from adapter
For more details about Communicating with Other Fragments, refer here
So there is a trick: just let the fragments have the object reference of one another and call the other's function to load data when you handle the onClickListener of the button.
E.g:
protected void onClickListener(View view) {
if (view == myButton) {
// Do other stuffs here
fragment1.reloadData();
}
}
P/S : I re-post this as answer to have the code formatter.
Problem
A Fragment is not reattached to its hosting ViewPager after returning from another fragment.
Situation
One Activity hosting a Fragment whose layout holds a ViewPager (PageListFragment in the example below). The ViewPager is populated by a FragmentStateViewPagerAdapter. The single Fragments hosted inside the pager (PageFragment in the example below) can open sub page lists, containing a new set of pages.
Behaviour
All works fine as long as the back button is not pressed. As soon as the user closes one of the sub PageLists the previous List is recreated, but without the Page that was displayed previously. Swiping through the other pages on the parent PageList still works.
Code
A sample application can be found on github:
Activity
public class MainActivity extends FragmentActivity {
private static final String CURRENT_FRAGMENT = MainActivity.class.getCanonicalName() + ".CURRENT_FRAGMENT";
public static final String ARG_PARENTS = "Parents";
public void goInto(String mHostingLevel, String mPosition) {
Fragment hostingFragment = newHostingFragment(mHostingLevel, mPosition);
addFragment(hostingFragment);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addBaseFragment();
}
private void addBaseFragment() {
Fragment hostingFragment = newHostingFragment("", "");
addFragment(hostingFragment);
}
private Fragment newHostingFragment(String mHostingLevel, String oldPosition) {
Fragment hostingFragment = new PageListFragment();
Bundle args = new Bundle();
args.putString(ARG_PARENTS, mHostingLevel + oldPosition +" > ");
hostingFragment.setArguments(args);
return hostingFragment;
}
private void addFragment(Fragment hostingFragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentSpace, hostingFragment, CURRENT_FRAGMENT);
transaction.addToBackStack(null);
transaction.commit();
}
}
PageListFragment
public class PageListFragment extends Fragment {
private String mParentString;
public PageListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_hosting, container, false);
}
#Override
public void onResume() {
mParentString = getArguments().getString(MainActivity.ARG_PARENTS);
ViewPager viewPager = (ViewPager) getView().findViewById(R.id.viewPager);
viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentString));
super.onResume();
}
private static class SimpleFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
private String mHostingLevel;
public SimpleFragmentStatePagerAdapter(FragmentManager fm, String hostingLevel) {
super(fm);
this.mHostingLevel = hostingLevel;
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
PageFragment pageFragment = new PageFragment();
Bundle args = new Bundle();
args.putString(MainActivity.ARG_PARENTS, mHostingLevel);
args.putInt(PageFragment.ARG_POSITION, position);
pageFragment.setArguments(args);
return pageFragment;
}
#Override
public int getCount() {
return 5;
}
}
}
PageFragment
public class PageFragment extends Fragment {
public static final String ARG_POSITION = "Position";
private String mHostingLevel;
private int mPosition;
public PageFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment_page, container, false);
setupTextView(contentView);
setupButton(contentView);
return contentView;
}
private void setupTextView(View contentView) {
mPosition = getArguments().getInt(ARG_POSITION);
mHostingLevel = getArguments().getString(MainActivity.ARG_PARENTS);
TextView text = (TextView) contentView.findViewById(R.id.textView);
text.setText("Parent Fragments " + mHostingLevel + " \n\nCurrent Fragment "+ mPosition);
}
private void setupButton(View contentView) {
Button button = (Button) contentView.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
openNewLevel();
}
});
}
protected void openNewLevel() {
MainActivity activity = (MainActivity) getActivity();
activity.goInto(mHostingLevel, Integer.toString(mPosition));
}
}
After a lengthy investigation it turns out to be a problem with the fragment manager.
When using a construct like the one above the fragment transaction to reattach the fragment to the page list is silently discarded. It is basically the same problem that causes a
java.lang.IllegalStateException: Recursive entry to executePendingTransactions
when trying to alter the fragments inside the FragmentPager.
The same solution, as for problems with this error, is also applicable here. When constructing the FragmentStatePagerAdapter supply the correct child fragment manager.
Instead of
viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentString));
do
viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getChildFragmentManager(),mParentString));
See also: github
What Paul has failed to mention is, if you use getChildFragmentManager, then you will suffer the "blank screen on back pressed" issue.
The hierarchy in my case was:
MainActivity->MainFragment->TabLayout+ViewPager->AccountsFragment+SavingsFragment+InvestmentsFragment etc.
The problem I had was that I couldn't use childFragmentManagerfor the reason that a click on the item Account view (who resides inside one of the Fragments of the ViewPager) needed to replace MainFragment i.e. the entire screen.
Using MainFragments host Fragment i.e. passing getFragmentManager() enabled the replacing, BUT when popping the back-stack, I ended up with this screen:
This was apparent also by looking at the layout inspector where the ViewPager is empty.
Apparently looking at the restored Fragments you would notice that their View is restored but will not match the hierarchy of the popped state. In order to make the minimum impact and not force a re-creation of the Fragments I re-wrote FragmentStatePagerAdapter with the following changes:
I copied the entire code of FragmentStatePagerAdapter and changed
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
...
}
with
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.detach(f);
mCurTransaction.attach(f);
return f;
}
}
...
}
This way I am effectively making sure that that the restored Fragments are re-attached to the ViewPager.
Delete all page fragments, enabling them to be re-added later
The page fragments are not attached when you return to the viewpager screen as the FragmentStatePagerAdapter is not re-connecting them. As a work-around, delete all the fragments in the viewpager after popbackstack() is called, which will allow them to be re-added by your initial code.
[This example is written in Kotlin]
//Clear all fragments from the adapter before they are re-added.
for (i: Int in 0 until adapter.count) {
val item = childFragmentManager.findFragmentByTag("f$i")
if (item != null) {
adapter.destroyItem(container!!, i, item)
}
}