I have a list, within a list, within a list, and so on. There's about 5 tiers.
It's easy enough to create 5 activities for each list on phones, but what if I want to support tablets as well? So I'd need to work with master detail flow.
However, I can't seem to find any tutorials or information in relations to a nested master detail flow.
Anyway, here is an illustration of what I'm describing:
In the tablet layout, I want the screen to shift 2 tiers at a time. User can advanced to the next tier by selecting a list item from the right tier. To go back to the previous tier, user can tap the back button.
Any idea how I can achieve this?
After a full day scouring the internet, I finally found a solution. To get a "Nested Master Details Flow" effect simply use a ViewPager with FragmentPageAdapter. The Master Detail Flow will look like this:
To change to a two panel mode when the user switches to landscape, in your extended FragmentPagerAdapter class, override the following method:
#Override
public float getPageWidth(int position) {
DisplayMetrics metrics = getResources().getDisplayMetrics();
// if the width is greater than 900dp halve the width of the page
if ((metrics.widthPixels / metrics.density) > 900) {
return (0.5f);
}
return super.getPageWidth(position);
}
To provide an "up button" for the view pager:
viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
// This method will be invoked when a new page becomes selected.
#Override
public void onPageSelected(int position) {
if (position == 0) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
});
You can tell the "up button" to go back a page like this (where viewpager is a member variable of your activity, holding the a reference to your ViewPager):
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int position = viewpager.getCurrentItem();
if (position > 0) viewpager.setCurrentItem(position-1);
return true;
}
REFERENCES:
ViewPager with FragmentPagerAdapter
Display back button on action bar
Multiple-View ViewPager Options
How to implement a ViewPager with different Fragments / Layouts and example github project
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.
A ViewPager preloads fragments.
But with a pageWidth=0.5 (2 fragments per screen), I think it loads too much views before the current position...
I have pageLimit=1 and pageWidth=0.5.
It should be ok to preload LEFT1 and LEFT2, but why LEFT3 ?
LEFT3 is not on the next left screen...
I tried to understand ViewPager.java from support v4:
https://android.googlesource.com/platform/frameworks/support/+/support-library-27.1.1/core-ui/src/main/java/android/support/v4/view/ViewPager.java#1155
line 1155, it says
// Fill 3x the available width or up to the number of offscreen
// pages requested to either side, whichever is larger.
But for multiple fragments per screen, it loads more before than current and after the current position.
With the Google sample project https://github.com/googlesamples/android-SlidingTabsBasic/
adding
protected void onCreate(Bundle savedInstanceState) {
[...]
mViewPager.setCurrentItem(25);
#Override
public float getPageWidth(int position) {
// 2 fragments per screen
return .5f;
}
#Override
public int getCount() {
// 50 fragments
return 50;
}
public Fragment getItem(int position) {
[...]
Log.d("debugpageviewer", "position="+position);
}
And it loads 7 fragments (3 before current position). The smaller the pageWidth, the more fragments are loaded before.
So, is this a bug or did I miss something ?
Try calling setOffscreenPageLimit() on your ViewPager in your onCreate method.
For example mViewPager.setOffscreenPageLimit(2) will make sure that no more than 3 fragments are always kept around: the current one and the two adjacent ones.
Okay, so I have a FragmentPagerAdapter with 3 pages on it...
Is there a way I can HIDE a page from the SlidingTabLayout? I don't want to destroy the page, as I want to be able to unhide it later. Is there a way to do this? Or do I have to destroy the page, and add it back in later?
Hold all your pages in an Array (or a List).
boolean isHide;
public int getCount (){
if(isHide){
return container.size() - 1;
}
return container.size();
}
public Fragment getItem(int position) {
if(isHide && position == positionToHide){
return container.get(position + 1);
}
return container.get(position);
}
One solution would be to prevent transition to that fragment by hiding the tab button and/or disabling swipe.
fragment will still be loaded but you want be able to move to it.
When transitioning between pages, each fragment page shares the same menu options. The menu option contains button which launches an activity. That activity is highly dependent on the information the current page item shows.
I noticed that when I change the view such that, the next page is almost shown on the screen (not fully transitioned) and I select the option for that item, the options shows the data from the previous item. I think this is because, he transition between pages was not fully complete. This is kind of confusing for my users. There could be people who swipe faster and press the option button. I noticed then when swiping between fragments and then suddenly press the menu option. The options shows the data from the previously active page.
If I could only hide the menu options, and only show it when the page is fully transitioned, I believe I can solve this problem. Or else, maybe I am doing something wrong which could have averted this in the first place?
I am using FragmentStatePagerAdapter.
Thank you!
Use ViewPager.SimpleOnPageChangeListener and when the onPageScrollStateChanged event is triggered check to see the state of the scroll if is idle then the page is in view and active (allow user to press the menu button), if is in another state then lock/hide the menu buttons.
I think I was able to solve my problem. I created an interface on my fragment which the hosting activity has to implement:
public interface OnOptionsMenuEnabledListener{ public boolean onOptionsMenuEnabledListener(); }
The hosting activity will just return a flag indicating if the menu is enabled.
#Override
public boolean onOptionsMenuEnabledListener()
{
return mOptionsEnabled;
}
And set the flag via ViewPager.onPageScrollStateChanged (int state):
#Override
public void onPageScrollStateChanged(int state)
{
switch(state)
{
case ViewPager.SCROLL_STATE_IDLE:
mOptionsEnabled = true;
break;
case ViewPager.SCROLL_STATE_DRAGGING:
mOptionsEnabled = false;
break;
case ViewPager.SCROLL_STATE_SETTLING:
mOptionsEnabled = true;
break;
}
}
Every time the option is being selected during screen transition, I call the interface method to communicate to me what the status of the flag:
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
boolean enable = true;
if(mOnOptionsMenuEnabledListener != null)
{
enable = mOnOptionsMenuEnabledListener.onOptionsMenuEnabledListener();
}
if(enable)
{
...
// select your menu items
}
return true;
}
So in this approach, the menu is still there (which is a big plus). But the option menu doesn't react until it settles down on a particular page.
With this I no longer encounter the problem.
I hope this help someone in the future!
Cheers!
When the button is clicked check ViewPagers current item -
https://developer.android.com/reference/android/support/v4/view/ViewPager.html#getCurrentItem()
YourObject object = yourList.get(yourViewPager.getCurrentItem());
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.