i wrote a FragmentActivity with a ViewPager, i did not want to use the actionbar for the tabs (because i did not want the tabs to be positioned over the menu) and also not the PagerTabStrip solution, because i could not customize it as i wanted.
so i wrote an own linearlayout as tabs container:
<LinearLayout
android:id="#+id/main_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:orientation="horizontal" >
</LinearLayout>
with an View.OnClickListener for each of each tab
OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(final View view) {
mapTabs.get(lastSelectedTab).setSelected(false);
view.setSelected(true);
Runnable runnable = new Runnable() {
#Override
public void run() {
int index = Integer.parseInt(view.getTag().toString());
FragmentActivity.setTab(index);
lastSelectedTab = index;
}
};
handler.post(runnable);
}
};
}
public static void setTab(int index) {
FragmentActivity.viewPager.setCurrentItem(index, true); // true or false does not make any difference
}
this is the method i wrote to add the single tabs to the tab container (main_tabs):
private void addTab(LayoutInflater inflater, ViewGroup tabs, int imageId, View.OnClickListener onClickListener) {
inflater.inflate(R.layout.main_tab, tabs);
int index = tabs.getChildCount() - 1;
ImageView ivTab = (ImageView) tabs.getChildAt(index);
ivTab.setTag(String.valueOf(index)); // stored the tab index
ivTab.setImageResource(imageId);
ivTab.setOnClickListener(onClickListener);
mapTabs.put(index, ivTab); // add an imageview to the tab
}
i used the runnalbe so that i see the highlighting of the tab immediately, and not after the slight delay when the new page appears. i also stored the last selected tab in order to deselect it when the new one gets selected.
i store all tabs in a map in order to access them later for selection.
this is the OnPageChangeListener i wrote (to highlight the associated tab):
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
mapTabs.get(position).setSelected(true); // selected the tag asociated with the current visible page
mapTabs.get(lastSelectedTab).setSelected(false); // deselectes the last selected tab
lastSelectedTab = position;
ListFragment<?> item = FragmentActivity.fragmentAdapter.getItem(position);
setTitle(item.getTitle());
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
everything works fine, i am just not clear about the performance:
when i scroll to the next page, everything goes very fast and smooth. the next page appears immediately without any dealy; only the menu in the action bar (created in the onCreateOptionsMenu(Menu menu, MenuInflater inflater) method of each Fragment) appears with a slight delay (about 500-700 ms).
but when i click on one of the tabs, the selected page appears not immediately but with a slight delay (actually the same time the menu needs to appear as mentioned above: menu and page appear together at the same time). bzw. this happens also when i use the tabstrip solution.
i guess if its possible to achieve a delay-less page change by scrolling, it should be possible somehow to achieve the same performance for the tab clicks.
but what do i do wrong?
and is there a possibility to make the menu appear immediately just like the pages when scrolling? can the different menus not be cached just like the pages?
the solution was extremely simple. i just had to keep all my 4 tabs in the ViewPager so that they don't need to get recreated (and therefore don't cause the short delay after each tab-clicki). i just had to add following lines to the onCreate method.
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setOffscreenPageLimit(4);
to avoid delay between tabs click in view pager should set the limit of tabs like
pager.setOffscreenPageLimit(4);
try this
myViewPager.animate().translationX(0f).setDuration(1000);
where 1000 is delay in milliseconds
Related
I have 3 fragments which is settled in TabLayout with ViewPager.
The Scenario is:
First fragment has one ImageView with some EditText and RadioButton with NEXT Button
Second fragment has 5 EditText with NEXT Button
Third Fragment has 4 ImageView and 4 EditText with SignUp Button
all fields are required.
Now what I have done:
Checked all validation on Button click of Fragment 1 and moved to NEXT fragment.
if (getActivity() != null) {
((RegisterActivity) getActivity()).mViewPager.setCurrentItem(1, true);
}
Same process
Same process and proceed for SignUp.
Problem:
How can I check values When I move to second fragment without clicking button (Sliding Viewpager or Clicking on TAB)
How can I update all final values (when I change it but do not click on button and move forward)
I have tried to use onAttach and onDetach for saving values but didn't worked.
Any solution for manage all data and check validation in each situation?
You can track of current selected tab by TabLayout.ViewPagerOnTabSelectedListener,
take a look here for more information docs
In general just implement this interface and then you will be notified when user swipe tabs or if you programmatically swipe you will still get notified.
Not the best solution but the workaround I have recently implemented.
Take boolean[] of total fragments.
like:
private boolean[] arrLastPageDone = new boolean[]{false, false, false};
If all values of fragment1 validated, set true on first position. Same for other fragments.
Simply check values in onPageSelected.
like:
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if (position > 0 && !arrLastPageDone[position - 1]) {
pager.setCurrentItem(pager.getCurrentItem() - 1);
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
And it works. It dosen't let swipe to next fragment until all values validated.
And you can save those data to SingleTon Class until all process finished.
I have a working code for having 3 tabs, with 3 fragments that you can either horizontally swipe between to change, or you can click on the tab to go to the requested fragment.
However, I wanted to do some tweaks to my design, and I went over to the Android Developer's website: Creating swipe views with tabs
, and it looks completely different than mine. Now I wonder if I should use the code in the developer's website, or keep my own.
Right now I am using ViewPager + TabLayout.
So simple, yet working.
This also makes me wonder, how come this simple code works, but in the official documentation there's a long story. It just makes me think that if I keep my code in the current design, I might encounter some big problems in the future.
Or - if it works - then it works?
MainActivity.java:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Find the view pager that will allow the user to swipe between fragments
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
// Create an adapter that knows which fragment should be shown on each page
TabsAdapter adapter = new TabsAdapter(this, getSupportFragmentManager());
// Set the adapter onto the view pager
viewPager.setAdapter(adapter);
// Find the tab layout that shows the tabs
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
// Connect the tab layout with the view pager. This will
// 1. Update the tab layout when the view pager is swiped
// 2. Update the view pager when a tab is selected
// 3. Set the tab layout's tab names with the view pager's adapter's titles
// by calling onPageTitle()
tabLayout.setupWithViewPager(viewPager);
}
}
TabsAdapter.java:
public class TabsAdapter extends FragmentPagerAdapter {
/** Context of the app */
private Context mContext;
public TabsAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
}
/**
* Return the {#link Fragment} that should be displayed for the given page number.
*/
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new NbFragment();
} else if (position == 1) {
return new LksFragment();
} else {
return new ChtFragment();
}
}
/**
* Return the total number of pages.
*/
#Override
public int getCount() {
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return mContext.getString(R.string.category_nb);
} else if (position == 1) {
return mContext.getString(R.string.category_lks);
} else {
return mContext.getString(R.string.category_cht);
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/primary_color"
android:orientation="vertical"
tools:context="com.example.barebones.barebones.MainActivity">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
style="#style/CategoryTab"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
And you can see it is completely different than the one on their website. How could it be? I would love to get some clarification, because as a beginner, I feel lost in this sea of information. Which method should I use in order to have swiping tabs that represent different fragments?
I'm lost what you're trying to solve here. Are you perhaps trying to make it so that when you swipe horizontally, respectively changing fragments, your tab layout is updated as well? If so. I have a snippet down below, which you may have to adjust based on your variable names.
protected void initializeNavigationTransition() {
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i1) {
}
#Override
public void onPageSelected(int i) {
tabs_bottom_nav.getTabAt(i).select();
}
#Override
public void onPageScrollStateChanged(int i) {
}
});
tabs_bottom_nav.addOnTabSelectedListener(new TabLayout.BaseOnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
updateTabIconOnSelect(current_tab, tab.getPosition());
current_tab = tab.getPosition();
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
I'll have an additional note that it is possible for fragments to refresh, meaning it will be reinitialized, which might be something you do not want, especially if you're displaying data unto these fragments, which you are fetching from your API or over the network, after swiping a couple of times to different fragments if you have probably at least 4 fragments within the view pager. Hence you would need to prevent the view pager from refreshing the fragments by using the code below
viewPager.setOffscreenPageLimit(4); // 4 is the number of fragments within your view pager
viewPager.setAdapter(sectionsPagerAdapter);
I believe the cause of fragments refreshing is caused by the viewpager trying to save memory
I have Nested Fragments [1 main Fragment called "MainFrag" and many child Fragments]
the App can show MainFrag or can show other fragments.
what I want to do is to setCurrentItem of MainFrag's ViewPager every time the user selects to show that MainFrag so I tried to put it at it's onCreateView but this only works for the first time the User Shows MainFrag as if user Selects another fragment and then re-selects MainFrag the seCurrentItem is not working
I also tried to put it inside MainFrag's onResume, It worked as required but it also causes a problem that if the user moved the App to background and then reOpen it viewPager will Scroll and i don't want this
So where Exactly Can I put setCurrentItem ?
mViewPager.setCurrentItem(mCurrentWeekFragmentItemNumber);
Edit: I am using Navigation Menu to Move between 3 main fragments where MainFrag is one of them.
After your mViewPager.setAdapter(adapter); method you can put
mViewPager.setCurrentItem(mCurrentWeekFragmentItemNumber);
After setting adapter to viewpager add setCurrentItem method
mViewPager.setAdapter(adapter);
mViewPager.setCurrentItem(mCurrentWeekFragmentItemNumber);
Try this:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
viewPager.setCurrentItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
I am using a ViewPager, which has two pages and it is being reused. The total count what i have is 156 pages. I want to disable left swipe for page 2.
Please help me out, thanks,
Here is my code..
public class SlidePagerActivity extends FragmentActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setOnPageChangeListener(new ViewPageChangeListener(){
// set current slide index
AbcController.getInstance().getSlideController().setCurrentSlide(_nNextSlideIndex);
// set Current Cycle Index
AbcController.getInstance().getSlideController().setCurrentCycle(_nNextSlideCycle);
// set pager index
_mPager_in.setCurrentItem(_nNextSlideIndex);
});
}
}
I wanted to to prevent the user to go swipe back to previous page, but didn't want the page neither to disappear (to allow him to swipe back if some condition are fulfilled).
I finally use OnPageChangeListener:
viewPager.addOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if(position < pageActuelle) {
viewPager.setCurrentItem( pageActuelle );
return;
}
pageActuelle = position;
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
Now I am forcing the user to go back to the his current page when he is swiping left. This solution was really adapted to my problem since I could display a Toast when the user tries to swipe back.
Hope it could help someone.
Can you specify your problem more detailed?
You can always remove all pages before the specific postion, in that case there is nothing to swipe too. (sorry cant comment)
My app has a ListView on startup. The user can either manually select an item in the ListView to go to a details screen or swipe using a ViewPager between the different details screens. The ViewPager's fragments are setup like this:
Listing
Detail 1
Detail 2
Detail 3
Detail 4
...
It's my understanding, when the Listing fragment is loaded, the ViewPager will execute Detail 1's code, for performance. The same when Detail 1 is loaded, Detail 2's code will execute.
The problem I'm running into is that I'm setting the title of each detail fragment in onActivityCreated, however, when the Listing fragment is loaded, it is displaying Detail 1's title. So I moved the code to onPageSelected of the ViewPager, which works if the user is swiping, but if the user manually selects an item in the ListView the title is never set.
I'm not sure if there is an event that is only fired when a user manually selects an item in the ListView and not when they are swiping or if I need to rethink my apps' setup. For example, instead of using this code in the Listing fragment's onListItemClick event:
final Intent listing = new Intent(getActivity().getApplicationContext(), Details.class);
startActivity(listing);
I need to somehow use the ViewPager.
mViewPager = (ViewPager)findViewById(R.id.viewpager);
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mMyFragmentPagerAdapter);
mViewPager.setSaveEnabled(false);
mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
String title = GetTitle(position);
getSupportActionBar().setTitle(title);
}
#Override
public void onPageScrolled(int position, float offset, int offsetPixel) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
You'll probably want to override the method getPageTitle(int) in your MyFragmentPagerAdapter class. The documentation states:
This method may be called by the ViewPager to obtain a title string to
describe the specified page. This method may return null indicating no
title for this page. The default implementation returns null.
So rather than returning null, make sure you return the actual page title. You get passed in the position/index of the page the title is requested for, so a simple switch-case statement should suffice. Alternatively, you could set up an interface for your pages and query the relevant page for its title.