How to refresh current view in ViewPager - android

I am using ViewPager with views V1, V2, V3 ..... I am trying to set visibility of a LinearLayout used in each view, by clicking on a button. Through this code it apply the change on the next view instead of the current view. e.g. I am on V5. When I click it hides/show the object on V6. If I am going backwards from V6 to V5, then it applies the change on V4.
Here is the code:
public class FragmentStatePagerSupport extends FragmentActivity {
static final int NUM_ITEMS = 10;
MyAdapter mAdapter;
ViewPager mPager;
static int mNum;
private Button btn_zoom;
static LinearLayout LL_Head;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(5);
btn_zoom = (Button)findViewById(R.id.btn_zoom);
btn_zoom.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
if (LL_Head.getVisibility() == View.VISIBLE) {
LL_Head.setVisibility(View.GONE);
}else{
LL_Head.setVisibility(View.VISIBLE);
}
}
});
.
.
.
}
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
}
public static class ArrayListFragment extends ListFragment {
static ArrayListFragment newInstance(int num) {
ArrayListFragment f = new ArrayListFragment();
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNum = getArguments() != null ? getArguments().getInt("num") : 1;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.sura_vpager, container, false);
TextView tv1=(TextView) v.findViewById(R.id.txtHead);
tv1.setText("Fragment #" + mNum);
LL_Head = (LinearLayout)v.findViewById(R.id.LL_Head);
return v;
}
Please advise
Thanks

In order to make a fluent experience the ViewPager not only loads the view you are currently looking at, but also the adjacent views. That means, that if you are scrolling from position 0 to position 1, what actually happens is that position 2 is loaded, so it will be ready when you scroll on. This is why the change is applied to the "next" view, rather than the current one (if you scroll from view 2 to 1, then view 0 is created).
Since you are setting the static LinearLayout in OnCreate, then it's only the last view to be created that is changed - and this will only ever be the one you are looking at, if you have scrolled to the end of the pager. Instead you should keep track of which fragment the user is looking at (ViewPager.setOnPageChangeListener()) and cache the fragment in your adapter. You then know which fragment position you want, and when you ask for it, you will just return the one you previously created (don't create a new one, then it won't work :)).
Or, the tl;dr version:
LL_Head is almost always set to be the next fragment, not the current one. Don't set it statically. Cache it in your PagerAdapter and reget it when you need it.
Edit:
Alternatively you may want to have the fragments listen to an event of sorts, which tells them whether they should show or hide the layout in question. Otherwise it will only be the current fragment that is affected by this, rather than all fragments.

The numbering in Java starts from 0. Thus when you want to set the 5th item, you have to call mPager.setCurrentItem(4);
Hope this helps!

Related

Android memory leak issue when using ViewPagerAdapter with nested fragments

I have a fragment, fragment A, which holds a ViewPager. The ViewPager loads different fragments which the user can swipe through "indefinitely" (I use a really high number of pages/loops to emulate this). When a user clicks on the current ViewPager fragment, then fragment A with the ViewPager is replaced by fragment B in the fragment manager. When the user returns from fragment B, the backstack is popped using popBackStackImmediate(). If the user repeats this action several times, the heap begins to fill up by about 100kb at a time until the app starts to become sloppy and malfunction as the memory fills up. I'm unsure what exactly is causing this, can anyone help?
My fragment A with the ViewPager:
public class MainFragment extends Fragment {
private MainWearActivity mMainWearActivity;
View view;
private int currentPage;
private ViewPager pager;
private ViewPagerAdapter adapter;
private LinearLayout helpIcons;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainWearActivity = (MainWearActivity) getActivity();
adapter = new ViewPagerAdapter(this.getChildFragmentManager());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_main, container, false);
// Scrolling menu
pager = (ViewPager) view.findViewById(R.id.watchNavPager);
pager.setAdapter(adapter);
pager.addOnPageChangeListener(adapter);
// Set current item to the middle page
pager.setCurrentItem(Consts.FIRST_PAGE);
currentPage = Consts.FIRST_PAGE;
// Set number of pages
pager.setOffscreenPageLimit(4);
// Set no margin so other pages are hidden
pager.setPageMargin(0);
return view;
}
#Override
public void onDestroyView() {
pager = null;
super.onDestroyView();
}
}
My adapter class:
public class ViewPagerAdapter extends FragmentPagerAdapter implements
ViewPager.OnPageChangeListener {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position)
{
position = position % Consts.PAGES;
switch(position){
case Consts.AUDIO_POS:
return new AdapterAudioFragment();
case Consts.VOICE_POS:
return new AdapterVoiceFragment();
case Consts.MAIL_POS:
return new AdapterMailFragment();
case Consts.INFO_POS:
return new AdapterInfoFragment();
default:
return null;
}
}
#Override
public int getCount()
{
return Consts.PAGES * Consts.LOOPS; // (4 * 1000)
}
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
}
One of my fragments that the adapter loads (they are all pretty much the same):
public class AdapterAudioFragment extends Fragment {
private ImageView menuImg;
private TextView menuText;
private LinearLayout rootView;
private MainWearActivity mMainWearActivity;
private View.OnClickListener imgClickListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainWearActivity = (MainWearActivity) getActivity();
imgClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
mMainWearActivity.replaceFragment(mMainWearActivity.getFragment(Consts.FRAG_AUDIO), Consts.FRAG_AUDIO);
}
};
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// Get root view of the fragment layout
rootView = (LinearLayout) inflater.inflate(
R.layout.fragment_nav_object, container, false);
// Set the current menu image and text
menuImg = (ImageView) rootView.findViewById(R.id.fragment_image);
menuImg.setImageResource(R.mipmap.ic_audio);
menuImg.setOnClickListener(imgClickListener);
menuText = (TextView) rootView.findViewById(R.id.menuTxt);
menuText.setText(Consts.MENU_HEADER_AUDIO);
// Set the current menu selection
mMainWearActivity.setCurrentSelection(Consts.AUDIO_POS);
return rootView;
}
}
I have a feeling that the adapter's fragments are all being created but never destroyed and piling up in the heap but I can't figure out how to resolve this. Do I need to call destroyItem in the adapter and manually destroy them? Any help would be most appreciated, thanks.
Adding this to Fragment stopped leaks for me:
#Override
public void onDestroyView() {
super.onDestroyView();
viewPager.setAdapter(null);
}
Looking at the source code, the problem seems to be that when calling ViewPager#setAdapter the view will register itself as observer for the adapter. So each time onViewCreated is called your pager adapter instance will have reference of the newly created view.
There is a specific PagerAdapter for your needs - FragmentStatePagerAdapter
This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.

notifyDataSetChanged Method Takes Away Smooth Scrolling From Tabs

I am currently using Material Design in an Android app that I am making. In this app, I am using the Material Design tab layout to display some information that I am receiving. However when I tap the tabs, the animation is not smooth, and it is very abrupt. Sliding to go to the other tab, however is very smooth.
mTabLayout = (TabLayout) findViewById(R.id.chem_tab_layout);
mGenericAdapter = new GenericPagerAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.view_pager);
mPager.setAdapter(mGenericAdapter);
//Notice how the Tab Layout links with the Pager Adapter
mTabLayout.setTabsFromPagerAdapter(mGenericAdapter);
//Notice how The Tab Layout and View Pager object are linked
mTabLayout.setupWithViewPager(mPager);
mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout){
#Override
public void onPageSelected(int position) {
mGenericAdapter.notifyDataSetChanged();
}
});
That is my code for setting the adapter, etc.
This is my custom adapter code for the tabs:
class GenericPagerAdapter extends FragmentStatePagerAdapter {
public GenericPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
ChemGridActivity.MyFragment myFragment = new ChemGridActivity.MyFragment();
return myFragment;
}
#Override
public int getCount() {
return 3; //returns number of tabs that need to be created
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) return "Chemistry";
if (position == 1) return "Mathematics";
if (position == 2) return "Physics";
else return null;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
I feel that the choppy transition between tabs is caused by the overriden method onPageSelected method when I add onPageChangeListener. What do I add to this method to make tapping on tabs a smoother animation?
Without knowing much about the internals of your classes, I imagine the problem is not that you have a listener, but what you are doing inside that listener.
In the case of most adapters notifyDataSetChanged() will cause it to re-render the entire view again (including all pages).
Seeing as you haven't specified what the intent here with the notification is, it's hard to tell you how you can do this in an alternative way, but you do need to do something less intensive if you want the animation to remain smooth.
I suspect you just want to change which fragment is shown, in which case just use the FragmentManager where necessary, remembering to reuse fragments which have already been seen once.
EDIT Based on additional info in comments
#Override
public Fragment getItem(int position) {
//POSITION_SOMETHINHG would be one of a set of constants to indicate hwa to display
return ChemGridActivity.MyFragment.newInstance(ChemGridActivity.MyFragment.POSITION_SOMETHINHG);
}
public class ChemGridActivity.MyFragment ... {
private static final String KEY_DISPLAY_TYPE = "KEY_DISPLAY_TYPE";
public static final int POSITION_SOMETHINHG = 11111;
public static MyFragment newInstance(int display) {
MyFragment f = new MyFragment();
Bundle bund = new Bundle();
bund.putInt(KEY_DISPLAY_TYPE, display);
f.setArguments(bund);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
mDisplay = args.getInt(KEY_DISPLAY_TYPE, 0);
}
}
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.my_layout, container, false);
//TODO: change something based on mDisplay
return view;
}

Swiping through views in Android

I have seen a lot of answered questions about this, but none about what I exactly want so here it goes (if there's an answered thread about this I'd appreciate it):
I want to create a kind of "level selection" app, where you basically have to swipe from right to left in order to be able to see the next list of levels, however I want to do it WITHOUT tabs (haven't found out how to do it yet).
Thanks.
EDIT: Solved it by simply using a ViewPager without even bothering or paying attention to the ActionBar part Android tells you to add (I just created my few fragments, my viewpager, and this last one did the rest, didn't even need to use a gesture detector for swipes as viewpager already provides this animation).
Any ViewPager Tutorial teaching you how to swipe between tabs basically has all the information I needed :) Thanks everyone!
For doing this you shiuld use ViewPager
Its easy !
Layout ViewPager
<android.support.v4.view.ViewPager
android:id="#+id/vpPager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v4.view.ViewPager>
Define Fragments
public class FirstFragment extends Fragment {
// Store instance variables
private String title;
private int page;
// newInstance constructor for creating fragment with arguments
public static FirstFragment newInstance(int page, String title) {
FirstFragment fragmentFirst = new FirstFragment();
Bundle args = new Bundle();
args.putInt("someInt", page);
args.putString("someTitle", title);
fragmentFirst.setArguments(args);
return fragmentFirst;
}
// Store instance variables based on arguments passed
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
page = getArguments().getInt("someInt", 0);
title = getArguments().getString("someTitle");
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_first, container, false);
TextView tvLabel = (TextView) view.findViewById(R.id.tvLabel);
tvLabel.setText(page + " -- " + title);
return view;
}
}
Setup FragmentPagerAdapter
public class MainActivity extends FragmentActivity {
// ...
public static class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return FirstFragment.newInstance(0, "Page # 1");
case 1: // Fragment # 0 - This will show FirstFragment different title
return FirstFragment.newInstance(1, "Page # 2");
case 2: // Fragment # 1 - This will show SecondFragment
return SecondFragment.newInstance(2, "Page # 3");
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
}
Apply the Adapter
public class MainActivity extends FragmentActivity {
FragmentPagerAdapter adapterViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
ViewPager vpPager = (ViewPager) findViewById(R.id.vpPager);
adapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
vpPager.setAdapter(adapterViewPager);
}
// ...
}
Setup OnPageChangeListener
// Attach the page change listener inside the activity
vpPager.setOnPageChangeListener(new OnPageChangeListener() {
// This method will be invoked when a new page becomes selected.
#Override
public void onPageSelected(int position) {
Toast.makeText(HomeActivity.this,
"Selected page position: " + position, Toast.LENGTH_SHORT).show();
}
// This method will be invoked when the current page is scrolled
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Code goes here
}
// Called when the scroll state changes:
// SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING
#Override
public void onPageScrollStateChanged(int state) {
// Code goes here
}
});
if you need more info see this link (it also explain how to use tabs) :
https://github.com/codepath/android_guides/wiki/ViewPager-with-FragmentPagerAdapter
You have to use HorizontalScrollView (http://developer.android.com/reference/android/widget/HorizontalScrollView.html) which will manage horizontall scroll by himself. Juste place other view inside and you're good.
Edit: Solved it by simply using a ViewPager without even bothering or paying attention to the ActionBar part Android tells you to add (I just created my few fragments, my viewpager, and this last one did the rest, didn't even need to use a gesture detector for swipes as viewpager already provides this animation).
Any ViewPager Tutorial teaching you how to swipe between tabs basically has all the information I needed :) Thanks everyone!

Android Fragments in a ViewPager

I'm kinda confused about the whole Fragment-way-of-thinking. I've followed a tutorial on how to create a ViewPager with Fragments like the Google Play app.
I have TabFragment class like this one:
public class SwipeyTabFragment extends SherlockFragment {
#Override
public void onCreate(Bundle b) {
super.onCreate(b);
Log.e("FRAGMENT: ", "Hello World!");
}
public static Fragment newInstance(String title) {
SwipeyTabFragment f = new SwipeyTabFragment();
Bundle args = new Bundle();
args.putString("title", title);
f.setArguments(args);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_swipeytab, null);
final String title = getArguments().getString("title");
((TextView) root.findViewById(R.id.text)).setText(title);
return root;
}
}
I know that the onCreateView method initialize the layout and the controlls like Button, ListView and so on.
Over to my FragmentAdapter
private class SwipeyTabsPagerAdapter extends FragmentPagerAdapter implements SwipeyTabsAdapter {
private final Context mContext;
public SwipeyTabsPagerAdapter(Context context, FragmentManager fm) {
super(fm);
this.mContext = context;
}
#Override
public Fragment getItem(int position) {
return SwipeyTabFragment.newInstance(TITLES[position]);
}
#Override
public int getCount() {
return TITLES.length;
}
public TextView getTab(final int position, SwipeyTabs root) {
TextView view = (TextView) LayoutInflater.from(mContext).inflate(R.layout.swipey_tab_indicator, root, false);
view.setText(TITLES[position]);
view.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mViewPager.setCurrentItem(position);
}
});
return view;
}
}
This will just construct a new Tab based on a String-Array, which will set the text and the header of the Fragment.
So this is where I get confused. Say for instance that I want several fragments with different layout, and different ways of interacting when the user presses on Button, Picture or whatever. How can I do so?
Thanks.
All the 'user presses button' stuff is handled in the fragments, you can call back to the Activity of course when you need to (see here).
You have to create different fragment classes for different layouts, logic. etc. and return them to the ViewPager in getItem. You could have a FirstPageFragment and a SecondPageFragment, then return them (depending on the index) in getView. This only makes sense if those fragments have different functionalities of course.
Hope it's clear what I mean ;)
EDIT: as to your comment:
I don't know what exactly you want to do, but you have your SwipeyTabFragment already defined in it's own file. Take this then, modify it, give it another layout and other functionality, then call it OtherFragment or whatever. Let's say you want to have 2 different 'pages' in your App - the getCount() method in your adapter defines the amount of 'pages' in your ViewPager, so let's let it return two.
In the getItem() method, if position is 0, let it return your SwipeyFragment, else (position is 1) let it return your new OtherFragment. Now you have a ViewPager with 2 different Fragments that can serve totally different purposes.

Activity for each row in database table

For an application which I am currently building in Java for Android I am looking for the following. I have a set with lets say 10 database records. For each row i want to show an activity (which is every time the same activity). The activity has some fields for each row to be updated.
Lets say for example you have a record set with contacts and now you want to loop through al 10 contacts and update the data.
Now I am not sure how this should work in android. Should I use fragments for this? How should i model this. Some pseudo code would help.
Since this is a custom app I am developing for ICS
Thanks very much
Create FragmentActivity with ViewPager.
ViewPager uses Adapter to display same fragments with different data.
For example, if you store in database images ids which you want to display, the code will look like this.
public class MyActivity extends FragmentActivity {
public static class MyFragment extends Fragment {
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final ImageView content = new ImageView(getActivity());
content.setImageResource(getArguments().getInt("img"));
return content;
}
}
private class MyPagerAdapter extends FragmentStatePagerAdapter {
private final ArrayList<Integer> imgs;
public AppTourPagerAdapter(final FragmentManager fm, final ArrayList<Integer> imgs) {
super(fm);
this.imgs = imgs;
}
#Override
public int getCount() {
return imgs.size();
}
#Override
public Fragment getItem(final int position) {
final MyFragment fragment = new MyFragment();
final Bundle args = new Bundle();
args.putInt("img", imgs.get(position));
fragment.setArguments(args);
return fragment;
}
}
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
final ViewPager pager = (ViewPager) findViewById(R.id.pager);
final ArrayList<Integer> imgIds = ... // get values from database
pager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), imgIds));
}
}
Use a viewpager with a viewpager adapter. then create a layout with a view pager, after that create a layout that will contain the items in the view pager you wish to flip through. In the adapter inflate the layout you wish to flip through. There are tons of examples online. Google it. You can copy paste 90% of what, you need to make it work.

Categories

Resources