I try to use AsyncTaskLoader in Fragment that is a child of ViewPager. Below a code of my DayFragment:
public class DayFragment extends Fragment
implements LoaderManager.LoaderCallbacks<DayAdapter.DayItem[]> {
private static final int CONTENT_LOADER = 0;
private DayAdapter mAdapter = null;
private int mWeekNumber = 1;
private int mDayCode = 1;
private Table.Timetable mTimetable;
private RecyclerView mRVContent;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final MainActivity mainActivity = (MainActivity) getActivity();
View v = inflater.inflate(R.layout.content, container, false);
mRVContent = (RecyclerView) v.findViewById(R.id.rvContent);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(mainActivity);
mRVContent.setLayoutManager(layoutManager);
mAdapter = new DayAdapter(getActivity());
mRVContent.setAdapter(mAdapter);
//Initialize the cursor loader
getLoaderManager().initLoader(CONTENT_LOADER, null, this).forceLoad();
return v;
}
#Override
public Loader<DayAdapter.DayItem[]> onCreateLoader(final int id, Bundle args) {
if(CONTENT_LOADER == id) {
return new ContentLoader(getContext(), mWeekNumber, mDayCode, mTimetable);
}
return null;
}
#Override
public void onLoadFinished(Loader loader, DayAdapter.DayItem[] items) {
if(CONTENT_LOADER == loader.getId()) {
mAdapter.setIs24HourFormat(SettingsManager.is24HourFormat(getContext()));
mAdapter.clear();
for (DayAdapter.DayItem item : items) {
mAdapter.add(item);
}
mAdapter.notifyDataSetChanged();
if (items.length == 0) {
mRVContent.setBackgroundResource(R.drawable.bg_lesson_empty);
} else {
mRVContent.setBackgroundColor(0xFFFFFFFF);
}
}
}
#Override
public void onLoaderReset(Loader loader) {
}
private static final class ContentLoader extends AsyncTaskLoader<DayAdapter.DayItem[]> {
private final int mWeekNumber;
private final int mDayCode;
private final Table.Timetable mTimetable;
public ContentLoader(Context context, final int weekNumber, final int dayCode,
Table.Timetable timetable) {
super(context);
mWeekNumber = weekNumber;
mDayCode = dayCode;
mTimetable = timetable;
}
#Override
public DayAdapter.DayItem[] loadInBackground() {
DatabaseHandler db = new DatabaseHandler(getContext());
db.openReadable();
List<Table.Lesson> lessons = db.findLessons(mDayCode, mWeekNumber, mTimetable.getId());
DayAdapter.DayItem[] items = new DayAdapter.DayItem[lessons.size()];
for (int i = 0; i < items.length; ++i) {
Table.Lesson lesson = lessons.get(i);
Table.Subject subject = db.getSubject(lesson.getSubjectId());
Table.Teacher teacher = db.getTeacher(lesson.getTeacherId());
if (teacher == null) {
teacher = new Table.Teacher(""); //Empty name
}
items[i] = new DayAdapter.DayItem()
.setId(lesson.getId())
.setTitle(subject.getTitle())
.setSubtitle(teacher.getName()));
}
db.close();
return items;
}
}
}
PageAdapater
public class PageAdapter extends FragmentStatePagerAdapter {
public static final int PAGE_COUNT = 7;
private int mWeekNumber;
private final int[] mDayCodes;
private final String[] mDays;
private final DayFragment[] mFragments = new DayFragment[7];
private Table.Timetable mTimetable;
private boolean mIsRebuildMode = false;
public PageAdapter(Context context, FragmentManager fm,
Table.Timetable timetable, final int weekNumber) {
super(fm);
//Initialize class members
}
#Override
public Fragment getItem(int position) {
DayFragment dayFragment;
if (mFragments[position] == null) {
dayFragment = new DayFragment();
Bundle args = new Bundle();
args.putSerializable(Keys.TIMETABLE, mTimetable);
dayFragment.setArguments(args);
dayFragment.setWeekNumber(mWeekNumber);
dayFragment.setDayCode(mDayCodes[position]);
mFragments[position]= dayFragment;
} else {
dayFragment = mFragments[position];
}
return dayFragment;
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
//do nothing here! no call to super.restoreState(arg0, arg1);
}
public void setWeekNumber(final int weekNumber) {
mWeekNumber = weekNumber;
Arrays.fill(mFragments, null);
}
public void setIsRebuildMode(final boolean isRebuildMode) {
mIsRebuildMode = isRebuildMode;
}
#Override
public CharSequence getPageTitle(final int position) {
return mDays[position];
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public int getItemPosition(Object object) {
if(mIsRebuildMode) {
return POSITION_NONE;
}
return super.getItemPosition(object);
}
}
But it doesn't call onLoadFinished method. I checked Log output... LoaderManager calls onCreateLoader but it never calls onLoadFinished except first run (when app started and ViewPager shows a first page (Fragment)). It's all! After if I switch a page to a next and a next and return to a first page LoaderManager doesn't call onLoadFinished for the first page too. But it creates loader calling onCreateLoader and resets loader calling onLoaderReset. Is it a joke from Google?
See fragment lifecycle. ViewPager don't re create fragment, just swipe them
-- Start ViewPager
: create
: createView
: start
: resume
not changed if you swipe fragments
--- sleep smartphone
: pause
: stop
--- wakeup smartphone
: start
: resume
: pause
--- Close ViewPager
: stop
: detach
Related
I am using viewpager in my application and i want to make only one view Swipeable . Is it possible to do so.
I don't want the buttons at the bottom move when i swipe i only want the textview to change.
Any way can anyone help i searched a lot but couldn't find anything.
Here is my code.
There is a lot of code i can't paste all of it here.
Here is the link to the rest of it.
ViewPagerAdapter.java
public class ViewPagerAdapter extends PagerAdapter {
private Activity activity;
private Cursor cursor;
private int i = 0;
private int f = 0;
OnItemClickListener mOnItemClickListener;
public ViewPagerAdapter(Activity activity, Cursor cursor) {
this.activity = activity;
this.cursor = cursor;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
#Override
public int getCount() {
return cursor.getCount();
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return view == object;
}
#NonNull
#Override
public Object instantiateItem(#NonNull final ViewGroup container, final int position) {
final Context context = container.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.quote_detail_viewpager, container, false);
final TextView tvQuotes;
final ImageButton favouriteBtn, shareBtn, copyBtn, bgBtn, fontBtn;
tvQuotes = v.findViewById(R.id.vp_quotes);
favouriteBtn = v.findViewById(R.id.vp_fav);
shareBtn = v.findViewById(R.id.vp_share);
copyBtn = v.findViewById(R.id.vp_copy);
bgBtn = v.findViewById(R.id.vp_bg);
fontBtn = v.findViewById(R.id.vp_font);
int[] fonts = {
R.font.architects_daughter, R.font.artifika, R.font.carter_one,
R.font.expletus_sans, R.font.fredoka_one, R.font.graduate, R.font.jose,
R.font.magnolia, R.font.oswald, R.font.quicksand_bold, R.font.righteous,
R.font.salsa, R.font.schoolbell, R.font.sofadi_one
};
cursor.moveToPosition(position);
String id = cursor.getString(0);
String quote = cursor.getString(1);
String author = cursor.getString(2);
String text = quote + "\n \n - "+ author;
if (new DatabaseHelper(context).isFavourite(id)) {
favouriteBtn.setImageResource(R.drawable.ic_favorite);
}
tvQuotes.setText(text);
tvQuotes.setTypeface(PrefUtils.getTypefaceFromPrefs(context));
favouriteBtn.setOnClickListener(view ->
UtilsHelper.addOrDeleteFavourite(context, favouriteBtn, id, quote, author));
shareBtn.setOnClickListener(view -> UtilsHelper.shareTextIntent(context, text));
copyBtn.setOnClickListener(view -> UtilsHelper.copyText(context, text));
bgBtn.setOnClickListener(view -> mOnItemClickListener.onItemClick(view));
fontBtn.setOnClickListener(view -> {
int j = f++;
if (j < fonts.length) {
PrefUtils.setTypefaceFromPrefs(context, fonts[j]);
tvQuotes.setTypeface(PrefUtils.getTypefaceFromPrefs(context));
notifyDataSetChanged();
}
if (f == fonts.length) {
f = 0;
}
});
container.addView(v);
return v;
}
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_NONE;
}
#Override
public void destroyItem(ViewGroup container, int position, #NonNull Object object) {
container.removeView((View) object);
}
public interface OnItemClickListener {
void onItemClick(View view);
}
}
DetailActivity.java
public class QuoteDetailActivity extends AppCompatActivity {
// Max swipe ad interval
private final int MAX_AD_COUNT = 10;
// count swipes
private int SWIPE_COUNT = 0;
// Min swipe ad interval
private int MIN_AD_COUNT = 6;
private int i = 0;
private InterstitialAd mInterstitialAd;
ViewPagerAdapter pagerAdapter;
RelativeLayout rootLayout;
int[] backgrounds;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
Objects.requireNonNull(getSupportActionBar()).hide(); //hide the title bar
setContentView(R.layout.activity_quote_detail);
showInterstitialAd();
// String extras
String incomingActivity = getIntent().getStringExtra(Constants.STRING_EXTRA_INCOMING_ACTIVITY);
int position = getIntent().getIntExtra(Constants.STRING_EXTRA_ADAPTER_POSITION, 0);
DatabaseHelper mDBHelper = new DatabaseHelper(this);
ViewPager viewPager = findViewById(R.id.single_quote_viewpager);
backgrounds = BackgroundUtils.getAllBackgrounds();
rootLayout = findViewById(R.id.root_container);
rootLayout.setBackgroundResource(BackgroundUtils.getBackground(position));
FrameLayout adContainerView = findViewById(R.id.ad_view_container);
adContainerView.post(() ->
AdHelper.loadBanner(this, adContainerView, viewPager));
assert incomingActivity != null;
if (incomingActivity.contains(Constants.ACTIVITY_QUOTES)) {
pagerAdapter = new ViewPagerAdapter(this, mDBHelper.getAllQuotes());
} else if (incomingActivity.contains(Constants.ACTIVITY_FAVOURITE)) {
pagerAdapter = new ViewPagerAdapter(this, mDBHelper.getFavourites());
}
viewPager.setAdapter(pagerAdapter);
viewPager.setCurrentItem(position); // Setup recycleView Item position
pagerAdapter.setOnItemClickListener(new ViewPagerAdapter.OnItemClickListener() {
#Override
public void onItemClick(View view) {
changeBackground();
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
int count = SWIPE_COUNT++;
if (count >= MIN_AD_COUNT) {
if (mInterstitialAd != null) {
mInterstitialAd.show(QuoteDetailActivity.this);
SWIPE_COUNT = 0;
}
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void changeBackground() {
int j = i++;
if (j < backgrounds.length) {
rootLayout.setBackgroundResource(backgrounds[j]);
}
if (i == backgrounds.length) {
i = 0;
}
}
private void showInterstitialAd() {
MobileAds.initialize(this, initializationStatus -> {
});
AdRequest adRequest = new AdRequest.Builder().build();
InterstitialAd.load(this, getString(R.string.interstitial_ad_unit),
adRequest, new InterstitialAdLoadCallback() {
#Override
public void onAdLoaded(#NonNull InterstitialAd interstitialAd) {
// The mInterstitialAd reference will be null until
// an ad is loaded.
mInterstitialAd = interstitialAd;
mInterstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() {
#Override
public void onAdDismissedFullScreenContent() {
// Called when fullscreen content is dismissed.
if (MIN_AD_COUNT < MAX_AD_COUNT) {
MIN_AD_COUNT++;
}
}
#Override
public void onAdShowedFullScreenContent() {
// Called when fullscreen content is shown.
// Make sure to set your reference to null so you don't
// show it a second time.
mInterstitialAd = null;
}
});
}
#Override
public void onAdFailedToLoad(#NonNull LoadAdError loadAdError) {
// Handle the error
mInterstitialAd = null;
}
});
}
}
Sure, something like the below pseudo_xml
<Parent View> -- background set to image
<ViewPager> -- only this part is swipable
<View Containing Buttons Fixed To The Bottom Of Parent/>
</Parent View>
I am working on a RecyclerView which must be Draggable & swipeable. Everything works perfect.
The Data is getting Fetched in one class called ExerciseDataProvider & the RV code is another Fragment RecyclerListViewFragment.
The problem is that i can't notify Data changed from the FetchExercise on postExecute method. So the Data's are not getting populated in the RV.
Please Guide me in a Right Direction.
ACTIVITY
public class DraggableSwipeableExampleActivity extends AppCompatActivity {
private static final String FRAGMENT_TAG_DATA_PROVIDER = "data provider";
private static final String FRAGMENT_LIST_VIEW = "list view";
private static final String FRAGMENT_TAG_ITEM_PINNED_DIALOG = "item pinned dialog";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(new ExampleDataProviderFragment(), FRAGMENT_TAG_DATA_PROVIDER)
.commit();
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new RecyclerListViewFragment(), FRAGMENT_LIST_VIEW)
.commit();
}
}
public AbstractDataProvider getDataProvider() {
final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DATA_PROVIDER);
return ((ExampleDataProviderFragment) fragment).getDataProvider();
}
DATA PROVIDER
public class ExerciseDataProvider extends AbstractDataProvider {
private List<ConcreteData> mData;
private ConcreteData mLastRemovedData;
private int mLastRemovedPosition = -1;
public ExerciseDataProvider() {
new FetchExercise().execute();
mData = new LinkedList<>();
}
class FetchExercise extends AsyncTask<Void,Void,Void> {
#Override
protected Void doInBackground(Void... params) {
final int viewType = 0;
final int swipeReaction = RecyclerViewSwipeManager.REACTION_CAN_SWIPE_UP | RecyclerViewSwipeManager.REACTION_CAN_SWIPE_DOWN;
String url = "https://gist.githubusercontent.com/fake/cb9aa5494e7ee36ac3ca/raw/a4abfd19368063/exercise.JSON";
Log.d("Path", url);
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
String jsonData = response.body().string();
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < jsonArray.length(); i++) {
final long id = i;
JSONObject jsonObject = jsonArray.getJSONObject(i);
String exercise_name = jsonObject.getString("name");
int exercise_duration = jsonObject.getInt("duration");
mData.add(new ConcreteData(id, viewType, exercise_name, exercise_duration, swipeReaction));
Log.d("exercise_name", exercise_name);
}
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
#Override
public int getCount() {
return mData.size();
}
#Override
public Data getItem(int index) {
if (index < 0 || index >= getCount()) {
throw new IndexOutOfBoundsException("index = " + index);
}
return mData.get(index);
}
#Override
public int undoLastRemoval() {
if (mLastRemovedData != null) {
int insertedPosition;
if (mLastRemovedPosition >= 0 && mLastRemovedPosition < mData.size()) {
insertedPosition = mLastRemovedPosition;
} else {
insertedPosition = mData.size();
}
mData.add(insertedPosition, mLastRemovedData);
mLastRemovedData = null;
mLastRemovedPosition = -1;
return insertedPosition;
} else {
return -1;
}
}
#Override
public void moveItem(int fromPosition, int toPosition) {
if (fromPosition == toPosition) {
return;
}
final ConcreteData item = mData.remove(fromPosition);
mData.add(toPosition, item);
mLastRemovedPosition = -1;
}
#Override
public void removeItem(int position) {
//noinspection UnnecessaryLocalVariable
final ConcreteData removedItem = mData.remove(position);
mLastRemovedData = removedItem;
mLastRemovedPosition = position;
}
public static final class ConcreteData extends Data {
private final long mId;
private final String mText;
private final int mViewType;
private final int mDuration;
private boolean mPinned;
ConcreteData(long id, int viewType, String text, int duration, int swipeReaction) {
mId = id;
mViewType = viewType;
mText = text;
mDuration = duration;
}
#Override
public int getViewType() {
return mViewType;
}
#Override
public int getDuration() {
return mDuration;
}
#Override
public long getId() {
return mId;
}
#Override
public String toString() {
return mText;
}
#Override
public String getText() {
return mText;
}
#Override
public boolean isPinned() {
return mPinned;
}
#Override
public void setPinned(boolean pinned) {
mPinned = pinned;
}
}
}
RecyclerListViewFragment
public class RecyclerListViewFragment extends Fragment {
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView.Adapter mAdapter;
private RecyclerView.Adapter mWrappedAdapter;
private RecyclerViewDragDropManager mRecyclerViewDragDropManager;
private RecyclerViewSwipeManager mRecyclerViewSwipeManager;
private RecyclerViewTouchActionGuardManager mRecyclerViewTouchActionGuardManager;
public RecyclerListViewFragment() {
super();
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recycler_list_view, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//noinspection ConstantConditions
mRecyclerView = (RecyclerView) getView().findViewById(R.id.recycler_view);
mLayoutManager = new LinearLayoutManager(getContext());
// touch guard manager (this class is required to suppress scrolling while swipe-dismiss animation is running)
mRecyclerViewTouchActionGuardManager = new RecyclerViewTouchActionGuardManager();
mRecyclerViewTouchActionGuardManager.setInterceptVerticalScrollingWhileAnimationRunning(true);
mRecyclerViewTouchActionGuardManager.setEnabled(true);
// drag & drop manager
mRecyclerViewDragDropManager = new RecyclerViewDragDropManager();
mRecyclerViewDragDropManager.setDraggingItemShadowDrawable(
(NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z3));
// swipe manager
mRecyclerViewSwipeManager = new RecyclerViewSwipeManager();
//adapter
final MyDraggableSwipeableItemAdapter myItemAdapter = new MyDraggableSwipeableItemAdapter(getDataProvider());
myItemAdapter.setEventListener(new MyDraggableSwipeableItemAdapter.EventListener() {
#Override
public void onItemRemoved(int position) {
((DraggableSwipeableExampleActivity) getActivity()).onItemRemoved(position);
}
#Override
public void onItemViewClicked(View v, boolean pinned) {
onItemViewClick(v, pinned);
}
});
mAdapter = myItemAdapter;
mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(myItemAdapter); // wrap for dragging
mWrappedAdapter = mRecyclerViewSwipeManager.createWrappedAdapter(mWrappedAdapter); // wrap for swiping
final GeneralItemAnimator animator = new SwipeDismissItemAnimator();
animator.setSupportsChangeAnimations(false);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mWrappedAdapter); // requires *wrapped* adapter
mRecyclerView.setItemAnimator(animator);
// additional decorations
//noinspection StatementWithEmptyBody
if (supportsViewElevation()) {
// Lollipop or later has native drop shadow feature. ItemShadowDecorator is not required.
} else {
mRecyclerView.addItemDecoration(new ItemShadowDecorator((NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.material_shadow_z1)));
}
mRecyclerView.addItemDecoration(new SimpleListDividerDecorator(ContextCompat.getDrawable(getContext(), R.drawable.list_divider_h), true));
mRecyclerViewTouchActionGuardManager.attachRecyclerView(mRecyclerView);
mRecyclerViewSwipeManager.attachRecyclerView(mRecyclerView);
mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView);
}
#Override
public void onPause() {
mRecyclerViewDragDropManager.cancelDrag();
super.onPause();
}
#Override
public void onDestroyView() {
if (mRecyclerViewDragDropManager != null) {
mRecyclerViewDragDropManager.release();
mRecyclerViewDragDropManager = null;
}
if (mRecyclerViewSwipeManager != null) {
mRecyclerViewSwipeManager.release();
mRecyclerViewSwipeManager = null;
}
if (mRecyclerViewTouchActionGuardManager != null) {
mRecyclerViewTouchActionGuardManager.release();
mRecyclerViewTouchActionGuardManager = null;
}
if (mRecyclerView != null) {
mRecyclerView.setItemAnimator(null);
mRecyclerView.setAdapter(null);
mRecyclerView = null;
}
if (mWrappedAdapter != null) {
WrapperAdapterUtils.releaseAll(mWrappedAdapter);
mWrappedAdapter = null;
}
mAdapter = null;
mLayoutManager = null;
super.onDestroyView();
}
private void onItemViewClick(View v, boolean pinned) {
int position = mRecyclerView.getChildAdapterPosition(v);
if (position != RecyclerView.NO_POSITION) {
((DraggableSwipeableExampleActivity) getActivity()).onItemClicked(position);
}
}
public AbstractDataProvider getDataProvider() {
return ((DraggableSwipeableExampleActivity) getActivity()).getDataProvider();
}
public void notifyItemChanged(int position) {
mAdapter.notifyItemChanged(position);
}
public void notifyItemInserted(int position) {
mAdapter.notifyItemInserted(position);
mRecyclerView.scrollToPosition(position);
}
}
To update recyclerView from onPostExecute in a data provider class, your onPostExecute should have access to context where your recyclerView is defined.
Since your FetchExercise async task is defined inside ExerciseDataProvider class, try passing activity context to ExerciseDataProvider's constructor and then pass it on to FetchExercise async task as described here: getting context in AsyncTask
public class MyCustomTask extends AsyncTask<Void, Void, Long> {
private Context mContext;
public MyCustomTask (Context context){
mContext = context;
}
protected void onPostExecute(Long result) {
//use mContext to update recycler view
}
}
}
Use the context to update the recyclerView.
UPDATE
Step 1
Define an interface that will notify your activity of data set change inside a class that initialises your data provider class and pass activity context to constructor of data provider class.
public class ExampleDataProviderFragment extends Fragment {
private AbstractDataProvider mDataProvider;
//Define an interface that will notify your activity of data set change
public interface EventListener {
void onNotifyDataSetChanged();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//Pass activity context to ExerciseDataProvider
mDataProvider = new ExerciseDataProvider(getActivity());
}
public AbstractDataProvider getDataProvider() {
return mDataProvider;
}
}
Step 2
Add context parameter to ExerciseDataProvider's constructor and use it to notify activity that implements your interface to notify dataset change.
public class ExerciseDataProvider extends AbstractDataProvider {
private List<ConcreteData> mData;
private ConcreteData mLastRemovedData;
private int mLastRemovedPosition = -1;
//Add context parameter to constructor
public ExerciseDataProvider(Context context) {
//Pass context to async task
new FetchExercise(context).execute();
mData = new LinkedList<>();
}
class FetchExercise extends AsyncTask<Void,Void,Integer> {
Context mContext;
public FetchExercise(Context context) {
mContext = context;
}
#Override
protected Integer doInBackground(Void... params) {
...
return 1;
}
#Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
//Typecast context to interface defined above
//and notify dataset changes by calling its method
ExampleDataProviderFragment.EventListener eventListener = (ExampleDataProviderFragment.EventListener)mContext;
eventListener.onNotifyDataSetChanged();
}
}
}
Step 3
Implement above defined interface in your activity class and notify recyclerview adapter inside it
public class DraggableSwipeableExampleActivity extends AppCompatActivity
implements ExampleDataProviderFragment.EventListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
}
//implement interface method and notify recyclerview of changes
#Override
public void onNotifyDataSetChanged() {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_LIST_VIEW);
// you might need to change visibility of `mWrappedAdapter` in the fragment that defines it or create a getter for it so that you can access it here
((RecyclerListViewFragment) fragment).mWrappedAdapter.notifyDataSetChanged();
}
...
}
I think #random is correct you should be notifying your Recycle view on post execute.
#Override
protected void onPostExecute(Void aVoid) {
mRecyclerViewAdapter.notifyDataSetChanged();
super.onPostExecute(aVoid);
}
or if you have done something in your async task to add/delete something in the data set you would do:
#Override
protected void onPostExecute(Void aVoid) {
mRecyclerViewAdapter.notifyItemRemoved(itemposition); // or item added
mRecyclerViewAdapter.notifyDataSetChanged();
super.onPostExecute(aVoid);
}
Hope it helps !
Hi i got the viewpager :contains one fragment with two different views inside [A] and [B]. both views contain a list view. when I scroll , list view [A] and switch to second fragment I want list view [B] also be scrolled with the same amount. I've searched that I can set onTouchListener to the listview.please find below the code i used.
viewpager adapter
public class ViewPagerAdapter extends FragmentStatePagerAdapter implements
CurrencyScrollListener {
private String nBankName;
private List<Currency> list;
private String mycode;
private CurrencyViewPager[] mFragments = new CurrencyViewPager[2];
private int mCurrentScrollPosition;
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return super.getItemPosition(object);
}
public CurrencyViewPager getCurrencyFragment(int index) {
CurrencyViewPager pager = mFragments[index];
CurrencyViewPager other = mFragments[1-index];
if (pager == null) {
if (index == 0) {
pager = new CurrencyViewPager(list, nBankName, true, mycode,
this, 0);
} else {
pager = new CurrencyViewPager(list, nBankName, false, mycode,
this, 1);
}
mFragments[index] = pager;
}
if(other!=null && other.getCurrentPosition() != pager.getCurrentPosition()) {
pager.scrollTo( other.getCurrentPosition() );
}
return pager;
}
public ViewPagerAdapter(FragmentManager fm, List<Currency> currencyList,
String name, String code) {
super(fm);
this.list = currencyList;
this.nBankName = name;
this.mycode = code;
}
#Override
public Fragment getItem(int item) {
Log.i("ViewPagerAdapter","Currency Fragment #"+item);
return getCurrencyFragment(item);
}
#Override
public int getCount() {
return 2;
}
}
}
And single fragment containing list view
public class CurrencyViewPager extends BaseFragment {
private ListView mcurrencyListview;
private GeoCurrencyService mCurrency = GeoCurrencyService.getInstance();
ButtonPressListener buttonListener;
private CurrencyAdapter mCurrencyAdapter;
private String bankName;
private boolean checkFlag;
private ProgressBar mCurrencyProgress;
private TextView mCurrencyLoading;
private List<Currency> mList;
private String recCode;
private static int Firstposition;
private final CurrencyScrollListener listener;
private final int mListViewIndex;
// constructor
public CurrencyViewPager(List<Currency> myList, String name, boolean flag,String code, CurrencyScrollListener listener, int listViewIndex) {
super();
this.mList = myList;
this.bankName = name;
this.checkFlag = flag;
this.recCode = code;
this.listener = listener;
this.mListViewIndex = listViewIndex;
}
// interface
public interface ButtonPressListener {
public void onListItemPressed(Currency object, String name,String code);
}
// attach on listener
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
buttonListener = (ButtonPressListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement onButtonPressed");
}
}
// creating the main View.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.listbuy, container, false);
mcurrencyListview = (ListView) view.findViewById(R.id.BuyList);
mCurrencyAdapter = new CurrencyAdapter(getActivity(),
R.layout.currency_row, mList, checkFlag, getLanguage());
mcurrencyListview.setAdapter(mCurrencyAdapter);
// calling method getCurrency which take bank code as a parameter
/*getCurrency(code,null);*/
// reference to the list view of the corresponding layout
mcurrencyListview.setOnItemClickListener(new OnItemClickListener() {
// onClick on the List item
#Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
// getting Currency object according to the position
Currency currencyObject = mCurrencyAdapter.getItem(position);
// sending the object to the mainActivity
buttonListener.onListItemPressed(currencyObject, bankName,recCode);
}
});
return view;
}
}
First off, you should be using FragmentPagerAdapter instead of FragmentStatePagerAdapter (Look here for more information).
Now, to the question at hand.
Use the following to get the current position of ListView [A]
int index = mListA.getFirstVisiblePosition();
View v = mListA.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
And use this to set the position of ListView [B]:
mListB.setSelectionFromTop(index, top);
Look at this SO post for more information.
I have view pager in my app. inside a viewpager there are two fragments [1] and [2] both with list view inside [A] and [B]. when I scroll listview [A] and switch fragment [2] I want listview [B] to be scrolled with the same amount automatically. please help
here is my snippet
public class CurrencyViewPager extends BaseFragment {
private ListView mcurrencyListview;
private GeoCurrencyService mCurrency = GeoCurrencyService.getInstance();
ButtonPressListener buttonListener;
private CurrencyAdapter mCurrencyAdapter;
private String bankName;
private boolean checkFlag;
private ProgressBar mCurrencyProgress;
private TextView mCurrencyLoading;
private List<Currency> mList;
private String recCode;
private static int Firstposition;
private final CurrencyScrollListener listener;
private final int mListViewIndex;
// constructor
public CurrencyViewPager(List<Currency> myList, String name, boolean flag,String code, CurrencyScrollListener listener, int listViewIndex) {
super();
this.mList = myList;
this.bankName = name;
this.checkFlag = flag;
this.recCode = code;
this.listener = listener;
this.mListViewIndex = listViewIndex;
}
// interface
public interface ButtonPressListener {
public void onListItemPressed(Currency object, String name,String code);
}
// attach on listener
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
buttonListener = (ButtonPressListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement onButtonPressed");
}
}
// creating the main View.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
Log.i("TAG","onCreateView");
View view = inflater.inflate(R.layout.listbuy, container, false);
mcurrencyListview = (ListView) view.findViewById(R.id.BuyList);
mCurrencyAdapter = new CurrencyAdapter(getActivity(),
R.layout.currency_row, mList, checkFlag, getLanguage());
mcurrencyListview.setAdapter(mCurrencyAdapter);
mcurrencyListview.setSelection(Firstposition);
// calling method getCurrency which take bank code as a parameter
/*getCurrency(code,null);*/
// reference to the list view of the corresponding layout
mcurrencyListview.setOnItemClickListener(new OnItemClickListener() {
// onClick on the List item
#Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
// getting Currency object according to the position
Currency currencyObject = mCurrencyAdapter.getItem(position);
// sending the object to the mainActivity
buttonListener.onListItemPressed(currencyObject, bankName,recCode);
}
});
return view;
}
// method getCurrency which takes bank code as a parameter and
// sets the adapter to the list on onPostExecute.
}
and fragment adapter for viewpager
public class ViewPagerAdapter extends FragmentStatePagerAdapter implements
CurrencyScrollListener {
private String nBankName;
private List<Currency> list;
private String mycode;
private CurrencyViewPager[] mFragments = new CurrencyViewPager[2];
private int mCurrentScrollPosition;
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return super.getItemPosition(object);
}
public CurrencyViewPager getFCurrencyFragment(int index) {
CurrencyViewPager pager = mFragments[index];
if (pager == null) {
if (index == 0) {
pager = new CurrencyViewPager(list, nBankName, true, mycode,
this, 0);
} else {
pager = new CurrencyViewPager(list, nBankName, false, mycode,
this, 1);
}
mFragments[index] = pager;
}
return pager;
}
public ViewPagerAdapter(FragmentManager fm, List<Currency> currencyList,
String name, String code) {
super(fm);
this.list = currencyList;
this.nBankName = name;
this.mycode = code;
}
#Override
public Fragment getItem(int item) {
Log.i("TAG","getItem");
return getFCurrencyFragment(item);
}
#Override
public int getCount() {
return 2;
}
}
The main problem I am running into is that when I try to update my PagerAdapter with a brand new set of data, I get a FC. More specifically, I get the FC only if I remove something from the PagerAdapter's dataset.
In my Main activity I have this loader callback:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor newCursor) {
List<CycleItem> cycleItems = CursorInflator.inflateList(newCursor, CycleItem.class);
mCycleAdapter.setCycleList(cycleItems);
mIndicator.notifyDataSetChanged();
}
Here is my CycleViewPagerAdapter (mCycleAdapter is a reference to this):
public class CycleViewPagerAdapter extends FragmentPagerAdapter implements TitleProvider {
private ArrayList<CycleItem> mCycleItems;
private CyclePagerCallbacks mParent;
public CycleViewPagerAdapter(CyclePagerCallbacks parent, FragmentManager fm) {
super(fm);
mParent = parent;
mCycleItems = new ArrayList<CycleItem>();
}
public CycleViewPagerAdapter(FragmentManager fm, List<CycleItem> cycleItems) {
super(fm);
mCycleItems = (ArrayList<CycleItem>) cycleItems;
}
public void setCycleList(List<CycleItem> cycleItems) {
mCycleItems = (ArrayList<CycleItem>) cycleItems;
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
return CycleDetailFragment.newInstance(mParent, mCycleItems.get(position % mCycleItems.size()).getId());
}
public String getTitle(int position) {
return mCycleItems.get(position % mCycleItems.size()).getName();
}
#Override
public int getCount() {
if (mCycleItems != null) {
return mCycleItems.size();
} else {
return 0;
}
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
This CycleViewPagerAdapter creates and returns CycleDetailFragments when the getItem method is called. Here is that class:
public class CycleDetailFragment extends Fragment implements PaginatedFragmentProtocol {
public interface CyclePagerCallbacks {
public void removeCycle(Uri cycleUri);
}
private static final String TAG = "CycleDetailFragment";
private CyclePagerCallbacks mParent;
private long mCycleId;
private Cycle cycle;
private static final String CYCLE_ID_KEY = "cycle_id";
private TextView mCycleName;
private TextView mNumItem;
private Button mDeleteCycle;
public static CycleDetailFragment newInstance(CyclePagerCallbacks parent, long cycleId) {
CycleDetailFragment cycleDetailFragment = new CycleDetailFragment(parent);
Bundle bundle = new Bundle();
bundle.putLong(CYCLE_ID_KEY, cycleId);
cycleDetailFragment.setArguments(bundle);
return cycleDetailFragment;
}
public CycleDetailFragment(CyclePagerCallbacks parent) {
mParent = parent;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle arguments = getArguments();
if (arguments != null) {
mCycleId = arguments.getLong(CYCLE_ID_KEY);
} else {
mCycleId = 0;
}
Log.i(TAG, Long.toString(mCycleId));
final Uri cycleUri = ContentUris.withAppendedId(CycleContentProvider.CONTENT_URI, mCycleId);
Cursor cursor = getActivity().getContentResolver().query(cycleUri, null, null, null, null);
final Cycle cycle = CursorInflator.inflateOne(cursor, Cycle.class);
Uri cycleItemsUri = ContentUris.withAppendedId(CycleItemContentProvider.CYCLE_ID_FIELD_CONTENT_URI, mCycleId);
Cursor cycleItemsCursor = getActivity().getContentResolver().query(cycleItemsUri, null, null, null, null);
mCycleName.setText(cycle.getName() + " " + cycle.getId());
mNumItem.setText(Integer.toString(cycleItemsCursor.getCount()));
mDeleteCycle.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getActivity().getContentResolver().delete(cycleUri, null, null);
mParent.refresh();
}
});
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i("Creating View", "Cycle ID: " + mCycleId);
View view = inflater.inflate(R.layout.cycle_detail, container, false);
mCycleName = (TextView) view.findViewById(R.id.cycle_name);
mNumItem = (TextView) view.findViewById(R.id.num_items);
mDeleteCycle = (Button) view.findViewById(R.id.delete_cycle);
return view;
}
#Override
public String getFragmentTitle() {
return cycle.getName();
}
}
So you see that the CycleDetailFragment looks in the database for information to populate the view.
Here is the problem I am having. After I DELETE a Cycle from the database, the onLoadFinished method is called and the cycleItems is populated with the correct items, but once mCycleAdapter.setCycleList(cycleItems) is called after this deletion I get my error. The onAcitivityCreated method in the CycleDetailFragment is called (not by me or my PagerAdapter - something else internal), but it is fed data that should no longer exist! The bundle that it is passed contains the cycleId of the item that I just deleted and that does not exist in the freshly pulled cycleItems list that I just created. So, it tries to pull something from the database that no longer exists and null pointer exceptions are thrown.
How can I refresh the dataset of my PagerAdapter without the ViewPager trying to use old data first?
shouldn't you use FragmentStatePagerAdapter here?