In my application I have two tabs, for which I am using ViewPager, two fragments are within this view pager. Sometimes when I tap on first tabs fragments EditBox keyboard pops, once I am done with the input keyboard disappears. But layout below keyboard region goes blank (White), this is happening sometimes, specially second launch of application. When application is launched from studio for first time issue doesn't appear. But when I launch it through system application by tapping on its ic_launhcher issue comes again. What are the possible causes of issue ?
The layout on launch looks like :
After I click on edit box & edit some text in address filed, when keyboard disappears, layout looks like :
I am using view pager inside another fragment & using FragmentStatePagerAdapter by passing a getChildFragmentManager()
private void setupViewPager(ViewPager viewPager) {
FragmentStatePagerAdapter fragmentStatePagerAdapter = new FragmentStatePagerAdapter(getChildFragmentManager()) {
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new GeneralInfoFragment();
Bundle bundle = new Bundle();
bundle.putString(context.getString(R.string.all_data), new Gson().toJson(profileResponse));
fragment.setArguments(bundle);
break;
case 1:
fragment = new ICEFragment();
break;
default:
return null;
}
return fragment;
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "General Info";
case 1:
return "ICE";
}
return "";
}
};
Note : Images are erased to protect privacy
Edit:
Put this code in your manifest file in your activity tag
android:configChanges="keyboard|keyboardHidden|orientation|
screenLayout|uiMode|screenSize|smallestScreenSize"
android:windowSoftInputMode="stateHidden|adjustPan"
Or
Try this one
setContentView(R.layout.activity_direction_3);
getWindow().getDecorView().setBackgroundColor(
android.R.color.transparent);
Or
if you have are using a custom theme then add this in your theme
<item name="android:windowBackground">Transparent color </item>
Tel me if you still get the issue.
Related
I have trouble finding a good example on how to swipe between fragments with help of bottom navigatiom bar. Since FragmentStatePagerAdapter is deprecated and a new ViewPager2 is now recommended instead of the old ViewPager I want to use ViewPager2 and FragmentStateAdapter in my code instead. I have found an example of how to combine BottomNavigationBar and ViewPager here and I want to do something similar. My code have many similarities to the one in the example with the only difference that I have my code in a fragment instead of an activity. Here is a picture of how my FrontendFragment display look like. I can switch between the views using the bottomnavigationbar but I also want to be able to swipe between the views. Can someone help me or at least direct me on the right way? Here is my code:
FragmentPagerAdapter Class:
public class FragmentPagerAdapter extends FragmentStateAdapter {
private static final int mFragmentCount = 5;
public FragmentPagerAdapter(#NonNull Fragment fragment) {
super(fragment);
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position){
case 0:
return new HomeFragment();
case 1:
return new SearchFragment();
case 2:
return new AddFragment();
case 3:
return new MessageFragment();
case 4:
return new ProfileFragment();
}
return null;
}
#Override
public int getItemCount() {
return mFragmentCount;
}
}
FrontendFragment Class:
public class FrontendFragment extends Fragment implements BottomNavigationView.OnNavigationItemSelectedListener{
private BottomNavigationView mBottomNavigationView;
private ViewPager2 mViewPager2;
private FragmentPagerAdapter mFragmentPagerAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_frontend, container, false);
loadFragment(new HomeFragment());
mBottomNavigationView = v.findViewById(R.id.bottomNavigationBar);
mBottomNavigationView.setOnNavigationItemSelectedListener(this);
return v;
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.home_icon:
selectedFragment = new HomeFragment();
break;
case R.id.search_icon:
selectedFragment = new SearchFragment();
break;
case R.id.add_icon:
selectedFragment = new AddFragment();
break;
case R.id.message_icon:
selectedFragment = new MessageFragment();
break;
case R.id.profile_icon:
selectedFragment = new ProfileFragment();
break;
}
return loadFragment(selectedFragment);
}
private boolean loadFragment(Fragment selectedFragment) {
if(selectedFragment != null){
MainActivity.sFm.beginTransaction().replace(R.id.relLayoutMiddle, selectedFragment).commit();
return true;
}
return false;
}
}
Thanks in advance!
As I'm already using BottomNav with ViewPager2 in one of my app, I can help.
Your code is partially correct which means your FragmentPagerAdapter is fine, but not your FrontEndFragment.
See, the FragmentPagerAdapter has to be set to a ViewPager2 as
//this here is the FrontEndFragment
mViewPager2.setAdapter(new FragmentPagerAdapter(this));
Then, You don't have to do the FragmentTransaction at all, you just have to change the ViewPager2's current item position through the BottomNavigationBar as
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.home_icon:
mViewPager2.setCurrentItem(0);
break;
case R.id.search_icon:
mViewPager2.setCurrentItem(1);
break;
case R.id.add_icon:
mViewPager2.setCurrentItem(2);
break;
case R.id.message_icon:
mViewPager2.setCurrentItem(3);
break;
case R.id.profile_icon:
mViewPager2.setCurrentItem(4);
break;
}
return false;
}
This is all, you don't have to deal with Fragments at all apart from the FragmentPagerAdapter. Also, don't forget to remove the loadFragment(new HomeFragment()); which is not required, nor the function loadFragment() is required.
(Optional), Furthermore, if you want to disable the Swipe Action of the ViewPager2 and want the Fragments to be selected based on the Selected BottomNav item only, then you can set setUserInputEnabled() property of ViewPager2 as false.
Next, to set the BottomNavigationBar's item as selected based on the swipe of the ViewPager2, what you've to do is,
Create a global var
MenuItem previousMenuItem;
Then, set a default item (first) to be selected of BottomNav on activity start as
mBottomNavigationView.getMenu().getItem(0).setChecked(true);
Finally, set an OnPageSelected() callback on ViewPager2 to update the selected Menu Item as:
mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if (previousMenuItem != null) {
previousMenuItem.setChecked(false);
}
else {
mBottomNavigationView.getMenu().getItem(0).setChecked(false);
}
mBottomNavigationView.getMenu().getItem(position).setChecked(true);
previousMenuItem = mBottomNavigationView.getMenu().getItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
What you're doing here is that you're setting default item to previousMenuItem and then on swiping to a different page, deselecting the previousMenuItem and selecting the new one, which means updating the BottomNav based on ViewPager's current item. This is the complete code you require to acheive your objective.
If you want to swipe between views, a simple solution would be to store all views in your parent view, but set all layouts for views except the initial view to android:visibility="gone". Make sure to set the initial view layout to android:visibility="visible" though. Now on you button clicks, you will have to implement onClick such that they turn on/ off view visibilities accordingly. For example, store views in order and control them via array index. But the whole thing you're trying to do is generally not a good design pattern in my opinion.
Why don't you load another Activity onClick instead of crowding your single activity? This will cause load time issues. Even if the views are non-visible, it's just an overall hassle to maintain all that in one place.
Background
I have an android app using fragments. I'm using a separate FragmentPagerAdapter (i.e. as a separate class, outside MainActivity).
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.MainActivity);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.setupWithViewPager(mViewPager);
}
SectionsPagerAdapter.java
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new frag1();
case 1:
return new frag2();
case 2:
return new frag3();
case 3:
return new frag4();
case 4:
return new frag5();
default:
return null;
}
}
#Override
public int getCount() {
return 5;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Name1";
case 1:
return "Name2";
case 2:
return "Name3";
case 3:
return "Name4";
case 4:
return "Name5";
}
return null;
}
The Issue
I need to refresh the view of, say, fragment #3 from fragment#2 (the ultimate goal is for changes the user initiates on fragment #2 to be visible as soon as the user proceeds to fragment#3. I have tried to detach/attach fragment #3 itself as soon as it is visible, like this:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
getFragmentManager().beginTransaction().detach(this).attach(this).commit();
}
Unfortunately, a) this has a penalty in performance, as there is a "stutter" during navigation; b) it doesn't work reliably. Some things (the texts of some buttons) are not refreshed, for some reason I can't figure out.
In any case, I have discovered that the only 100% reliable method for forcing a refresh/redraw is to restart the activity (for example through an onClickListener on a Save button the user needs to press on the previous fragment, #2). But this restarts the Activity on fragment #1
My Question
How can I restart the activity on this specific fragment (#3), considering the fact I'm using a separate FragmentPagerAdapter?
I've studied several other questions/answers, for example this: https://stackoverflow.com/a/36064344
but how can I implement something like this (putting extras on the intent call) when the FragmentPagerAdapter is on a separate class? Alternatively, is there something else I can do to refresh the fragment view?
If you need extra info or code, let me know and I'd be glad to add clarification.
EDIT:
I managed to find another solution that so far seems to be working as intended. I added the following code on the onClickListener of the Save button on fragment #2
frag3 f3 = new frag3();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragmentContainer, f3);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
However, I'm still interested in my original question (how to start an activity on a specific fragment when using a separate FragmentPagerAdapter), so if you have any ideas about that, I'm all ears.
Well, wasn't that simple...
this.recreate(); not only restarts the activity but it also remembers the fragment position.
Just in case someone else needs this, when you're working from fragments you need to use it this way:
First declare the activity which contains the fragment in question. E.g.
public class frag1 extends Fragment {
...
Activity mactivity;
#Override
public void onAttach(Activity MainActivity) {
super.onAttach(MainActivity);
mactivity = MainActivity;
}
Then instead of getActivity().recreate(); simply use mactivity.recreate();
I get the next error: java.lang.IllegalStateException: Fragment already added.
This is a layout getting inflated each time I change tab, as seen in this photo:
![FirstTab]http://prntscr.com/h4e8pc
![SecondTab]http://prntscr.com/h4ebyn
So i get the idea of the problem, but I've tried many ways to fix it with no result. I've tried to use .replace instead of .add, that way it doesnt crash but the map is not loaded. And also tried to delete the fragment as soon as I switch to that tab but the result is similar to the .replace one.
case R.id.navigation_dashboard:
frame = (FrameLayout) findViewById(R.id.content);
frame.removeAllViewsInLayout();
LayoutInflater.from(getApplicationContext())
.inflate(R.layout.activity_prueba_dashboard, frame, true);
fragmentTransaction =
getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.content2, mMapFragment);
fragmentTransaction.commit();
mMapFragment.getMapAsync(my_maps_class);
I believe it's much cleaner to use viewpager in that situation. Check this code :
public class HomeViewPagerAdapter extends FragmentPagerAdapter {
public static final int FRAGMENT_A_INDEX = 0;
public static final int FRAGMENT_B_INDEX = 1;
public static final int FRAGMENT_C_INDEX = 2;
public static final int FRAGMENTS_COUNT = 3;
public HomeViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case FRAGMENT_A_INDEX:
return FragmentA.newInstance();
case FRAGMENT_B_INDEX:
return FragmentB.newInstance();
case FRAGMENT_C_INDEX:
return FragmentC.newInstance();
default:
return null;
}
}
#Override
public int getCount() {
return FRAGMENTS_COUNT;
}
}
and then link the adapter with the ViewPager in the hosting appcompat activity as here :
HomeViewPagerAdapter homeTabsAdapter = new HomeViewPagerAdapter(getSupportFragmentManager());
homeTabsViewPager.setAdapter(homeTabsAdapter);
and you can easily switch between fragments by calling :
homeTabsViewPager.setCurrentItem(HomeViewPagerAdapter.FRAGMENT_A_INDEX, false);
You seen to be trying replacing the content of the same Fragment, in this case you dont need the FragmentTransaction.
The solution is to create a second fragment on which you have the map, in this case the Transaction is needed.
I am using view pager with 3 tabs in MainActivity of my app to show grid images. I have one MovieFragment for all 3 tabs and I am using FragmentsPagerAdapter to create new instance of MovieFragment everytime tab is changed.
//FragmentsPagerAdapter method
#Override
public Fragment getItem(int position) {
switch (position){
case 0: return MoviesFragment.newInstance(buildUrl(Utility.POPULAR_URL));
case 1: return MoviesFragment.newInstance(buildUrl(Utility.RATED_URL));
case 2: return MoviesFragment.newInstance("favorite");
default: return null;
}
}
//Fragment method
public static MoviesFragment newInstance(String uri){
MoviesFragment fragment = new MoviesFragment();
Bundle bundle = new Bundle();
bundle.putString("fragment", uri);
fragment.setArguments(bundle);
return fragment;
}
Now I was getting different results in my app so I debugged this method and I found it calls methods before tab is selected i.e when I open app it creates instances for case 0 and case 1 and when I go to second tab it creates instance for case 2.
Now in my second tab when I make changes to database and try to get them in third tab changes are not reflected as it already created instance of third tab when second tab was selected.
How do I solve this problem?
#Sahil This is because in android ViewPager keeps track of previous and next pages only. So when you initialize the viewPager it automatically create first 2 page and call MoviesFragment for 1st two tab. And once you start scrolling through, it starts keeping the track of only previous and next page.
Read this
I'm trying to create a Navigation Menu + Tabs, the code for this is fine.
Now I've implemented some code I had as an Activity in a Fragment in order to add it to the Tabs viewer.
When I run the code the app works just fine..
Until I swipe right to the other Tab and then the App crashed and I get this from the logcat:
java.lang.NullPointerException: Attempt to write to field 'android.support.v4.app.FragmentManagerImpl android.support.v4.app.Fragment.mFragmentManager' on a null object reference
I'm leaving you my Main Activity, 'cause I think there's the issue, if you think you might need some Fragments like the Tabs, please just ask!
CODE: http://pastebin.com/TTcJMR77
You have to add default case on your switch in getItem(int position) method:
default:
return new Fragment();
Instead of
return null;
Like this:
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new YourFragment();
default:
return new Fragment();
}
}