I'm trying to use Fragments in android.
So in the method "getItem()" into the class "TabAdapter", I have:
public Fragment getItem(int position) {
switch (position) {
case 0:
Browse tab1 = new Browse();
return tab1;
case 1:
Create tab2 = new Create();
return tab2;
default:
return null;
}
}
So, in class "Browse", I use inflate to put into TabLayout fragment, the layout that I want to use:
public class Browse extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_browse, container, false);
return rootView;
}
And in this class, a inner class that has the method "onCreate()"
public class BrowseInner extends AppCompatActivity{
DBManager db = new DBManager(this);
DBManager.DatabaseHelper dbh = new DBManager.DatabaseHelper(this);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("TRYTOSTAMP");
setContentView(R.layout.activity_browse);
Cursor cursor = dbh.giveAllItemFromDB();
... //"and all the logics to interact with layout"...
}
But, in this way, I have only the layout in the Fragment TabLayout, as it is defined in .xml design, but I can't interact whit it (query not processed, so fields in the layout not fill, an event on button not called and so on...)
Where am I doing wrong?
Thanks in advance.
If I am correct you want to put the data that you get in BrowseInner into the fields of the two Fragments?
In general you are doing everything wrong.
You have no meaningful names like BrowseInnerActivity, BrowseFragment and CreateFragment
You can return like this. No need to assign.
public Fragment getItem(int position) {
switch (position) {
case 0:
return new Browse();
case 1:
return new Create();
default:
return null;
}
}
Why are you trying to inflate the same layout both in the Activity and the Fragment? Even if the views should be the same you need to create separate layouts and name them accordingly. You can not inflate in Fragment: R.layout.activity_browse
And the above is basic programming. Your design is not correct. The Activities and Fragments are "Views" in terms of MVP, MVVM and etc. They need to be passive. Only present some data and notify something, for example the ViewModel about some actions executed on them.
So in your case the Activity can only create the Fragments. You can create a ViewModel, which is shared between the two Fragments. The ViewModel can hold a reference to some Repository and update LiveData objects in itself. Then Fragments can observe the LiveData and do with the data what they need to do. There are pretty good design guidelines:
https://developer.android.com/jetpack/docs/guide
And here are some sample apps:
https://github.com/android/architecture-samples
And in general you should understand what is MVVM,ViewModel, LiveData and etc.
Related
I'm creating an android quiz application in which I'll be using a viewpager. I've gone through some tutorials in which for each new page a new class is created for fragment/tabs. Now if I want to have total 15 pages, then will I have to create that much classes? Isn't there any other alternative?
Each page in the ViewPager will be a different instance of a Fragment, but there's no requirement that each page be a different class of Fragment.
In an excessively simple application, you could imagine that each page in the ViewPager would just show a different number. You could achieve this by creating a OneFragment class to show "1", a TwoFragment class to show "2", and so on. Or, you could create a single NumberFragment class and use fifteen different instances to show fifteen different numbers:
public class NumberFragment extends Fragment {
public static NumberFragment newInstance(int number) {
Bundle args = new Bundle();
args.putInt("number", number);
NumberFragment fragment = new NumberFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.number_fragment, container, false);
int number = getArguments().getInt("number");
String numberText = String.valueOf(number);
TextView numberView = root.findViewById(R.id.numberView);
numberView.setText(numberText);
return root;
}
}
With this in place, your FragmentPagerAdapter is incredibly simple:
public class MyAdapter extends FragmentPagerAdapter {
#Override
public Fragment getItem(int position) {
return NumberFragment.newInstance(position);
}
#Override
public int getCount() {
return 15;
}
}
This same concept applies even in more complex applications. As long as each page of the ViewPager has the same structure to its data, you can just create a new instance of a single Fragment class by passing the necessary configuration to newInstance().
You can also mix-and-match this technique with the one where you create a new Fragment subclass for each page. Maybe you want your first and your last pages to be unique, but the thirteen middle pages are all basically the same.
You should create a Fragment for each different page you want to show in the ViewPager. Then you could select each Fragment by the position in the FragmentPagerAdapter.
I am trying to write a test app which contains an activity. There are two fragments inside this activity, which are defined as LeftFragment and RightFragment. I used getFragmentManager().findFragmentById() to get connection from each other fragments. By using that methode I am able to get an LeftFragment object from RightFragment, but not RightFragment object from LeftFragment. It just works only oneway. I am doing this, because I want to call some operations from other fragment, that return some values. I was thinking about using EventBus but I failed too. How can I achive that?
Here is my LeftFragment
public class LeftFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.rightFragment);
if (rightFragment != null){
makeToast(rightFragment.getMessageFromRight());
}else {
makeToast("does not found rightFragment");
}
return inflater.inflate(R.layout.fragment_left, container, false);
}
public String getMessageFromLeft(){
return "Hi! Im left";
}
private void makeToast(String text){
Toast.makeText(getContext(),text,Toast.LENGTH_SHORT).show();
}
}
And here is my RightFragment
public class RightFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
LeftFragment leftFragment = (LeftFragment) getFragmentManager().findFragmentById(R.id.leftFragment);
if (leftFragment != null){
makeToast(leftFragment.getMessageFromLeft());
}else {
makeToast("does not found leftFragment");
}
return inflater.inflate(R.layout.fragment_right, container, false);
}
public String getMessageFromRight(){
return "Hi! Im right!";
}
private void makeToast(String text){
Toast.makeText(getContext(),text,Toast.LENGTH_SHORT).show();
}
}
There are many ways to communicate between 2 fragments . If 2 fragments loaded at the same time. I usaually use one of 2 ways below to do it.
You can use this link using obserable pattern to communication 2 fragments.
you can use EventBus lib for communication, it 's very simple
Your issue:
By using that methode I am able to get an LeftFragment object from
RightFragment, but not RightFragment object from LeftFragment
I think your problem is LeftFragment is intitialized previous, so you can find it from RightFragment. Your solution is ok, using EventBus. YOu need to review your codes to find the issue. You can test by creating other methods, after 2 fragment was initialized.
For ex: click button in LeftFragment, toast a message in RightFragment.
Probably what is happening is that the Left Fragment is getting the OnCreateView() call first, at which point the Right Fragment has not been inflated yet (therefore it can't be "found" by findFragmentbyId()).
I would suggest moving the code that gets the references to the other fragments into onStart(), and only inflate the fragments in onCreateView().
Basicly I have very same code as described here :
In each of the fragments I have different asynctask, that just fetches data from website.
It looks like this :
TextView text;
String content;
View today;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
today = inflater.inflate(R.layout.today, container, false);
text = (TextView) today.findViewById(R.id.textView);
// ((TextView) today.findViewById(R.id.textView)).setText("Today");
new RetriveSiteData().execute("http://menza.lupajz.eu/?den=dnes");
content = (String) text.getText();
return today;
}
I am wondering if the data is always fetched, while swiping through fragments if the TabPagerAdapter class (on the website)
has this methode implemented the same way
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
//Fragement for Android Tab
return new Android();
case 1:
//Fragment for Ios Tab
return new Ios();
case 2:
//Fragment for Windows Tab
return new Windows();
}
return null;
}
In case if the data is always fetched and the asynctask is always executed how can I prevent that ? Maybe adding some onResume() methods to each fragment ?
Basically you have two options:
Execute the async tasks only once in the activity that contains the view pager, and then pass the data to your fragment. You can do it with some lazy loading as well.
Use the ViewPager.setOffscreenPageLimit(int) function to set the number of fragment to be retained in the view pager. Default value is set to 1, and thus the getItem() method is called every time you switch. I don't recommend going with this option though, especially when you have complex and memory consuming views.
Firstly, I know these subjects have been created a lot of time on stackoverflow, but I don't have found the solution to my problems. Secondly, I'm french, so my english is not perfect, sorry per advance and tell me if you don't understand something. And to finish this introduction, it's the first time that I'm dealing with fragments, so, sorry if there is something that I don't have well understand !
I have three buttons, that allow to switch between three fragments.
Inside one of these fragments, I have a view pager with two fragments. For the moment, each fragments (there are 5), only contains a TextView.
I'm using the latest version of android-support-v4 (I have read a lot of subject in stackoverflow that say that the latest version of support solve the "Recursive entry to executePendingTransactions" error that I have).
My two problems :
When I click two times in one button, I have an IllegaleStateException "can't change tag of fragment". I was able to fix that by creating a new fragment on onButtonSelected method, but I don't want to recreate fragment each time, for memory reasons and for functional reasons : fragment have to keep her state. This problem is not my main problem, indeed, i know that to disable the button when user is already on fragment is possible, but it's strange to have an exception when this management is not done, no ?.
When I go out from the fragment with the view pager, and I go back to this fragment, I have an IllegalStateException "Recursive entry to executePendingTransactions". I can fix this by setting my adapter on an handler, or use FragmentStatePagerAdapter instead of FragmentPageAdapter (see Display fragment viewpager within a fragment), but even if my application don't crash, when I go back to my fragment with the view pager, the view pager has disapear !
Can you help me ?
Java source code is bellow, layout source code is, I think, useless.
MainActivity :
public class MainActivity extends SherlockFragmentActivity{
private SherlockFragment fragmentOne, fragmentTwo, fragmentThree;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// this.fragmentOne = new fragmentOne();
// this.fragmentTwo = new fragmentTwo();
// this.fragmentThree = new fragmentThree();
// Call new or instanciate ? What's the correct way ?
this.fragmentOne = (SherlockFragment) SherlockFragment.instantiate(this, FragmentOne.class.getName());
this.fragmentTwo = (SherlockFragment) SherlockFragment.instantiate(this, FragmentTwo.class.getName());
this.fragmentThree = (SherlockFragment) SherlockFragment.instantiate(this, FragmentThree.class.getName());
// Add fragment
FragmentTransaction transaction = (
this.getSupportFragmentManager().beginTransaction()
);
transaction.add(
R.id.tab_fragment,
this.fragmentOne,
this.fragmentOne.toString()
);
transaction.commit();
}
public void onButtonSelected(View v){
switch (v.getId()){
case R.id.button_one_tab:{
showFragment(this.fragmentThree);
break;
}
case R.id.button_two_tab:{
showFragment(this.fragmentOne);
break;
}
case R.id.button_three_tab:{
showFragment(this.fragmentTwo);
break;
}
default:{
break;
}
}
}
public void showFragment(SherlockFragment fragmentToShow){
FragmentTransaction transaction = (
this.getSupportFragmentManager().beginTransaction()
);
transaction.replace(R.id.tab_fragment, fragmentToShow, fragmentToShow.toString());
transaction.commit();
}
}
Fragment two and three only inflate a layout that only contains a TextView.
Fragment one (note that i'm using a DirectionalViewPager - a lib - instead of a ViewPager):
public class FragmentOne extends SherlockFragment{
private FragmentOneAdapter fragmentOneAdapter;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Set up the pager
final DirectionalViewPager pager = (DirectionalViewPager)getActivity().findViewById(R.id.pager);
if (this.fragmentOneAdapter== null){
this.fragmentOneAdapter= new FragmentOneAdapter (getActivity().getSupportFragmentManager());
}
pager.setAdapter(fragmentOneAdapter);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_page, container, false);
}
FragmentOneAdapter :
public class FragmentOneAdapter extends FragmentPagerAdapter{
private ArrayList<SherlockFragment> fragmentsList;
public FragmentOneAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
SherlockFragment fragmentFour = new FragmentFour();
SherlockFragment fragmentFive = new FragmentFive();
this.fragmentsList = new ArrayList<SherlockFragment>();
this.fragmentsList.add(fragmentFour);
this.fragmentsList.add(fragmentFive);
}
#Override
public Fragment getItem(int position) {
return this.fragmentsList.get(position);
}
#Override
public int getCount() {
return this.fragmentsList.size();
}
}
Thanks per advance for your help !
I have solved my problem !
I have simply replace getSupportFragmentManager() on FragmentOne to getChildFragmentManager(). Then, I have edited my onButtonSelected's method by creating a new instance of fragment each time instead of using my three different instances (if I don't do that, I have an exception : java.lang.IllegalStateException: Activity has been destroyed).
I have still a problem with this solution : I lose the state of each fragment each time I'm switching between fragmentOne, fragmentTwo and fragmentThree. Do you have a solution for that ?
Is it possible to use one fragment in a viewpager multiple times? I am trying to build a dynamically updated UI using ViewPager.
I want to use the same design, basically the same fragment with different data for every page, like a listview adapter.
You can instantiate the same Fragment class for every page in your ViewPager, passing the position of the ViewPager to control what to display. Something like that:
public class MyFragment extends Fragment {
private int mIndex;
public MyFragment(int index) {
mIndex = index;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
switch(mIndex){
case 0:
// do you things..
case 1:
// etcetera
}
}
}
then, in you FragmentPagerAdapter:
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return new MyFragment(position);
}
}
That way you can reuse most of your code changing only what you need in the switch/case statement.
You misunderstood the concept of class versus object of class. Your MyFragment.java source code defines class which you turn into "living thing" each time you instantiate it with new operator (new MyFragment();) - this creates object which is an instance of your class. Unless you intentionally prevent this (by i.e. using Singleton pattern) your can make as many instances of the class as you like, same way you are allowed to make as many i.e. cakes using single recipe. And this applies to fragments as well.
So as long as you create separate object (aka said instance) of your class for each page, you should be able to do what you want.