i'm new to Android, (not programming, or even Java) so bear with me.
I'm trying to get a handle on the use of fragments.
I've got a project that I've created using the default swipe/actionbar. I've extended this further to handle the settings i want.... however i don't quite understand what's going on/how to fix this.
/**
* 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 int getCount() {
// Show 8 total pages.
return 8;
}
#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);
case 3:
return getString(R.string.title_section4).toUpperCase(l);
case 4:
return getString(R.string.title_section5).toUpperCase(l);
case 5:
return getString(R.string.title_section6).toUpperCase(l);
case 6:
return getString(R.string.title_section7).toUpperCase(l);
case 7:
return getString(R.string.title_section8).toUpperCase(l);
}
return null;
}
}
/**
* A dummy fragment representing a section of the app, but that simply
* displays dummy text.
*/
public class DummySectionFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
public String ARG_SECTION_NUMBER = "section_number";
public DummySectionFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
int position;
position = getArguments().getInt(ARG_SECTION_NUMBER)-1;
View rootView;
TextView dummyTextView;
I don't really want anything static or final here, and I've got it mostly worked out but I don't understand the following line or how to fix it. I kinda get what it's doing.
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
The error is: cannot make a static reference to the non-static field DummySectionFragment.ARG_SECTION_NUMBER
There is probably a simple fix for this, i just am unfamiliar enough with Android and Java, as my current job i spend all my time in SQL Server.
-- EDITED ADDITIONS
i'm not opposed to anything static or final etc. the problem i'm not quite understanding is when i want to DO something in each of those fragments. I have a textview on each of those layouts and i want to be able to manipulate them say in a loop. I think i'm stuck in a circle and can't figure my way out... lol.
For example below the code I put above is
case 4:
rootView = inflater.inflate(R.layout.fragment_main_location,container, false);
dummyTextView= (TextView) rootView .findViewById(R.id.section_label);
// location
Button btnShowLocation = (Button) rootView.findViewById(R.id.btnShowLocation);
Button btnShowDBLocList = (Button) rootView.findViewById(R.id.btnShowDBLocList);
Button btnLocationsCount = (Button) rootView.findViewById(R.id.btnLocationsCount);
Button btnTruncateDBLocationsTable = (Button) rootView.findViewById(R.id.btnTruncateDBLocationsTable);
btnTruncateDBLocationsTable.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Activity activity = getActivity();
int intCount = 0;
/*if (activity != null) {
//dummyTextView.setText("");
try {
locationDatabaseHandler.truncateLocationTable();
intCount = locationDatabaseHandler.getLocationCount();
} catch (Exception e){
//dummyTextView.append(e.toString());
}
//dummyTextView.append("Count:" + intCount + "\n\n");
Toast.makeText(activity, "toast_you_just_clicked_a_fragment btnTruncateDBLocationsTable button", Toast.LENGTH_LONG).show();
}*/
}
});
dummyTextView = (TextView) rootView .findViewById(R.id.section_label);
dummyTextView.append("\nLocation Stuff\n");
break;
//dummyTextView.append("Count:" + intCount + "\n\n");
I run into a circle where if I dummyTextView try to use the dummmyText w/in the onClick event, it says that i need to make it static (quick fix) with a complaining error of : cannot refer to a non-final variable dummy7Text inside an indder class defined in a different method.
I've added a variables to handle this inside the onCreate that get filled for (LayoutInflater and Viewgroup, and then reference them w/in the onclick (not shown), but when i go in and instansiate ... nothing happens with the textviews...
There is something i'm not quite getting here, and once i get by that hurdle, i'll have this by the balls, and will be able to make it do what i want.
I don't really want anything static or final here
Why? They will not negatively impact performance, nor are they a sign of poor coding practices.
I don't understand the following line
Every Fragment can be created with a Bundle containing any number of key-value pairs. DummySectionFragment.ARG_SECTION_NUMBER is a the key (a String), and position + 1 is the value. Thus this code is telling the new DummySectionFragment which section of content the Fragment should show.
This method is preferable to putting these arguments in a constructor because your custom constructor for a Fragment isn't guaranteed to be called. There are many ways for Android to generate Fragments, so this lowers the possibility of problems such as NullPointerExceptions.
the error is: cannot make a static reference to the non-static field DummySectionFragment.ARG_SECTION_NUMBER
As you seem to know, DummySectionFragment.ARG_SECTION_NUMBER is referring to a static field within the DummySectionFragment class called ARG_SECTION_NUMBER. By making this field non-static, you can no longer reference this constant value without a DummySectionFragment instance.
Another option (if you really don't want a static field) would be to hardcode the String. Thus your code would be:
args.putInt("section_number", position + 1);
However, a public static field is a much better coding practice and will prevent silly mistakes with typos in your Strings.
I run into a cirle where if i dummyTextView try to use the dummmyText w/in the onClick event, it says that i need to make it static (quick fix) with a complaining error of : cannot refer to a non-final variable dummy7Text inside an indder class defined in a different method.
Instead of using an anonymous inner class, I would let your Fragment implement OnClickListener.
For example:
public class MyFragment extends Fragment implements OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
Button btnTruncateDBLocationsTable = (Button) rootView.findViewById(R.id.btnTruncateDBLocationsTable);
btnTruncateDBLocationsTable.setOnClickListener(this);
// ...
}
#Override
public void onClick(View v) {
// You can reference dummyTextView here without any problems
}
}
that means that ARG_SECTION_NUMBER should be declared as public static. Better if it declared as public static final
Related
I have a Fragment inside the mainActivity, the fragment contains fragmentcontainerView which can be replaced by multiple child fragments with spinner onselectedListener. I want to able to pass those values from the child fragment via eg: Do something with: fragmentevent.TogetFName(); with a button in Mainactivity. In the parent fragment , I get the value from the child fragment(fragment_Birthday) with fragment_fr_event_birthday = (fragment_fr_event_Birthday) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment); and other value from other childfragment with frag_fr_event_wed = (fragment_fr_event_wedding) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment);, I know that they cannot be assigned with the different fragment class at once, but is there a clever way to do this or is there any other way I can pass value from child -> parent fragment->mainActivity
MainActivity:
public void onClick(View view){
case "Event":
Fragment_fr_Event fragment_fr_event = (Fragment_fr_Event) getSupportFragmentManager().findFragmentById(R.id.fragment_generated_mainView);
if(fragment_fr_event.TogetWedChildFcoupleName() !=null && fragment_fr_event.TogetEventType().equals("Wedding")){
testThis.setText(fragment_fr_event.TogetWedChildFcoupleName());
}if( fragment_fr_event.TogetEventType().equals("Birthday") && fragment_fr_event.TogetBirthdayFName() !=null){
testTat.setText(fragment_fr_event.TogetBirthdayFName());
}
}
ChildFragment(BirthdayFragment):
public String TogetEventBirthdayFName (){
EditText FBirthdayName = rootView.findViewById(R.id.Edittext_birthDay_FirstName);
return FBirthdayName.getText().toString();
}
ChildFragment(Wedding fragment):
public String toGetFcoupleName(){
EditText FCoupleName = rootView.findViewById(R.id.textView_wedding_Name);
return FCoupleName.getText().toString();
}
ParentFragment(EventFragment):
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Spinner TypeEventSpinner = rootview.findViewById(R.id.type_event);
TypeEventSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String tag_items = parent.getItemAtPosition(position).toString();
switch (tag_items){
case "Wedding":
frag_fr_event_wed = new fragment_fr_event_wedding();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_event_child_fragment, frag_fr_event_wed).disallowAddToBackStack().commit();
break;
case "Birthday":
fragment_fr_event_birthday = new fragment_fr_event_Birthday();
transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_event_child_fragment , fragment_fr_event_birthday).disallowAddToBackStack().commit();
break;
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
public String TogetWedChildFcoupleName(){
if(frag_fr_event_wed !=null){
frag_fr_event_wed = (fragment_fr_event_wedding) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment);
return frag_fr_event_wed.toGetFcoupleName();
}return "Empty";
}
public String TogetBirthdayFName(){
if(fragment_fr_event_birthday != null){
fragment_fr_event_birthday = (fragment_fr_event_Birthday) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment);
return fragment_fr_event_birthday.TogetEventBirthdayFName();
}
return "Empty";
}
To be honest , I couldn't understand what you did there , but i got what you want , you want to communicate with parent's parent class , the way you are doing it made it so complicated even it's not readable , BUT of course there are always a good way to do something , in your case there are Android Navigation Component , which give you the simplicity and power to do make it much more easy to handle , You can put all your fragment in one graph and from within the destinations "fragment are called destinations here" you can communicate with other fragment and the parent using actions and global actions "going from one fragment to another is called action here" parameters, but there are no need to a parent's parent here , all destinations and its parent can share one ViewModel which will allow you to share data all around your app .
You can read more if it sound good to you here
I have a couple of fragments in the viewpager, I set some values in the first fragment I have to update the very next fragment with the values from the first one, so I store the values created in the first fragment in the database and I'm trying to update the next view with the values from database, I'm using Room for database, right now what I'm trying to follow an approach described in this answer How to determine when fragment becomes visible in viewpager and what I'm doing is I'm creating a new thread to access the database and update the global variable bodyScore from the database. here is some code
public class MindFragment extends Fragment implements FragmentLifecycle {
SeekBar mindSeekBar;
private View rootView;
ViewPager viewPager;
private int seekBarProgress;
private Date date = new Date();
private static final String TAG = MindFragment.class.getSimpleName();
private LifeWheelView lview;
private volatile int bodyScore;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.activity_fill, container, false);
viewPager = getActivity().findViewById(R.id.viewpager);
lview = rootView.findViewById(R.id.lifeWheelView);
lview.setMind(bodyScore, 0);
}
#Override
public void onResumeFragment() {
Log.i(TAG, "onResumeFragment()");
Thread thread = new Thread(new Runnable() {
ScoreDao mScoreDao;
CategoryDao mCategoryDao;
#Override
public void run() {
LifewheelRoomDatabase db = LifewheelRoomDatabase.getDatabase(getContext());
mScoreDao = db.scoreDao();
mCategoryDao = db.categoryDao();
CategoryName categoryName = mCategoryDao.getCategoryByName("Body");
Integer category_id = categoryName.getId();
int bodyScoreExceptionFlag = 0;
while ( bodyScoreExceptionFlag == 0 ) {
try {
Score st = mScoreDao.getScoreByCategory(HelperMethods.getStartOfDay(), HelperMethods.getEndOfDay(), category_id);
if( st == null) {
bodyScore = 0;
}
else {
bodyScore = st.score;
}
bodyScoreExceptionFlag++;
} catch (NullPointerException e) {
continue;
}
}
}
});
thread.start();
lview.setMind(bodyScore, 0);
}
}
On the line lview.setMind(bodyScore,0); I'm getting a NullPointerException.
So my question is:
if the Fragment has been created before it's been viewed lview variable should be initialized. and when it becomes visible why when I'm trying to call a method on it why am I getting this exception.
How should the things like this be handled when updating the view ( not list or recycler view ) from the database in the view pager where the subsequent views are updated with the input from the last view.
If you have not done so yet, please look at sharing data between fragment using the view model.
Google has provided a simple way of Inter-Fragment Communication using Shared ViewModel.
The ViewModel and LiveData are components that came out of Google IO '17 but they became really mature and the standard as of Google IO '18, the architecture components are a lot more mature now.
It is a simple way to get an app up and running with minimal code.
Here is a github example of how to do exactly that. While there is a bug in that code, it should provide a good starting point.
I am basically playing an animation on each fragment of the view pager. The animation plays when the user slides to the specific fragment. However, certain fragments don't play the animation the second time I visit them. That's because the view pager keeps them in memory.
I need to destroy each fragment after the user slides to another fragment. This way, the animations play every time I revisit those fragments.
Main View:
pager = (ViewPager) findViewById(R.id.guidepager);
mAdapter = new NewUserGuideAdapter(getSupportFragmentManager());
pager.setAdapter(mAdapter);
pager.setOffscreenPageLimit(0); //Tried this too. Didnt work
Fragment:
public class NewUserPage_Two extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_new_user_page__two, container, false);
//Play animation, etc
Animation animation_1 = AnimationUtils.loadAnimation(NewUserPage_Two.this.getActivity(), R.anim.abc_slide_in_bottom);
person1.setAnimation(animation_1);
return rootView;
}
Adapter:
public class NewUserGuideAdapter extends FragmentPagerAdapter {
public NewUserGuideAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new NewUserPage_One();
case 1:
return new NewUserPage_Two();
case 2:
return new NewUserPage_Three();
case 3:
return new NewUserPage_One();
case 4:
return new NewUserPage_One();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 5;
}
}
How can I amend my code guys?
ViewPager provide a method mViewPager.setOffscreenPageLimit(0);
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
Try this inside the fragment:
((BaseAdapter) *YourContainer*.getAdapter()).notifyDataSetChanged();
You can refer to: Refresh Current Fragment (ListView Data) remaining in the same activity
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
That's the method body,DEFAULT_OFFSCREEN_PAGES=1by the way.I think google add this limits cause you need at least the 2 views between current item while you are sliding.
You can try using addOnPageChangeListener() and start your animation on onPageSelected.
I understand that Fragments can receive information from the parent Activity through methods like onAttach() and implementations of Listeners, but those approaches involve passing information exclusively at the conception of each Fragment. I'm wondering how to change information in a Fragment from an Activity while the Fragment is running; for instance, in my case, I have a Navigation Drawer with check items as my parent navigation Activity. The checks correspond to which items of an ArrayList should be displayed in the Fragment.
How can I make my Fragment's ArrayList immediately responsive to changes in the parent Activity without recreating the Fragment?
#Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, final int childPosition, long id) {
checkBox = (CheckBox) v.findViewById(R.id.show_child_subject_checkBox);
checkBox.toggle();
tempSI = SubjectInfo.findById(SubjectInfo.class,
childItem.get(groupPosition).get(childPosition).getId());
onSubjectCheck(tempSI);
tempSI.subjectChecked = !tempSI.subjectChecked;
tempSI.save();
childItem.get(groupPosition).set(childPosition, tempSI);
myAdapter.notifyDataSetChanged();
Toast.makeText(getApplicationContext(), "Toggled " +groupPosition + "|" + childPosition,
Toast.LENGTH_SHORT).show();
// the oncheckedchangedListener now resides and operates out of adapter
// in a much more efficient, clean manner
return true;
}
onSubjectCheck is the implementation of the Listener in the Activity. Perhaps this is not the best way to do this, but I figured I'd show how I toyed around with the idea, at least.
#Override
public void onSubjectCheck(SubjectInfo si) {
for (int go = 0; go < subjectList.size(); go++) {
if (subjectList.get(go).equals(si)) {
si = new SubjectInfo(si.subjectName, si.itemHeaderTitle, si.subjectGrade,
si.subjectArchived, !si.subjectChecked);
amFragment.subjectList.set(go, si);
}
}
amFragment.sorterAndFilter(false);
}
There are many ways you can pass information from a parent Activity to a fragment.
Using setArguments on Fragment.
Fragment mFragment = new MyFragment();
Bundle args = new Bundle();
args.put**(KEY,value);
mFragment.setArguments(args);
Using fragment instance directly.
Inside Activity,
mFragment = getFragmentManager().findFragmentById(id) or findFragmentByTag(tag);
mFragment.methodCall();
I have a problem with the FragmentPagerAdapter .
I can not save the state of the Fragment and then there is the view that within the Fragment . Whenever I use the swipe left and right , the Fragment is recreated by overriding the method getItem ( int position ) in the static class that extends the FragmentPagerAdapter .
public static class GraphicsCollectionPagerAdapter extends FragmentPagerAdapter {
final int NUM_ITEMS = 3; // number of tabs
public GraphicsCollectionPagerAdapter(FragmentManager fm) {
super(fm);
fragmentList = new AnalyzeFragmentPageListWithDate();
fragment1 = new AnalyzeFragmentPage1();
fragment2 = new AnalyzeFragmentPage2();
}
#Override
public Fragment getItem(int position) {
//Log.i(TAG, "getItem() -> New fragment at position " + position);
switch (position) {
case 0:
return fragmentList;
case 1:
return fragment1;
case 2:
return fragment2;
}
return null;
}
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Fragm1";
case 1:
return "Fragm2";
case 2:
return "Fragm3";
}
return "OBJECT " + (position + 1);
}
}
Within the method OnCreateView of each instance of the Fragment there are several steps to the SQLite database and this causes a saturation of the Database Connection Pool.
The warning found whenever change dynamically fragment is: "W / SQLiteConnectionPool (1111 ) : A SQLiteConnection object for database ' / data / data / com.myapp / databases / mydb ' was leaked ! Please fix your application to end transactions in progress properly and to close the database When it is no longer needed . "
I already tried to use the FragmentStatePagerAdapter without success.
Could you kindly tell me how to proceed ? I do not want the Fragment is regenerated each time, causing problems to the database. Have you got an example for save Fragment/View sate?
I have not found any suggestion for now .
thank you very much
use setOffscreenPageLimit property for your ViewPager object.
ViewPager pager = (ViewPager) findViewById(R.id.viewPagerId);
pager.setOffscreenPageLimit(2);
First of all create a new instance of Fragment every time the getItem() is called
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FragmentList();
case 1:
return new AnalyzeFragmentPage1();
case 2:
return new AnalyzeFragmentPage2();
}
return null;
}
Note that FragmentPagerAdapter does not work exactly like normal Adapter meaning it does NOT use an empty vessel Fragment and then refills it with different data.
Instead it creates a new Fragment whenever it is needed. So you should pass data (if any) when the Fragment is created. Please study the example in the Android docs.
In a real time projects instead of passing current position to a new Fragment you could pass the actual ID of the entry that a Fragment should refer to.