I have problem that it takes long time to load data and show only when i scroll the view pager. Several time i already used asynctask but not work. After changing tab, i called same fragment any one help me??
Here, is first time loaded data
After changing tab call same fragment
Here is my Adapter for ViewPager
public class AdapterCategoryViewPager extends FragmentPagerAdapter {
Context context;
private List<Fragment> fragmentList;
private List<Type> typeList;
public AdapterCategoryViewPager(FragmentManager fm, Context context, List<Fragment> fragmentList, List<Type> typeList) {
super(fm);
this.context = context;
this.fragmentList = fragmentList;
this.typeList = typeList;
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public CharSequence getPageTitle(int position) {
return typeList.get(position).getType().toString();
}
#Override
public int getCount() {
return fragmentList.size();
}
}
Here is my Fragment for category this function i call on async task
private void setData(View v)
{
addWomenCategory();
ll_list.removeView(v);
gridView.removeView(v);
gridView.setMode(PullToRefreshBase.Mode.PULL_FROM_END);
adapterCategory=new AdapterCategory(v.getContext(),categoryArrayList,height,width);
gridView.setAdapter(adapterCategory);
g=(GridView) gridView.getRefreshableView();
g.setHorizontalSpacing(h7);
g.setVerticalSpacing(h7);
// g.setEnabled(false);
gridView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener<GridView>() {
#Override
public void onRefresh(final PullToRefreshBase<GridView> refreshView) {
gridView.postDelayed(new Runnable() {
#Override
public void run() {
refreshView.getLoadingLayoutProxy().setRefreshingLabel("Loding Data...");
refreshView.getLoadingLayoutProxy().setReleaseLabel("Refresh complete");
addWomenCategory();
adapterCategory.notifyDataSetChanged();
gridView.setRefreshing(false);
gridView.onRefreshComplete();
}
},2000);
}
});
}
Here is my HomeFragment which actually add fragments category in array list this function i also called in asynctask
private void setData(View v)
{
addType();
pager.setOffscreenPageLimit(3);
adapterType=new AdapterType(typeArrayList,v.getContext(),height,width);
fragmentList.add(new FragmentListCategory());
fragmentList.add(new FragmentMenListCategory());
fragmentList.add(new FragmentKidListCategory());
fragmentList.add(new FragmentListCategory());
fragmentList.add(new FragmentMenListCategory());
fragmentList.add(new FragmentKidListCategory());
pager.setAdapter(new AdapterCategoryViewPager(getFragmentManager(),v.getContext(),fragmentList,typeArrayList));
tabs.setViewPager(pager);
setUpTabStrip(v);
}
I have used universal imageloader library to display image and i was initialized its object every time in adapter to solve this issue i initialize that configuration object only once in whole application in main activity's onCreate() method
As addWomenCategeory() being an asynchronous task, the code in the main thread doesn't wait for the result.
Consider moving the code
adapterCategory.notifyDataSetChanged(); gridView.setRefreshing(false); gridView.onRefreshComplete();
to addWomencategory() method, where you recieve result from your back-end
Related
I using Fragment set cameraView
There is an error this onResume
#Override
public void onResume() {
super.onResume();
if (isCameraAccessGranted() && isRecordAudioGranted() && isWriteStorageGranted()) {
cvCamera.start();
} else {
ActivityCompat.requestPermissions(getActivity(), VIDEO_PERMISSIONS, 0);
}
}
I doing Stepper 5 Page Fragment this is PagerAdapter
public class PagerAdapter extends FragmentStatePagerAdapter {
public PagerAdapter(FragmentManager supportFragmentManager, int behavior) {
super(supportFragmentManager, behavior);
}
#Override
public int getCount() {
return 6;
}
#Override
public Fragment getItem(int position) {
return new BlankFragmentTestCamera();
}
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
Please help me.
ViewPager by default keeps previous and next fragment in the resumed state. So it's like one camera opened on three screens at the same time.
Please, try to
Use only 1 fragment with camera view
Use flag FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT for the ViewPager
I've prepared a small working demo, please, take a look
https://github.com/3akat/Ziggeo_CameraView_PagerDemo
In my MainActivity, I have:
#Override
protected void onResume() {
super.onResume();
checkForCrashes();
checkForTutorial();
checkForUpdates();
setStore();
setup();
}
In setup(), I call initializeTabs() in a callback:
protected void setup() {
final Store s = getStore();
setBackground();
if (s == null) {
unauthorizedHandler();
return;
}
final Context mainActivity = this;
fragments = getTabFragments();
StoresController.getStoreDetails(s, mainActivity, new Callback<StoreDetailDecorator>() {
#Override
public void success(StoreDetailDecorator storeDetailDecorator, Response response) {
s.prettyName = storeDetailDecorator.store.pretty_name;
s.save();
Log.v(TAG, s.prettyName);
TextView toolbar_label = (TextView)findViewById(R.id.toolbar_label);
toolbar_label.setText(MainActivity.getTruncatedMenuName(s.name()));
SummaryTab t1 = (SummaryTab)fragments.get(0);
t1.notifier = (SummaryTabLoadingNotifier)mainActivity;
initializeTabs(s, fragments);
t1.populateReport();
}
}
}
public void initializeTabs(Store s, List<Fragment> fragments ) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), getTabTitles(), fragments);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setOffscreenPageLimit(2);
pager.setAdapter(adapter);
}
That last line is crashing for some customers.
java.lang.IllegalStateException: Fragment InfoTab{1bcb80f} is not currently in the FragmentManager
But not every time. Why would this happen sometimes? Something to do with the fragment not being connected? I read some things about checking isAdded() on a fragment, but that's for checking if a fragment is added to an Activity. I'm using a FragmentStatePagerAdapter :
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private CharSequence[] titles;
private List<Fragment> fragmentList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fragmentManager, CharSequence titles[], List<Fragment> fragmentList) {
super(fragmentManager);
this.titles = titles;
this.fragmentList = fragmentList;
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public CharSequence getPageTitle(int position) { return titles[position]; }
#Override
public int getCount() { return fragmentList.size(); }
#Override
public Parcelable saveState() {
return null;
}
}
Could saveState(){ return null;} be it? This is there, as I understand things, to ensure the view is refreshed every time.
onResume() is called every time your activity returns to foreground.
If one of your users, for example, presses the home button and returns to your app then getStoreDetails() will be called again and you'll create a new adapter (with the same old fragment list) when the result arrives.
You should instantiate your fragments, create an adapter and populate your viewpager with the adapter only once (onCreate() would be the spot).
When new data arrives from getStoreDetails() just update the already added fragments with the new data.
This is because you are returning null in saveState(). You shouldn't try to override this behavior.
But even if you want to do it for some reason, you have to override public void restoreState(Parcelable state, ClassLoader loader) as well to avoid crash, because that method is relying on data saved in saveState().
But I would suggest you to not override it at all, and instead explain what you wanted to achieve there.
Main goal is to update Fragment info mainly from its own class.
Main activity:
public class MainActivity extends AppCompatActivity {
final Handler GUIHandler = new Handler();
final Runnable r = new Runnable()
{
public void run()
{
updateFragments();
GUIHandler.postDelayed(this, 1000);
}
};
#Override
protected void onPause() {
super.onPause();
GUIHandler.removeCallbacks(r);
}
#Override
protected void onResume() {
super.onResume();
GUIHandler.postDelayed(r, 600);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
...
mViewPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new PagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
mViewPager.setAdapter(mPagerAdapter);
...
}
private void updateFragments() {
mPagerAdapter.updateFragments();
}
PagerAdapter:
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
private Observable mObservers = new FragmentObserver();
public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
mObservers.deleteObservers(); // Clear existing observers.
switch (position) {
case 0:
FragmentWeather weatherTab = new FragmentWeather();
weatherTab.setActivity(mActivity);
if(weatherTab instanceof Observer)
mObservers.addObserver((Observer) weatherTab);
return weatherTab;
case 1:
FragmentMemo tab2 = new FragmentMemo();
return tab2;
case 2:
FragmentHardware tab3 = new FragmentHardware();
return tab3;
default:
return null;
}
}
public void updateFragments() {
mObservers.notifyObservers();
}
}
FragmentObserver
public class FragmentObserver extends Observable {
#Override
public void notifyObservers() {
setChanged(); // Set the changed flag to true, otherwise observers won't be notified.
super.notifyObservers();
Log.d("Observer", "Sending notification");
}
}
FragmentWeather:
public class FragmentWeather extends Fragment implements Observer {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
return layout;
}
public void setTemperatures(){
Log.d("Android", "setTemperatures is called");
}
#Override
public void update(Observable observable, Object data) {
setTemperatures();
}
}
Problem now is, that PagerAdapter::getItem() doesnt get called when Fragments are created at the start of application. That means WeatherFragment dont get associated with mObservers. If I swipe to the 3rd view and then swipe back, everything is working properly. How to restructurize this to make it working?
this line:
mObservers.deleteObservers(); // Clear existing observers.
is removing all the observers, but the method getItem gets called several times, that means only the last time it calls anything stays there. REMOVE this line.
Also, the following code is a very bad pattern and it will go wrong on several occasions:
case 0:
FragmentWeather weatherTab = new FragmentWeather();
weatherTab.setActivity(mActivity);
if(weatherTab instanceof Observer)
mObservers.addObserver((Observer) weatherTab);
return weatherTab;
that's because fragments get re-created by the system when necessary, so setActivity is pointless, so as is addObserver. The moment the system needs to destroy/recreate the fragments, you'll have a memory leak of those old fragments, the old activity, and the new ones won't have the activity and won't be on the observers.
The best situation here is to rely on the natural callbacks from the fragments. An example follows (ps.: that was typed by heart, I'm sure there might be some mistakes, but you'll get the idea)
public interface ObservableGetter{
public Observable getObservable();
}
public void MyFragment extends Fragment implements Observer {
#Override onAttach(Activity activity){
super.onAtttach(activity);
if(activity instanceof ObservableGetter){
((ObservableGetter)activity).getObservable().
addObserver(this);
}
}
#Overrude onDetach(){
Activity activity = getActivity();
if(activity instanceof ObservableGetter){
((ObservableGetter)activity).getObservable().
removeObserver(this);
}
super.onDetach();
}
}
then you can just make the activity implements ObservableGetter and have the Observable on it.
Then your adapter code will be just:
case 0:
return new FragmentWeather();
all the rest of the logic uses the regular callbacks.
I hope it helps.
I must clear that I am looking for an example or answer where I can use various differnt layout designs in a viewpager and the data in all the pages would be dynamic and all pages can be interacted by the user.
My Use Case and current approach towards the problem :
So I have got 8 different types of question types and so I have created layouts for all of them. Also I the data in the views for these layouts has to be populated via java Map that has fetched data from the sqlite DB.
But a test may contain 25 questions with different layouts out of the above 8. And for all these 25 questions I want to use a Viewpager and a Fragment that will return the required layout based on the passed question type value out of my java map.
My apporach towards this :
I have created an activity and have inflated it with a viewpager layout :
R.layout.practice_pager
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/test_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
*Activity Edited code *
public class TestActivity extends FragmentData implements FragmentData{
FragmentManager manager=getSupportFragmentManager();
private ViewPager mViewPager;
private MyFragmentPagerAdapter mMyFragmentPagerAdapter;
int PAGE_COUNT = 0;
GrePracticeTestRecord p=new GrePracticeTestRecord();
private HashMap<Integer, GrePracticeResultRecord> mPracResultMap;
public static int fragmentToReturn=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.practice_pager);
mViewPager = (ViewPager) findViewById(R.id.test_container);
//This function is called to fetch the data from sqlite db and fill the map with data
LoadingTestView();
PAGE_COUNT=mPracRecordMap.size();
initPager();
}
//here I initialize the pager and set the adapter to it
public void initPager()
{
p.setQUES(mPracRecordMap.get(1).getQUES());
p.setEXPL(mPracRecordMap.get(1).getEXPL());
fragmentToReturn=Integer.parseInt(mPracRecordMap.get(1).getQTYPE());
setData(p);
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),fList);
mViewPager.setAdapter(mMyFragmentPagerAdapter);
mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
mMyFragmentPagerAdapter.notifyDataSetChanged();
p.setQUES(mPracRecordMap.get(mViewPager.getCurrentItem()+1).getQUES());
p.setEXPL(mPracRecordMap.get(mViewPager.getCurrentItem()+1).getEXPL());
setData(p);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
public void setData(GrePracticeTestRecord p) {
// TODO Auto-generated method stub
}
#Override
public GrePracticeTestRecord getData() {
// TODO Auto-generated method stub
return p;
}
}
My Adapter Edited code
public class MyFragmentPagerAdapter extends FragmentStatePagerAdapter{
private List<Fragment> fragments;
public MyFragmentPagerAdapter(FragmentManager fm,List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
/** This method will be invoked when a page is requested to create */
#Override
public Fragment getItem(int position) {
System.out.println("value of position "+position);
return this.fragments.get(position);
}
/** Returns the number of pages */
#Override
public int getCount() {
return this.fragments.size();
}
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return MyFragmentPagerAdapter.POSITION_NONE;
}
}
Interface FragmentData
public interface FragmentData {
public void setData(GrePracticeTestRecord p);
public GrePracticeTestRecord getData();
}
TestFragment Edited code
public class TestFragment extends Fragment {
AnswerEnterListener callBack;
FragmentData fD;
Button submitAnswer;
EditText userAnswer;
TextView qText,expl;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.qtype1, container, false);
}
public interface AnswerEnterListener
{
public void onInputAnswer(String ans);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
callBack=(AnswerEnterListener) activity;
fD=(FragmentData) activity;
} catch (Exception e) {
// TODO: handle exception
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
qText=(TextView) getActivity().findViewById(R.id.question_text);
expl=(TextView)getActivity().findViewById(R.id.explanation_text);
qText.setText(Html.fromHtml(fD.getData().getQUES()));
expl.setText(Html.fromHtml(fD.getData().getEXPL()));
}
}
Similar to TestFragment , I have not the other fragments too. Each for a layout type.
Issues :
The first layout is repeated two times at the first time , and also when I swipe back then the position of data is misplaced.
Is this the right approach, I have been suggested by someone that you should use three fragments and update the left and right fragments with data , but this actually bounced off me. Can anyone share a good example of it or a blog.
I must clear that I am looking for an example or answer where I can
use various differnt layout designs in a viewpager and the data in all
the pages would be dynamic and all pages can be interacted by the
user.
This should be quite easy to make but you seem to have complicated this a lot. You need to do something like this:
Get the data from the database with the LoadingTestView(); function.
Initialize the ViewPager and set it's adapter. The adapter will return the proper fragment instance in the getItem() method, based on whatever criteria you have.
In the fragments, retrieve the data from the activity which should expose the data through some method.
I've made a simple example that you can find here.
I'm having a strange issue with fragment views not refreshing properly, but it only happens after the app is closed and re-opened after some amount of time.
I have a TabSwitcherActivity which contains a ViewPager. The ViewPager switches between 3 fragments, and each fragment represents a different view of the same information. There are certain events that can happen which will cause the information to become stale, so the fragments need to be notified to refresh the view. To accomplish this, my TabSwitcherActivity has a method called notifyDataSetChanged() which will iterate the 3 fragments and tell them to refresh. Here is (I think) the most relevant code:
public class TabSwitcherActivity extends FragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Used to keep track of the child fragments of this activity so we can update their list adapters.
mFragments = new ArrayList<Fragment>();
// Set up the view pager and tab adapter.
setContentView(R.layout.pager_layout);
mViewPager = (ViewPager) super.findViewById(R.id.pager);
mFragments.add(Fragment.instantiate(this, FragmentOne.class.getName()));
mFragments.add(Fragment.instantiate(this, FragmentTwo.class.getName()));
mFragments.add(Fragment.instantiate(this, FragmentThree.class.getName()));
mViewPager.setAdapter(new ListPagerAdapter(super.getSupportFragmentManager(), mFragments));
}
public void notifyDataSetChanged() {
for (Fragment fragment : mFragments) {
if (fragment instanceof IDataSetChangedListener) {
((IDataSetChangedListener) fragment).notifyDataSetChanged();
}
}
}
}
Now, this works as intended the first time the app is launched. The problem is that after the app is closed and re-opened some time later, calling TabSwitcherActivity.notifyDataSetChanged() while the app is running does not update the fragment views (this works the first time the app is started after a reboot). This leads me to believe that there is a life cycle situation with the fragments that I'm not handling correctly. Any idea what this might be? It occurs to me that If the fragments are destroyed and recreated, they are probably not correctly stored in my mFragments array.
I'm answering my own question in case others have a similar problem to what I had. I was able to resolve the problem based on the comments from invertigo. I'll post some generified pieces of the important parts of the code.
As I suspected, the fragments stored in the array were not correctly updated if they were destroyed and re-created by the application. (This would sometimes happen when the application was closed and re-opened.) It was a little bit tricky to remove the array since I was using it to store the fragments for my tabs in my FragmentPagerAdapter implementation.
Here is my old ListPagerAdapter class:
public class ListPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragments;
public ListPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public String getPageTitle(int position) {
return ((ITitledFragment) mFragments.get(position)).getTitle();
}
}
Here is my new implementation, which removes the problem caused by the array holding bad fragment references:
public class ListPagerAdapter extends FragmentPagerAdapter {
private static final int FRAGMENT_ONE_POSITION = 0;
private static final int FRAGMENT_TWO_POSITION = 1;
private static final int FRAGMENT_THREE_POSITION = 2;
private static final int COUNT = 3;
public ListPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case FRAGMENT_ONE_POSITION:
return new FragmentOne();
case FRAGMENT_TWO_POSITION:
return new FragmentTwo();
case FRAGMENT_THREE_POSITION:
return new FragmentThree();
}
return null;
}
#Override
public int getCount() {
return COUNT;
}
#Override
public String getPageTitle(int position) {
switch (position) {
case 0:
return FragmentOne.getTitle();
case 1:
return FragmentTwo.getTitle();
case 2:
return FragmentThree.getTitle();
}
return null;
}
}
Then, the only remaining problem is how to have the my TabSwitcherActivity class update all of the fragments when they are stale. As invertigo pointed out, this can be done with tags. However, I decided to use an approach where each fragment registers itself with the activity when it is created, and unregisters itself before it is destroyed. The activity can iterate registered fragments to update them.
The Relevant code from TabSwitcherActivity:
class TabSwitcherActivity extends FragmentActivity {
private List<Fragment> mFragments;
private void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Used to keep track of the child fragments of this activity so we can update their list adapters.
mFragments = new ArrayList<Fragment>();
// Set up the view pager and tab adapter.
setContentView(R.layout.pager_layout);
mViewPager = (ViewPager) super.findViewById(R.id.pager);
mViewPager.setAdapter(new ListPagerAdapter(super.getSupportFragmentManager()));
}
public void startTrackingFragment(Fragment f) {
mFragments.add(f);
}
public void stopTrackingFragment(Fragment f) {
mFragments.remove(f);
}
public void notifyDataSetChanged() {
for (Fragment fragment : mFragments) {
if (fragment instanceof IDataSetChangedListener) {
((IDataSetChangedListener) fragment).notifyDataSetChanged();
}
}
}
}
With that, all that remains is to have the fragments register themselves for updates:
public class FragmentOne extends Fragment implements IDataSetChangedListener {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((TabSwitcherActivity) getActivity()).startTrackingFragment(this);
}
public void onDestroy() {
((TabSwitcherActivity) getActivity()).stopTrackingFragment(this);
super.onDestroy();
}
}
You could always just refresh the data in onResume() of your fragments, or call notifyDataSetChanged() in onResume() of your activity.
re: "It occurs to me that If the fragments are destroyed and recreated, they are probably not correctly stored in my mFragments array."
Instead of storing the fragments in an array, use the fragment manager to fetch the fragments by tag or id.
http://developer.android.com/guide/components/fragments.html#Managing
http://developer.android.com/reference/android/app/FragmentManager.html#findFragmentByTag(java.lang.String)