ViewPagerAdapter returns wrong position - android

I'm developing an application in which i use a ViewPager.
Underneath the ViewPager, there are some bullet points that should indicate which page is displayed, but their behaviour is wrong.
Here's the activity part:
ind1 = (ImageView)findViewById(R.id.page1);
ind2 = (ImageView)findViewById(R.id.page2);
ind3 = (ImageView)findViewById(R.id.page3);
ind4 = (ImageView)findViewById(R.id.page4);
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
Here's the Adapter:
/*Adapter for ViewPager*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position){
case 0:
ind1.setImageResource(on);
ind2.setImageResource(off);
ind3.setImageResource(off);
ind4.setImageResource(off);
return Frag1.newInstance();
case 1:
ind1.setImageResource(off);
ind2.setImageResource(on);
ind3.setImageResource(off);
ind4.setImageResource(off);
return Frag2.newInstance();
case 2:
ind1.setImageResource(off);
ind2.setImageResource(off);
ind3.setImageResource(on);
ind4.setImageResource(off);
return Frag3.newInstance();
case 3:
ind1.setImageResource(off);
ind2.setImageResource(off);
ind3.setImageResource(off);
ind4.setImageResource(on);
return Frag4.newInstance();
default:
ind1.setImageResource(on);
ind2.setImageResource(off);
ind3.setImageResource(off);
ind4.setImageResource(off);
return Frag1.newInstance();
}
}
#Override
public int getCount() {
return NUM_PAGES; /*NUM_PAGES = 4*/
}
}
Here's Frag1 (the other Fragments are equal to Frag1, the only differences are the layout and the loaded HTML file):
public class Frag1 extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.frag_page_1, container, false);
WebView wv = (WebView) rootView.findViewById(R.id.webView1);
wv.loadUrl("file:///android_asset/html/tabel1.html");
wv.setBackgroundColor(0x00000000);
wv.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
return rootView;
}
public static Frag1 newInstance(){
Frag1 f = new Frag1();
return f;
}
}
The wrong behaviour is the following: when activity starts, the first fragment is displayed but the 'active' bullet point is the second, for the second page the active point is the third, for the third page is active the fourth, for the fourth page the fourth point remains active. If i swipe back to the third page the second bullet point is active.
In simple terms, the active bullet point doesn't match with the displayed page.
I tried to print the position in the Adapter, like following, under each case::
Toast.makeText(context, String.valueOf(position), Toast.LENGTH_SHORT).show();
When activity starts, are printed two consecutive message, 0 and 1, without interacting with the ViewPager.
When i change page, the position is always wrong, but the relative fragment are displayed correctly.
I tried to set the bullet point state in the fragments class, but nothing changed.
Why the adapter indicates the wrong position? How can i make that the correct bullet point indicates the right displayed page?

Related

Error DialogFragment starting in multiple fragments

I currently got a ViewPager that has 3 tabs. Each tab is a fragment. I made a custom dialogFragment to login. I want that logindialog to open only when i go on my 3rd tab. In my onCreateView, i created a new object of my logindialog and show. My problem is, whenever i switch from my 1st tab to 2nd, the dialog also appears and i don't want that.
This is my viewpager adapter
public Fragment getItem(int position) {
switch (position) {
case 0:
return frag1;
case 1:
return frag2;
case 2:
return frag3;
default:
return null;
}
}
My 3rd Frag
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_tab_annonce, container, false);
DialogFragment loginDialog = new DialogFragment();
loginDialog .show(getActivity().getSupportFragmentManager(), "customLogin");
return root;
}
ViewPager will create and retain "offscreen" pages as a performance optimization (with one offscreen page in either direction by default). What this means in practice is that when you are on Page 1, Page 2 has already been created off-screen. When you switch from Page 1 to Page 2, now Page 3 is created off-screen (and Page 1 is retained in case you want to switch back to it).
That means that onCreateView() is simply not the right place to show the error message.
You could perhaps create your own subclass of ViewPager.SimpleOnPageChangeListener and override onPageSelected() to show the dialog.
ViewPager.OnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (position == 2) {
// show dialog
}
}
};
viewPager.addOnPageChangeListener(listener);

FragmentPagerAdapter opens a different fragment automatically

I have Fragment connected with FragmentPagerAdapter, with code
public class LeaveAdapterApproval extends FragmentPagerAdapter{
private static final String TAG = LeaveAdapterApproval.class.getSimpleName();
private static final int FRAGMENT_COUNT = 4;
public LeaveAdapterApproval(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToAll");
return new LeaveFragmentToAll();
case 1:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToPending");
return new LeaveFragmentToPending();
case 2:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToApproved");
return new LeaveFragmentToApproved();
case 3:
Log.d(TAG, "LOG : CURRENT FRAGMENT LeaveFragmentToDenied");
return new LeaveFragmentToDenied();
}
return null;
}
#Override
public int getCount() {
return FRAGMENT_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0:
return "All";
case 1:
return "Pending";
case 2:
return "Approved";
case 3:
return "Denied";
}
return null;
}
}
and results in Logcat
09-22 15:45:21.259 27234-27234/dan.taaku D/LeaveAdapterApproval: LOG : CURRENT FRAGMENT LeaveFragmentToPending
09-22 15:45:21.259 27234-27234/dan.taaku D/LeaveAdapterApproval: LOG : CURRENT FRAGMENT LeaveFragmentToAll
09-22 15:45:21.259 27234-27234/dan.taaku D/LeaveAdapterApproval: LOG : CURRENT FRAGMENT LeaveFragmentToApproved
We can see from the Logcat results that Fragment loads three Fragments at once, I set when the Fragment is opened Automatic to LeaveFragmentToPending in LeaveClassApproval
public class LeaveClassApproval extends Fragment {
private static final String TAG = LeaveClassApproval.class.getSimpleName();
private TabLayout tabLayout;
private ViewPager viewPager;
public LeaveClassApproval() {
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.hr_employee_leave_class, container, false);
tabLayout = view.findViewById(R.id.tabs);
viewPager = view.findViewById(R.id.view_pager);
viewPager.setAdapter(new LeaveAdapterApproval(getChildFragmentManager()));
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, true); // I set it here
return view;
}
}
But why LeaveClassApproval open LeaveFragmentToAll and LeaveFragmentToApproved, should not it be opening LeaveFragmentToPending instead. Is this a fault or is it a Fragment function?
I searched through Google but did not find an answer
So how to fix it?
Sounds like your problem is the default functionallity from the ViewPager itself. It has a variable called offscreenPageLimit, where the default value is 1. Means it'll always load one page to the left and one to the right. And you won't be able to set it to zero, because the source code of android behind it looks like this:,
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
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 you realy want to get rid of this behaviour one thing you could do is building your own Pager class based on the android source code and simply try to cut out this functionallity.
By default the ViewPager opens the current Fragment and its two neighboring ones (that is, the one before and the one after in the data). If you want to change the default behavior use viewPager.setOffscreenPageLimit(int numberOfFragmentsToBePreloadedOnEachSideOfTheCurrentFragment). Notice, however, that the ViewPager will always load at least its two neighbors.
From the docs:
int getOffscreenPageLimit ()
Returns the number of pages that will be retained to either side of
the current page in the view hierarchy in an idle state. Defaults to
1.

Different Activity each tab fragment

I have 3 tabs, and I'm trying to show a different activity and layout each tab.
I am using the tabs as my app navigation, so when a has changed, the whole layout and activity need to be changed.
Actually only the layout is changing.
Since the MainActivity.java + layout has the PageAdapter in it (the PageAdapter is loading the tab's content),
I have moved my first page activity from the MainActivity.java to a new java activity called showUpdates.java. Now I'm trying to access this activity as my Tab #1 content.
If you still didn't understand what I'm asking for, The following will make it clear:
My 3 tabs are:
Java: TabFragment1.java, XML: activity_show_updates.xml
Java: TabFragment2.java, XML: tab_fragment_2.xml
Java: TabFragment3.java, XML: tab_fragment_3.xml
The TabFragment1.java:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/**
* Was trying:
* Intent intent = new Intent(...)
* startActivity(intent);
*/
return inflater.inflate(R.layout.activity_show_updates, container, false);
}
As you can see, it's moving to a new layout, activity_show_updates.xml.
What I'm trying to do is - using my showUpdates.java as the Activity of the tab_fragment_1.
I've been trying to use Intent but my app crashed when I created it at TabFragement1.java.
Anyone knows how I can make showUpdates.java as the activity of this layout?
You cannot nest activities. There are fragments for that. Just create a different fragment for each tab and use an Activity to hold all of them. If you need it, you can also create child fragments (although the support is not so good and you might have some problems, but nothing really hard to handle).
If you have viewpager working, you can do the following:
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) {
if(position == 0){
//return first fragment
}
else if(position == 1){
//return second fragment
}
...
}
and you can do your onCreate logic in onCreateView:
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View yourFragment = inflater.inflate (Resource.Layout.fragment_name, container, false);
... //do stuff
return yourFragment;
}
All you need to do is define which fragments to output in your adapter

How to display images in Android and change it by swipe right/left?

I want to add an introduction to my Android application, to inform the user about how the app works. This intro will be displayed only, if the preferred settings intro will be false. So in this intro, there will be 3 images and at the end, there will be a page, with some text and two buttons, to enable the user to access the application, by making a login. The change between each image, will be made with a swipe movement, (so right to left +, left to right -). How Can I do ?
This can be done via the use of Fragments and ViewPager and FragmentPagerAdapter. Look at this documentation:
FragmentPagerAdapter: http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
ViewPager:
http://developer.android.com/reference/android/support/v4/view/ViewPager.html
You can have one fragment that is instantiated based on the id in the ViewPager, and that id will indicate which image to show in your image fragment. So for three images, you instantiate a new fragment that sets the image in the fragment based on the current page in the FragmentPagerAdapter. The second fragment can be one for the login buttons and text you want at the end.
Ex for adapter defined in your FragmentActivity (or AppCompatActivity)
public class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
if(position < 3)
return ImageFragment.newInstance(position);
else
return new LoginFragment();
}
}
Ex for the image fragment for the various images in your introduction:
public static class ImageFragment extends Fragment{
private int mPosition;
public ImageFragment(){
}
public static ImageFragment newInstance(int pos){
ImageFragment frag = new ImageFragment();
Bundle args = new Bundle();
args.putInt("pos", pos);
frag.setArguments(args);
return frag;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPosition = getArguments().getInt("pos");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_image, container, false);
ImageView backgroundView = (ImageView) v.findViewById(R.id.background_image);
switch(mPosition){
case 0:
//set background view image 1
case 1:
//set background view image 2
default:
//set background view image 3
}
return v;
}
}
I would recommend using a ViewPager. Check out this tutorial from the Developer Guide
http://developer.android.com/training/animation/screen-slide.html
If you want to add functionality to each of these pages instead of having just images then perhaps you can implement a fragmentStatePagerAdapter and then put all the functionality in each fragment. Here is a tutorial to implement one.
http://www.truiton.com/2013/05/android-fragmentstatepageradapter-example/
I think we can do it by using recycler view itself.
Using PagerSnapHelper layout manager in recycler view, we can implement swipe to change images.
recyclerView.setLayoutManager(new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false));
// add pager behavior
PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

Dynamic UI with sliding menu and actionbarsherlock

Trying to achieve a dynamic UI with facebook like sliding menu and actionbarsherlock
.First i have look into android documentation which introduce fragment to handle dynamic button. But with no luck and a week time , i still can't get it to work anyhow , i guess is my misunderstand on android concept.The slidingbar and actionbarsherlock work without any problem.
I have a HomeScreen.java which contain all my menu and presetation stage
and so far i have created a pagerAdapter1.java that extends FragmentPagerAdapter
, and three example fragment class that handle my work which is task1.java,task2.java
,task3.java simple enough
here is part of my code
HomeScreen.java
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.slidingmenu.lib.SlidingMenu;
import com.slidingmenu.lib.app.SlidingFragmentActivity;
public class HomeScreen extends SlidingFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_screen);
setBehindContentView(R.layout.menu_frame);
}
PagerAdapter1.java
public class PagerAdapter1 extends FragmentPagerAdapter {
private List<Fragment> fragments;
public PagerAdapter1(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
public Fragment getItem(int position) {
return this.fragments.get(position);
}
public int getCount() {
return this.fragments.size();
}
}
and three task1.java,2,3
import android.support.v4.app.Fragment;
public class Tab1Fragment extends Fragment{
onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
return null;
}
return (LinearLayout)inflater.inflate(R.layout.tab_frag1_layout, container, false);
}
I think its better to explain my problem with picture
A homescreen which is a presetation stage , whenever user click on menu , this page will change to the page he want
and this is my menu
My problem is how do i include this 3 fragment into my homescreen ? i have tried so many tutorial but it doesn't work in my situation.Most tutorial are creating fragment with code, i just want to include my 3 task into it
I´ll try to explain this sample code and you use for your need.
This is the ListFragment of your BehindContent (SlidingMenu):
public class ColorMenuFragment extends ListFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.list, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String[] colors = getResources().getStringArray(R.array.color_names);
ArrayAdapter<String> colorAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, android.R.id.text1, colors);
setListAdapter(colorAdapter);
//This array is only to fill SlidingMenu with a Simple String Color.
//I used MergeAdapter from Commonsware to create a very nice SlidingMenu.
}
#Override
public void onListItemClick(ListView lv, View v, int position, long id) {
//This switch case is a listener to select wish item user have been selected, so it Call
//ColorFragment, you can change to Task1Fragment, Task2Fragment, Task3Fragment.
Fragment newContent = null;
switch (position) {
case 0:
newContent = new ColorFragment(R.color.red);
break;
case 1:
newContent = new ColorFragment(R.color.green);
break;
case 2:
newContent = new ColorFragment(R.color.blue);
break;
case 3:
newContent = new ColorFragment(android.R.color.white);
break;
case 4:
newContent = new ColorFragment(android.R.color.black);
break;
}
if (newContent != null)
switchFragment(newContent);
}
// the meat of switching the above fragment
private void switchFragment(Fragment fragment) {
if (getActivity() == null)
return;
if (getActivity() instanceof FragmentChangeActivity) {
FragmentChangeActivity fca = (FragmentChangeActivity) getActivity();
fca.switchContent(fragment);
} else if (getActivity() instanceof ResponsiveUIActivity) {
ResponsiveUIActivity ra = (ResponsiveUIActivity) getActivity();
ra.switchContent(fragment);
}
}
}
Here is your BaseActivity Class:
It dont have swipe, as I could understand, you don't need this.
public class FragmentChangeActivity extends BaseActivity {
private Fragment mContent;
public FragmentChangeActivity() {
super(R.string.changing_fragments);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the Above View
if (savedInstanceState != null)
mContent = getSupportFragmentManager().getFragment(savedInstanceState, "mContent");
if (mContent == null)
mContent = new ColorFragment(R.color.red);
// set the Above View
//This will be the first AboveView
setContentView(R.layout.content_frame);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, mContent)
.commit();
// set the Behind View
//This is the SlidingMenu
setBehindContentView(R.layout.menu_frame);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.menu_frame, new ColorMenuFragment())
.commit();
// customize the SlidingMenu
//This is opcional
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, "mContent", mContent);
}
public void switchContent(Fragment fragment) {
// the meat of switching fragment
mContent = fragment;
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
getSlidingMenu().showContent();
}
}
Ok, So If you want to change the ColorFragment to anything else, do this:
First, choice the item that you want to use:
case 0:
newContent = new ColorFragment(R.color.red);
break;
to:
case 0:
newContent = new ArrayListFragment();
break;
I have made just a arraylist, it is just a simple example, you can do a lot of thing, then you can read about Fragment to learn how to do different things.
public class ArrayListFragment extends ListFragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, Listnames.TITLES));
//Listnames is a class with String[] TITLES;
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Log.i("FragmentList2", "Item clicked: " + id);
String item = (String) getListAdapter().getItem(position);
Toast.makeText(getActivity(), item, Toast.LENGTH_LONG).show();
}
}
Well, if you misunderstood something, just tell me.
My problem is how do i include this 3 fragment into my homescreen ?
It really depends on how do you want them to behave.
Do you want them to appear only one at a time without allowing swipeing between them? If yes then add/insert a container layout(for example a simple FrameLayout) in your Activity on which you'll add the Fragments. I didn't worked with the SlidingMenu library but it should have a callback called when you click one of the items in the menu. In that callback you'll attach the proper fragment to the container layout(the FrameLayout) I mention earlier.
Do you want to show only one Fragment but you want to allow the user to swipe between them? If yes use a ViewPager in the activity layout and in the callback triggered by the SlidingMenu library's menu selection set the current page of the ViewPager with the setCurrentItem() method.
If you want something different then this provide more details.
Most tutorial are creating fragment with code, i just want to include
my 3 task into it
This, I don't quite understand. If you want to "include" your task fragments directly in your xml layout, you can but you'll be limited on what you can do with them(not to mention that all the fragments will be on one single screen) and I would avoid it. If you want something else provide more details.
I don't think it will work like that with Fragments, I was looking for a solution as well and ended up adding the fragments by hand.
I'm working on something similar like this, but for me there was also the case of opening WebViews to designated URL's. So the "above" screen would always update on any click.
To control the behaviour of this I created a MenuItemResource object, which basically holds the properties, like the ID of the icon, the name of the menu item and the URL.
public class MenuItemResource {
private int aValue;
private int aUrl;
private int aIconIdle;
private int aIconActive;
public MenuItemResource(int value, int url, int iconIdle, int iconActive) {
aValue = value;
aUrl = url;
aIconIdle = iconIdle;
aIconActive = iconActive;
}
}
The behaviour is handled by an OnItemClickListener which checks with a switch which values are in the MenuItemResource that is being clicked. For the WebView it's quite straightforward:
newFragment = new WebViewFragment();
final Bundle arguments = new Bundle();
arguments.putString(Constants.KEY_URL, getString(item.getUrl()));
newFragment.setArguments(arguments);
startFragment(newFragment, false);
// boolean is used to add the fragment to the backstack
The startFragment method just uses the FragmentManager and FragmentTransaction to replace the current Fragment. This works the same for other MenuItemResources that do start regular fragments.
newFragment = new Task1Fragment();
startFragment(newFragment, false);
I don't refer to the fragments in the MenuItemResource (yet), but it works pretty well for URLs and WebViews. The fragments are started based on the value in the MenuItemResource
I'm not sure how you would refer to the fragments like you did in the comments (Task1.java, etc), since you don't start them with Intents like Activities. Also I'm not sure why you would want to do this dynamically for Fragments (I can imagine this case being dynamic for WebViews though) as they need to be compiled anyway, so that's why my menu items are added by hand.

Categories

Resources