Crash when dynamically add and remove fragments from viewpager - android

I am using TabLayout with ViewPager (fragmented webview) to get a tabbed browser. The issue I am facing is my app crashes when I remove any tab expect the first and the last tab from tabbed layout.
In this case we are also removing linked Fragment from ViewPager which contains the WebView.
After that again we add multiple tabs and removes then the app crash.
The Add function
private void addViewPagerAndTab(String tag) {
mTabTags.add(tag);
MyFragment browserWebviewFragment = new MyFragment();
Bundle bundle = new Bundle();
String timestamp = System.currentTimeMillis() + "";
bundle.putString("timestamp", timestamp);
browserWebviewFragment.setArguments(bundle);
adapter.addFrag(browserWebviewFragment, "TAB " + countTabHeading, "TAB " + countTabHeading, timestamp);
countTabHeading++;
if (adapter != null) {
adapter.notifyDataSetChanged();
}
tabLayout.setupWithViewPager(viewPager);
tabLayout.setScrollPosition(((mTabTags.size()) + 1), 0f, true);
viewPager.setCurrentItem(((mTabTags.size()) + 1));
setupTabIcons();
}
The Remove Function
private void removeTabs() {
int position = tabLayout.getSelectedTabPosition();
if (deleteTabPosition > position) {
mTabTags.remove(deleteTabPosition);
tabLayout.removeTabAt(deleteTabPosition);
adapter.removeViewPagerView(deleteTabPosition);
} else if (deleteTabPosition < position) {
mTabTags.remove(deleteTabPosition);
tabLayout.removeTabAt(deleteTabPosition);
adapter.removeViewPagerView(deleteTabPosition);
tabLayout.setScrollPosition(position - 1, 0f, true);
viewPager.setCurrentItem(position - 1);
} else {
int tabCount = tabLayout.getTabCount();
if (tabCount > position + 1) {
mTabTags.remove(deleteTabPosition);
tabLayout.removeTabAt(deleteTabPosition);
adapter.removeViewPagerView(deleteTabPosition);
tabLayout.setScrollPosition(position, 0f, true);
viewPager.setCurrentItem(position);
} else if (tabCount <= position + 1) {
mTabTags.remove(deleteTabPosition);
tabLayout.removeTabAt(deleteTabPosition);
adapter.removeViewPagerView(deleteTabPosition);
tabLayout.setScrollPosition(position - 1, 0f, true);
viewPager.setCurrentItem(position - 1);
}
}
}
The ViewPager Adapter
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<TabInfo> mFragmentTitleList = new ArrayList<TabInfo>();
private int deleteTabPosition = -1;
class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFrag(Fragment fragment, String title, String url, String timestamp) {
mFragmentList.add(fragment);
TabInfo infoTab = new TabInfo();
infoTab.setStrTabName(title);
infoTab.setStrTabUrl(url);
infoTab.setTimestamp(timestamp);
mFragmentTitleList.add(infoTab);
}
#Override
public int getItemPosition(Object object) {
Fragment fragmentTemp = (Fragment) object;
if (mFragmentList.contains(fragmentTemp)) {
return mFragmentList.indexOf((Fragment) object);
} else {
return POSITION_NONE;
}
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position).getStrTabName();
}
public View getTabView(final int position) {
View v = LayoutInflater.from(MainActivity.this).inflate(R.layout.tab_bg, null);
TextView tv = (TextView) v.findViewById(R.id.tvTab);
tv.setText(mFragmentTitleList.get(position).getStrTabName());
final ImageView img = (ImageView) v.findViewById(R.id.ic_tab_close);
img.setImageResource(R.drawable.ic_tab_close);
img.setTag(mFragmentTitleList.get(position).getTimestamp());
img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String strTag = (String) v.getTag();
deleteTabPosition = -1;
if (mFragmentTitleList != null) {
for (int i = 0; i < mFragmentTitleList.size(); ++i) {
if (mFragmentTitleList.get(i).getTimestamp().equals(strTag)) {
deleteTabPosition = i;
break;
}
}
}
if (tabLayout.getTabCount() != 1) {
removeTabs();
} else {
}
}
});
return v;
}
/*
* Remove the View pager page associated with Tab
* */
public void removeViewPagerView(int position) {
mFragmentList.remove(position);
mFragmentTitleList.remove(position);
this.notifyDataSetChanged();
}
}
Full sourcecode on following link
http://45.33.27.221/CustomTabNoida.zip
java.lang.IllegalStateException: Fragment already active
at android.support.v4.app.Fragment.setInitialSavedState(Fragment.java:574)
at android.support.v4.app.FragmentStatePagerAdapter.instantiateItem(FragmentStatePagerAdapter.java:110)
at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:870)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1054)
at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:552)
at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:514)
at android.support.v4.view.ViewPager.dataSetChanged(ViewPager.java:946)
at android.support.v4.view.ViewPager$PagerObserver.onChanged(ViewPager.java:2910)
at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
at android.support.v4.view.PagerAdapter.notifyDataSetChanged(PagerAdapter.java:276)
at com.sourcefuse.sns.views.Activities.HomeScreenActivity.addViewPagerAndTab(HomeScreenActivity.java:603)
at com.sourcefuse.sns.views.Activities.HomeScreenActivity.onClick(HomeScreenActivity.java:525)
at android.view.View.performClick(View.java:5204)
at android.view.View$PerformClick.run(View.java:21153)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Related

Null object reference when using getChildFragmentManager().beginTransaction()

I have 4 fragments in my ViewPgaer using FragmentPagerAdapter. I try to add each fragment after a constant delay to have a jerk free animation while switching bottom bar tabs.
I use this library for the bottom bar which has a shifting animation. All four fragments have multiple api calls and if I don't add these after a delay, the shifting animation is jerky.
The problem is I get an error log (posted below) which is hard to replicate.
Error Log:
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:380)
at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:369)
at com.vanitee.services.view.FragmentFake$1.run(FragmentFake.java:46)
at android.os.Handler.handleCallback(Handler.java:761)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6585)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:941)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:831)
ViewPagerAdapterClass:
class MyAccountPagerAdapter extends FragmentPagerAdapter {
public MyAccountPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position == ME_TAB) {
if (Utility.isEmpty(fragmentProfile)) {
fragmentProfile = FragmentFake.create(new FragmentUserProfile(), 50);
}
return fragmentProfile;
} else if (position == BOOKINGS_TAB) {
if (Utility.isEmpty(fragmentBooking)) {
fragmentBooking = FragmentFake.create(new FragmentBookings(), 100);
}
return fragmentBooking;
} else if (position == WALLET_TAB) {
if (Utility.isEmpty(fragmentWallet)) {
fragmentWallet = FragmentFake.create(new FragmentWallet(), 150);
}
return fragmentWallet;
} else if (position == LIKES_TAB) {
if (Utility.isEmpty(fragmentLikes)) {
fragmentLikes = FragmentFake.create(new FragmentLikes(), 200);
}
return fragmentLikes;
}
return null;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case ME_TAB:
return getContext().getString(R.string.me_tab);
case BOOKINGS_TAB:
return getContext().getString(R.string.bookings_tab);
case WALLET_TAB:
return getContext().getString(R.string.wallet_tab);
case LIKES_TAB:
return getContext().getString(R.string.likes_tab);
default:
return null;
}
}
#Override
public int getCount() {
return 4;
}
}
FragmentFake:
public class FragmentFake extends Fragment {
private View root;
Fragment fragment;
public static Fragment create(Fragment frag, int timeMs) {
FragmentFake fragmentFake = new FragmentFake();
Bundle bundle = new Bundle();
fragmentFake.fragment = frag;
bundle.putInt("time", timeMs);
fragmentFake.setArguments(bundle);
return fragmentFake;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
root = inflater.inflate(R.layout.layout_fake_fragment, container, false);
Bundle bundle = getArguments();
replaceFragment(bundle.getInt("time"));
return root;
}
private void replaceFragment(int time) {
root.postDelayed(new Runnable() {
#Override
public void run() {
if(!isAdded()){
return;
}
getChildFragmentManager().beginTransaction().add(R.id.replace_fragment_container, fragment).commit();
}
}, time);
}
}
The Error log reported is on the line
getChildFragmentManager().beginTransaction().add(R.id.replace_fragment_container, fragment).commit();
Create one Abstract Fragment that extends Fragment. like this
public abstract BaseChildFragment extends Fragment{
public void onViewPagerPagechange();
}
Make All ChildFragment extends BaseChildFragment(FragmentUserProfile, FragmentUserLike,FragmentUserWellet,ETC) ,
On your Adapter add below code.
LruCache<Integer,BaseChildFragment> cache = new LruCache<>(4);
public BaseChildFragment getBaseChildFragment(int position){
return cache.get(position);
}
#Override
public Fragment getItem(int position) {
if (position == ME_TAB) {
if (Utility.isEmpty(fragmentProfile)) {
fragmentProfile = FragmentFake.create(new FragmentUserProfile(), 50);
cache.put(position,fragmentProfile );
}
return fragmentProfile;
} else if (position == BOOKINGS_TAB) {
if (Utility.isEmpty(fragmentBooking)) {
fragmentBooking = FragmentFake.create(new FragmentBookings(), 100);
cache.put(position,fragmentBooking );
}
return fragmentBooking;
} else if (position == WALLET_TAB) {
if (Utility.isEmpty(fragmentWallet)) {
fragmentWallet = FragmentFake.create(new FragmentWallet(), 150);
cache.put(position,fragmentWallet );
}
return fragmentWallet;
} else if (position == LIKES_TAB) {
if (Utility.isEmpty(fragmentLikes)) {
fragmentLikes = FragmentFake.create(new FragmentLikes(), 200);
cache.put(position,fragmentLikes );
}
return fragmentLikes;
}
return null;
}
On your Fragment that have View pager AddpageChangeListener like this
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
// optional
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
// optional
#Override
public void onPageSelected(int position) {
BaseChildFragment fragment = adapter.getBaseChildFragment(position);
fragmeng.onViewPagerPagechange();
}
// optional
#Override
public void onPageScrollStateChanged(int state) { }
});
This will call your ChildFragment onViewPagerPagechange(); method. where you can load your child fragments.
You cannot do this
public static Fragment create(Fragment frag, int timeMs) {
FragmentFake fragmentFake = new FragmentFake();
Bundle bundle = new Bundle();
//remove this line, it won't work
fragmentFake.fragment = frag;
bundle.putInt("time", timeMs);
fragmentFake.setArguments(bundle);
return fragmentFake;
}
You can remove that assignment line from the create statement and add another method to assign the fragment after an instance is created. Your code should be like this
public static Fragment create(Fragment frag, int timeMs) {
FragmentFake fragmentFake = new FragmentFake();
Bundle bundle = new Bundle();
bundle.putInt("time", timeMs);
fragmentFake.setArguments(bundle);
return fragmentFake;
}
public void setFragment(Fragment fragment){
this.fragment = fragment;
}
//How you assign fragment
FragmentFake fakeFragment = FragmentFake.create(0);
fakeFragment.setFragment(yourFragment);
Then make the fragment transaction

onCreateView is not always called after viewPagerAdapter.addTab()

I want to create app with dynamic add/remove tabs option and I've got problem with one thing.
When I'm adding tabs first time everything is ok but when I remove one tab and I want to add new tab again then there is no onCreateView called on that new fragment.
Example:
I add new 3 tabs:
Add tab 1:
ViewPagerAdapter vpa = (ViewPagerAdapter) mViewPager.getAdapter();
vpa.addTab("TAB 1");
vpa.notifyDataSetChanged();
onCreateView is called.
some logs:
[add tab] mViewPager.getChildCount() = 1
[add tab] vpa.getCount() = 1
Add tab 2:
vpa.addTab("TAB 2");
vpa.notifyDataSetChanged();
onCreateView is called.
some logs:
[add tab] mViewPager.getChildCount() = 2
[add tab] vpa.getCount() = 2
Add tab 3:
vpa.addTab("TAB 3");
vpa.notifyDataSetChanged();
onCreateView is called.
some logs:
[add tab] mViewPager.getChildCount() = 3
[add tab] vpa.getCount() = 3
now I want to delete Tab 2:
vpa.removeTab("TAB 2");
vpa.notifyDataSetChanged();
some logs:
[add tab] mViewPager.getChildCount() = 2
[add tab] vpa.getCount() = 2
and I want to add Tab 2 again:
vpa.addTab("TAB 2");
vpa.notifyDataSetChanged();
and there is no onCreateView called :(
some logs:
[add tab] mViewPager.getChildCount() = 2
[add tab] vpa.getCount() = 3
Can anyone know how to fix that? Why onCreateView is not always called?
App code:
private void initViewpagerAndTabs(){
mViewPager = (ViewPager) findViewById(R.id.viewpager_slave);
mViewPager.setOffscreenPageLimit(10);
mAdapterViewPager = new ViewPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mAdapterViewPager);
mTabLayout = (TabLayout) findViewById(R.id.tabLayout_slave);
mTabLayout.setupWithViewPager(mViewPager);
}
/** Tabs content fragment */
public static class DummyFragment extends Fragment implements RecyclerAdapter.ViewHolder.ClickListener {
private RecyclerView mRecyclerView;
private RecyclerAdapter mAdapterRecycler;
private RecyclerView.LayoutManager mLayoutManager;
private Fab fab;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_slave, container, false);
//setRetainInstance(true);
initRecyclerView(view);
fab = (Fab) getActivity().findViewById(R.id.fabRemoveItems);
return view;
}
private void initRecyclerView(View view){
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_slave);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getContext());
mRecyclerView.setLayoutManager(mLayoutManager);
RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
itemAnimator.setAddDuration(500);
itemAnimator.setRemoveDuration(500);
mAdapterRecycler = new RecyclerAdapter(this);
mRecyclerView.setAdapter(mAdapterRecycler);
}
}
private class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<DummyFragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public DummyFragment getItem(int num) throws IndexOutOfBoundsException{
return mFragmentList.get(num);
}
public void addTab(String title) throws Exception {
if(isTabExist(title)){
throw new Exception("Tabs already exist!");
}
//Log.d(TAG, "[addTab] mFragmentTitleList before add: " + mFragmentTitleList);
//Log.d(TAG, "[addTab] mFragmentList before add: " + mFragmentList);
mFragmentList.add(new DummyFragment());
mFragmentTitleList.add(title);
//Log.d(TAG, "[addTab] mFragmentTitleList after add: " + mFragmentTitleList);
//Log.d(TAG, "[addTab] mFragmentList after add: " + mFragmentList);
}
public void removeTab(int position){
//Log.d(TAG, "[removeTab] mFragmentTitleList before removed: " + mFragmentTitleList);
//Log.d(TAG, "[removeTab] mFragmentList before removed: " + mFragmentList);
mFragmentTitleList.remove(position);
mFragmentList.remove(position);
//Log.d(TAG, "[removeTab] mFragmentTitleList after removed: " + mFragmentTitleList);
//Log.d(TAG, "[removeTab] mFragmentList after removed: " + mFragmentList);
}
private boolean isTabExist(String name){
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(name)) {
return true;
}
}
return false;
}
public int getTabPosition(String tabName) throws IndexOutOfBoundsException{
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(tabName)) {
return i;
}
}
throw new IndexOutOfBoundsException();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
getSupportFragmentManager().beginTransaction().remove((Fragment) object)
.commit();
}
#Override
public int getItemPosition(Object object) {
if (mFragmentList.contains(object)) return mFragmentList.indexOf(object);
else return POSITION_NONE;
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Ok, I resolve that problem.
Delete:
mViewPager.setOffscreenPageLimit(10);
Override methods to save fragments state:
public void onViewStateRestored(#Nullable Bundle savedInstanceState)
public void onSaveInstanceState(Bundle outState)
And I've created my own FragmentStatePagerAdapter class. I needed to don't save fragment state on tab which was deleted.
Code:
public class FragStatePagerAdaper extends PagerAdapter {
private static final String TAG = "FragStatePagerAdapter";
private static final boolean DEBUG = true;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
/** Cached fragments */
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
private Fragment mCurrentPrimaryItem = null;
private List<DummyFragment> mFragmentList = new ArrayList<>();
private List<String> mFragmentTitleList = new ArrayList<>();
public FragStatePagerAdaper(FragmentManager fm) {
mFragmentManager = fm;
}
/**
* Return the Fragment associated with a specified position.
*/
public DummyFragment getItem(int num) throws IndexOutOfBoundsException{
return mFragmentList.get(num);
}
#Override
public void startUpdate(ViewGroup container) {
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
}
public void addTab(String title) throws Exception {
if(isTabExist(title)){
throw new Exception("Tabs already exist!");
}
mFragmentList.add(new DummyFragment());
mFragmentTitleList.add(title);
}
//look here!
public void removeTab(int position){
Log.d(TAG, "removeTab: position = " + position);
mFragmentTitleList.remove(position);
mFragmentList.remove(position);
mSavedState.remove(position); // <---- here!
}
private boolean isTabExist(String name){
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(name)) {
return true;
}
}
return false;
}
public int getTabPosition(String tabName) throws IndexOutOfBoundsException{
for(int i = 0; i < mFragmentTitleList.size(); i++) {
if (mFragmentTitleList.get(i).equals(tabName)) {
return i;
}
}
throw new IndexOutOfBoundsException();
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
#Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
}
ArrayList<Fragment> update = new ArrayList<Fragment>();
for (int i=0, n=mFragments.size(); i < n; i++) {
Fragment f = mFragments.get(i);
if (f == null) continue;
int pos = getItemPosition(f);
while (update.size() <= pos) {
update.add(null);
}
update.set(pos, f);
}
mFragments = update;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}
#Override
public Parcelable saveState() {
Bundle state = null;
if (mSavedState.size() > 0) {
state = new Bundle();
Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
mSavedState.toArray(fss);
state.putParcelableArray("states", fss);
}
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null && f.isAdded()) {
if (state == null) {
state = new Bundle();
}
String key = "f" + i;
mFragmentManager.putFragment(state, key, f);
}
}
return state;
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state != null) {
Bundle bundle = (Bundle)state;
bundle.setClassLoader(loader);
Parcelable[] fss = bundle.getParcelableArray("states");
mSavedState.clear();
mFragments.clear();
if (fss != null) {
for (int i=0; i<fss.length; i++) {
mSavedState.add((Fragment.SavedState)fss[i]);
}
}
Iterable<String> keys = bundle.keySet();
for (String key: keys) {
if (key.startsWith("f")) {
int index = Integer.parseInt(key.substring(1));
Fragment f = mFragmentManager.getFragment(bundle, key);
if (f != null) {
while (mFragments.size() <= index) {
mFragments.add(null);
}
f.setMenuVisibility(false);
mFragments.set(index, f);
} else {
Log.w(TAG, "Bad fragment at key " + key);
}
}
}
}
}
#Override
public int getItemPosition(Object object) {
if (mFragmentList.contains(object)) return mFragmentList.indexOf(object);
else return POSITION_NONE;
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Greetings!

OnCreateView in Fragment gets called every time

I have a ViewPager and I'm using FragmentStatePagerAdapter to hold my fragments. Now the problem is that when I slide the OnCreateView and OnCreate of the Fragments are called everytime and I make network calls in the Fragments which will be called again and again causing lag.
I have copied from here
How to Avoid calling the OnCreateView and OnCreate of the Fragments.
Here's my code:
FragmentHolder.class
public class ViewPagerAdapter extends SmartFragmentStatePagerAdapter {
final int PAGE_COUNT = 3;
private final ArrayList<Fragment> mFragments = new ArrayList<>();
private long baseId = 0;
private String tabTitles[] = new String[] { "HOME", "DISCOVER", "MESSAGES" };
private Context context;
private TabLayout tabLayout;
public int[] imageResId = {
R.drawable.ic_ac_unit_black_24dp,
R.drawable.ic_airport_shuttle_black_24dp,
R.drawable.ic_all_inclusive_black_24dp
};
public ViewPagerAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public Fragment getItem(int position) {
if(position == 0)
{
Log.d("Fragment","1");
return FragmentOne.newInstance(position + 1);
}
else if(position == 1)
{
Log.d("Fragment","2");
return FragmentTwo.newInstance(position + 2);
}
else if(position == 2)
{
Log.d("Fragment","3");
return FragmentThree.newInstance(position + 3);
}
return new Fragment();
}
#Override
public CharSequence getPageTitle(int position)
{
Drawable image = ContextCompat.getDrawable(context, imageResId[position]);
image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
SpannableString sb = new SpannableString(" ");
ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM);
sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return sb;
}
}
MainActivity:
lockableViewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager(),
Dashboard.this));
lockableViewPager.setSwipeable(false);
lockableViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if (position == 0) {
fragmentOne.message();
} else if (position == 1) {
fragmentTwo.message();
} else if (position == 2) {
fragmentThree.message();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Yes, using the setOffscreenPageLimit to 3 (depending on the no of fragments you have), allows you to retain the Fragments (not calling on CreateView)
lockableViewPager.setOffscreenPageLimit(3);

How to destroy a Fragment programmatically in FragmentStatePagerAdapter

I'm using the MainActivity's SearchView here to filter the listview which is showing under tabs but there is an issue of the state of Fragment.
It filters perfectly but when I select first Tab(ADVISORY), then it filters the child of this Tab. And after selecting next Tab(TOP ADVISORY), it filters all listview of the child of this tab but it stops filtering in previous Tab (ADVISORY). And at last I move to the EXPERTVIEW it works only for it not for others.
After lots of debugging I found that it doesn't get destroy when we select another Parent Tab so now I wanna know how I can remove a fragment from its adapter on unselected and reselect on selected again.
MainTabFragment.java
public class MainTabFragment extends Fragment {
public static int position_child_tab = 0, three_childs_tab_position = 0;
static int count = -1, index = 0;
int position_tab = 0;
Bundle args;
int Current_tab = 0;
public static MyTabLayout myTabLayout;
private static MainTabFragment sMainTabFragment;
public static NonSiwpablePager pager;
private Fragment mFragment;
SharedPreferences pref;
private static final int NUM_ITEMS = 2;
List<Integer> TABLE;
public MainTabFragment() {
// Required empty public constructor
}
public static MainTabFragment getInstance() {
return sMainTabFragment;
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment MainTabFragment.
*/
// TODO: Rename and change types and number of parameters
public static MainTabFragment newInstance(String param1, String param2) {
return new MainTabFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
args = getArguments();
if (args != null && args.containsKey("pos_next"))
position_tab = args.getInt("pos_next");
if (args != null && args.containsKey("pos_end"))
position_child_tab = args.getInt("pos_end");
if (position_child_tab != 3) {
three_childs_tab_position = position_child_tab;
} else {
three_childs_tab_position = 0;
}
args = new Bundle();
args.putInt("pos_end", position_child_tab);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.main_tab_fragment, container, false);
myTabLayout = (MyTabLayout) view.findViewById(R.id.mainTabLayout);
pager = (NonSiwpablePager) view.findViewById(R.id.pager);
final ViewPagerAdapter mAdapter = getViewPagerAdapter();
pager.setAdapter(mAdapter);
myTabLayout.setupWithViewPager(pager);
for (int i = 0; i < mAdapter.getCount(); i++) {
View customView = mAdapter.getCustomeView(getActivity(), i);
myTabLayout.getTabAt(i).setCustomView(customView);
}
pager.setCurrentItem(position_tab);
pager.setOffscreenPageLimit(NUM_ITEMS-1);
changeTab();
final Fragment fragment = getFragmentManager().findFragmentByTag("TOP");
if (fragment != null) {
pager.setCurrentItem(position_tab);
}
index = pager.getCurrentItem();
pref = getActivity().getPreferences(0);
SharedPreferences.Editor edt = pref.edit();
edt.putInt("facebook_id", pager.getCurrentItem());
edt.commit();
// ViewPagerAdapter adapter = ((ViewPagerAdapter)pager.getAdapter());
final Fragment fragment22 = mAdapter.getRegisteredFragment(pager.getCurrentItem());
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
pref = getActivity().getPreferences(0);
SharedPreferences.Editor edt = pref.edit();
edt.putInt("facebook_id", pager.getCurrentItem());
edt.commit();
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
//Log.e("Fragment", fragment + "" +pager.getCurrentItem() + fragment22);
}
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
// Log.e("Fragment", fragment + "" +pager.getCurrentItem() + fragment22);
}
});
myTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
pager.setCurrentItem(tab.getPosition());
//Log.e("Fragment", fragment + "" +pager.getCurrentItem() + fragment22);
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
// mAdapter.destroyAllItem();
// Log.e("Fragment", fragment + "" +pager.getCurrentItem() + fragment22);
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
// Log.e("Fragment", fragment + "" +pager.getCurrentItem() + fragment22);
}
});
return view;
}
public void changeTab() {
Current_tab = pager.getCurrentItem();
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myTabLayout.getTabAt(position_tab).getCustomView().setSelected(true);
}
public void setCurrentItem(int item) {
pager.setCurrentItem(item);
}
private ViewPagerAdapter getViewPagerAdapter() {
ViewPagerAdapter mAdapter = new ViewPagerAdapter(getChildFragmentManager());
String title_arr[] = {"ADVISORY", "TOP ADVISORS", "EXPERT VIEW"};
for (int i = 0; i < title_arr.length; i++) {
Map<String, Object> map = new Hashtable<>();
map.put(ViewPagerAdapter.KEY_TITLE, title_arr[i]);
if (i == 0) {
map.put(ViewPagerAdapter.KEY_FRAGMENT, AdvisoryPagerFragment.newInstance());
AdvisoryPagerFragment.newInstance().setTargetFragment(this, getTargetRequestCode());
} else if (i == 1) {
map.put(ViewPagerAdapter.KEY_FRAGMENT, TopAdvisoryPagerFragment.newInstance());
TopAdvisoryPagerFragment.newInstance().setTargetFragment(this, getTargetRequestCode());
} else if (i == 2) {
map.put(ViewPagerAdapter.KEY_FRAGMENT, ExperViewPagerFragment.newInstance());
ExperViewPagerFragment.newInstance().setTargetFragment(this, getTargetRequestCode());
}
mAdapter.addFragmentAndTitle(map);
}
return mAdapter;
}
public static class ViewPagerAdapter extends FragmentStatePagerAdapter {
private static final String KEY_TITLE = "fragment_title";
private static final String KEY_FRAGMENT = "fragment";
boolean abc = false;
private int[] drawables = new int[]{R.drawable.advisory_selector, R.drawable.top_advisors_selector, R.drawable.expertview_selector};
private List<Map<String, Object>> maps = new ArrayList<>();
private Fragment mCurrentFragment;
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public View getCustomeView(Context context, int pos) {
View mView = LayoutInflater.from(context).inflate(R.layout.custom_tab_view, null);
TextView mTextView = (TextView) mView.findViewById(R.id.textView);
mTextView.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/ufonts.com_cambria.ttf"));
ImageView mImageView = (ImageView) mView.findViewById(R.id.imageView2);
mImageView.setTag(pos);
/*if(count >0)
{
Toast.makeText(context,"Count Is "+count,Toast.LENGTH_SHORT).show();
mImageView = (ImageView) mImageView.getTag(0);
mImageView.setSelected(false);
}
*/
mImageView.setImageResource(drawables[pos]);
mTextView.setText(getPageTitle(pos));
return mView;
}
public void addFragmentAndTitle(Map<String, Object> map) {
maps.add(map);
}
#Override
public CharSequence getPageTitle(int position) {
return (CharSequence) maps.get(position).get(KEY_TITLE);
}
#Override
public Fragment getItem(int position) {
Log.e("Fragmentss", (Fragment) maps.get(position).get(KEY_FRAGMENT) + "");
return (Fragment) maps.get(position).get(KEY_FRAGMENT);
}
public Fragment getCurrentFragment() {
return mCurrentFragment;
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (mCurrentFragment != object) {
mCurrentFragment = (Fragment) object;
}
super.setPrimaryItem(container, position, object);
}
#Override
public int getCount() {
return maps.size();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}
}

ViewPager + FragmentStatePagerAdapter: How to recreate all fragment with new adapter

I'm using the v4 compatibility library. I have a Fragment Tabhost, the first tab have a ViewPager using FragmentStatePagerAdapter. The problem is when I switch to other tabs, change data of the first tab's ViewPager, then return to the first tab, recreate new Adapter with new data and set it to ViewPager, but the first 3 fragments in ViewPager still hold reference to old data.
My code in the first tab to create adapter (called in onCreateView method):
private void setPagerData() {
if (mPager == null || AppConfig.getInstance().todayData == null)
return;
if (AppConfig.getInstance().todayData != null && !AppConfig.getInstance().todayData.isEmpty()) {
//
mPager.setOffscreenPageLimit(AppConfig.PAGER_OFFSCREEN_PAGE_LIMIT);
mPager.setAlwaysDrawnWithCacheEnabled(true);
//
mAdapter = new TodayPagerAdapter(getChildFragmentManager(), AppConfig.getInstance().todayData);
mPager.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
if (mAdapter.getCount() == 1)
mPager.setCurrentItem(0);
else
mPager.setCurrentItem(1);
text_currentpage.setText("1");
text_totalpage.setText(String.valueOf(mAdapter.getCount() - 2));
anim_numpage = AnimationUtils.loadAnimation(TodayFragment.this.getActivity(), R.anim.fade_numpage);
layout_numpage.setVisibility(View.INVISIBLE);
image_nextpage.setOnClickListener(this);
image_prevpage.setOnClickListener(this);
//
mPager.setOnPageChangeListener(this);
} else
onNoData(true);
}
onDestroyView():
public void onDestroyView() {
AppUtils.Log("Destroy view Today Fragment");
mAdapter = null;
mPager = null;
super.onDestroyView();
}
My Adapter:
public class TodayPagerAdapter extends FragmentStatePagerAdapter {
private List<PostDTO> posts;
private FragmentManager fm;
public TodayPagerAdapter(FragmentManager fm, List<PostDTO> posts) {
super(fm);
this.fm = fm;
this.posts = posts;
if (posts == null)
posts = new ArrayList<PostDTO>();
}
#Override
public Fragment getItem(int page) {
if (page == 0 && posts.size() > 1)
return TodayPagerFragment.init(posts.size() - 1, posts.get(posts.size() - 1));
if (page == posts.size() + 1)
return TodayPagerFragment.init(0, posts.get(0));
if (posts.size() > 1)
return TodayPagerFragment.init(page - 1, posts.get(page - 1));
return TodayPagerFragment.init(page, posts.get(page));
}
#Override
public int getCount() {
if (posts.size() <= 1)
return posts.size();
return posts.size() + 2;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
My ViewPager's Fragment:
public class TodayPagerFragment extends AbstractFragment implements View.OnClickListener {
int page;
private ImageView iv_image;
private LikeButton button_like;
private PostDTO data;
public static TodayPagerFragment init(int val, PostDTO data) {
TodayPagerFragment frm = new TodayPagerFragment();
Bundle args = new Bundle();
args.putInt("page", val);
frm.setArguments(args);
frm.data = data;
return frm;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
page = getArguments() != null ? getArguments().getInt("page") : 0;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tab_today_viewpager, container, false);
setupView(view);
iv_image = (ImageView) view.findViewById(R.id.today_viewpager_image);
button_like = (LikeButton) view.findViewById(R.id.today_button_like);
button_like.setUp(true);
initViews();
return view;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.today_viewpager_image:
((BaseContainerFragment) getParentFragment().getParentFragment()).replaceFragment(TodayDetailFragment.init(this.data), true);
break;
case R.id.today_button_like:
if (!AppConfig.getInstance().isLoggedIn || AppConfig.getInstance().appUser == null) {
AppUtils.openLogin(getActivity());
} else {
String ms = getString(R.string.do_you_want_to_like_this_post_);
if (data.isLiked())
ms = getString(R.string.do_you_want_to_unlike_this_post_);
new MyDialogHandler().confirm(getActivity(), getString(R.string.like_confirm), ms, getString(R.string.no), getString(R.string.yes), new Runnable() {
#Override
public void run() {
new PostLikeTask(button_like, data, !data.isLiked()).execute();
// data.setLiked(!data.isLiked());
}
}, null);
}
break;
default:
break;
}
}
// Setup view data
private void initViews() {
iv_image.setOnClickListener(this);
button_like.setOnClickListener(this);
if (this.data != null) {
AppUtils.Log("Current: //////// " + (data.toString().equals(AppConfig.getInstance().todayData.get(page))));
// if (AppConfig.getInstance().appUser != null &&
// AppConfig.getInstance().isLoggedIn)
// new GetLikedTask().execute();
// else
button_like.setData(data.getLikes(), data.isLiked());
// ImageLoader.getInstance().displayImage(AppConfig.SERVER_IMAGE_URL
// + this.data.getImage(), iv_image);
// onLoading(true);
UrlImageViewHelper.setUrlDrawable(iv_image, AppConfig.SERVER_IMAGE_URL + this.data.getImage());
}
}
}

Categories

Resources