TabLayout shows a blank fragment, the second time - java

In my Android application I'm using a TabLayout with a FragmentStatePagerAdapter to show dynamically generated fragments.
It shows correctly the fragments the first time, but when going back to some that are already viewed and are not the adjacent ones it shows a blank page...
I've looked into this and there seems to be two way of fixing this:
Using FragmentStatePagerAdapter instead of FragmentPagerAdapter, which I already did and it didn't change anything.
Change
adapter = new FragmentStatePagerAdapter(*getSupportFragmentManager*());
into:
adapter = new FragmentStatePagerAdapter(*getChildFragmentManager*());
that I can't do because I'm in AppCompatActivity and this method is in Fragment...
Is there a way to solve this issue without extending this activity out of FragmentActivity that would screw up other stuff in my project?
Code:
private class SubsPagerAdapter extends FragmentStatePagerAdapter {
private final ArrayList<Fragment> fragmentsList = new ArrayList<>();
private final ArrayList<String> titleList = new ArrayList<>();
SubsPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return fragmentsList.get(position);
}
#Override
public int getCount() {
return fragmentsList.size();
}
void addFragment(Fragment fragment, String title) {
fragmentsList.add(fragment);
titleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
}
#Override
public Parcelable saveState() {
return null;
}
}
Implementation:
subsPager = (ViewPager) findViewById(R.id.subsPager);
tabLayout = (TabLayout) findViewById(R.id.tabs_view);
subsPager.setAdapter(adapter);
tabLayout.setupWithViewPager(subsPager)
Edit - Fragment Code:
public class PostsListFragment extends Fragment {
private ArrayList<CustomPost> posts;
private Sorting sorting;
private String name;
private RecyclerView recyclerView;
private SubPostsAdapter adapter;
private LinearLayoutManager linearLayoutManager;
private Fetcher fetcher
public PostsListFragment() {
this.posts = new ArrayList<>();
}
public static Fragment newInstance(String name) {
PostsListFragment pf = new PostsListFragment();
pf.name = name;
return pf;
}
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
recyclerView = (RecyclerView) inflater.inflate(R.layout.posts_list_holder, container, false);
//default sorting
this.sorting = Sorting.HOT;
this.fetcher = new Fetcher(name);
loadItems();
return recyclerView;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
}
private void loadItems() {
if (posts.size() == 0) {
new Thread() {
#Override
public void run() {
posts.addAll(fetcher.fetchPosts(sorting));
new Thread() {
#Override
public void run() {
linearLayoutManager = new LinearLayoutManager(recyclerView.getContext());
adapter = new SubPostsAdapter(posts, getActivity());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
});
}
}.start();
}
}.start();
}
}
}

It was more stupid then it looked, removing this line:
if (posts.size() == 0) {
from loadItems in the Fragment, makes it work as intended.
Thanks to cricket_007 for the advices!

Related

Firebase data display recyclerview in the Tab Layout

I am new to android firebase. The question I have is. The first is to distribute the date from an Activity to several fragments. So far I have solved that problem. But I have a requirement to display the data in Firebase database in each Fragment. But the problem is that the application stops after entering the data in firebase.
stdAttn Activity
public class stdAttnDisplay extends AppCompatActivity {
private ViewPager pager;
private TabLayout tabs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_std_attn_display);
pager=(ViewPager) findViewById(R.id.vpager);
tabs=(TabLayout) findViewById(R.id.tablayout1);
tabs.setupWithViewPager(pager);
tabs.setTabGravity(TabLayout.GRAVITY_FILL);
tabs.setTabMode(TabLayout.MODE_SCROLLABLE);
setupViewPager(pager);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
//bundle
Bundle bundle =new Bundle();
bundle.putString("DATE","01-05-2023");
IABF_Attnfrag frag_iabf = new IABF_Attnfrag();
DABF_Attnfrag frag_dabf = new DABF_Attnfrag();
CMA_Attnfrag frag_cma = new CMA_Attnfrag();
CHARTERED_Attnfrag frag_chartered = new CHARTERED_Attnfrag();
ENGLISH_Attnfrag frag_english = new ENGLISH_Attnfrag();
AAT1_Attnfrag frag_aat1 = new AAT1_Attnfrag();
AAT2_Attnfrag frag_aat2 = new AAT2_Attnfrag();
AAT3_Attnfrag frag_aat3 = new AAT3_Attnfrag();
frag_iabf.setArguments(bundle);
frag_dabf.setArguments(bundle);
frag_cma.setArguments(bundle);
frag_chartered.setArguments(bundle);
frag_english.setArguments(bundle);
frag_aat1.setArguments(bundle);
frag_aat2.setArguments(bundle);
frag_aat3.setArguments(bundle);
adapter.addFrag(frag_iabf,"IABF");
adapter.addFrag(frag_dabf,"DABF");
adapter.addFrag(frag_cma,"CMA");
adapter.addFrag(frag_chartered,"CHARTERED");
adapter.addFrag(frag_english,"ENGLISH");
adapter.addFrag(frag_aat1,"AAT-i");
adapter.addFrag(frag_aat2,"AAT-ii");
adapter.addFrag(frag_aat3,"AAT-iii");
viewPager.setAdapter(adapter);
}
}
Fragment
public class IABF_Attnfrag extends Fragment {
String dateTxt;
private Bundle bundle;
myadapter adapter;
RecyclerView recViewiabf;
private DatabaseReference propertyRef;
public IABF_Attnfrag() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_i_a_b_f__attnfrag, container, false);
recViewiabf = (RecyclerView) view.findViewById(R.id.recViewiabf);
bundle = this.getArguments();
dateTxt = bundle.getString("DATE");
Log.e("DATE_FRAG_INBOUND",""+dateTxt);
propertyRef = FirebaseDatabase.getInstance()
.getReference()
.child("attendance")
.child("IABF")
.child(dateTxt);
propertyRef.keepSynced(true);
recViewiabf.setLayoutManager(new LinearLayoutManager(getContext()));
FirebaseRecyclerOptions<modelAttn> options =
new FirebaseRecyclerOptions.Builder<modelAttn>()
.setQuery(propertyRef, modelAttn.class)
.build();
adapter = new myadapter(options);
recViewiabf.setAdapter(adapter);
return view;
}
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
}
}
Adapter
public class myadapter extends FirebaseRecyclerAdapter<modelAttn,myadapter.myviewholder>
{
public myadapter(#NonNull FirebaseRecyclerOptions<modelAttn> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull myviewholder holder, int position, #NonNull modelAttn model) {
holder.stdFulName.setText(model.getName());
holder.stdidtv.setText(model.getSt_id());
}
#NonNull
#Override
public myviewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.attn_item,parent,false);
return new myviewholder(view);
}
public class myviewholder extends RecyclerView.ViewHolder {
TextView stdFulName, stdidtv;
public myviewholder(#NonNull View itemView) {
super(itemView);
stdidtv = itemView.findViewById(R.id.indextext);
stdFulName = itemView.findViewById(R.id.nametext);
}
}
}
Model Attn
public class modelAttn {
String Name, st_id;
public modelAttn() {
}
public modelAttn(String name, String st_id) {
this.Name = name;
this.st_id = st_id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getSt_id() {
return st_id;
}
public void setSt_id(String st_id) {
this.st_id = st_id;
}
}
FireBase Database Screenshot
Database Screenshot
What I need is to distribute the data from the Activity to the Fragment and then display the relevant data in the RecyclerView using the data in the Firebase database.
If you create a regular adapter then pass it a list of custom objects which you retrieve from firebase. You can make a static list on your MainActivity that can be referenced anywhere including in the fragment.

FragmentStateAdapter removing last tab instead of current ViewPager2

ViewPager2 is removing last tab instead of current tab, same problem was occurring when i was using viewpager.
Also when selected tab is last one it removes last tab correctly
dependency
implementation "androidx.viewpager2:viewpager2:1.0.0"
This is main activity code
MainActivity.java
public class MainActivity extends AppCompatActivity {
TabLayout tabLayout;
ViewPager2 viewPager;
ViewPagerAdapter adapter;
Button btnAddTab,btnRemoveCurrentTab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.view_pager);
tabLayout = findViewById(R.id.tabs);
btnAddTab=findViewById(R.id.btn_add);
btnRemoveCurrentTab=findViewById(R.id.btn_remove);
adapter=new ViewPagerAdapter(this);
viewPager.setAdapter(adapter);
new TabLayoutMediator(tabLayout, viewPager,
new TabLayoutMediator.TabConfigurationStrategy() {
#Override public void onConfigureTab(#NonNull TabLayout.Tab tab, int position) {
tab.setText("Store " + (position + 1));
}
}).attach();
btnRemoveCurrentTab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
adapter.removeFrag(tabLayout.getSelectedTabPosition());
}
});
btnAddTab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DynamicFragment fView = new DynamicFragment();
adapter.addFrag(fView,"Store "+(adapter.getmFragmentIdList().contains(adapter.getItemCount()+1)?adapter.getItemCount()+2:adapter.getItemCount()+1),(adapter.getmFragmentIdList().contains(adapter.getItemCount()+1)?adapter.getItemCount()+2:adapter.getItemCount()+1));
}
});
}
}
ViewPagerAdapter
This is FragentStateAdapter
public class ViewPagerAdapter extends FragmentStateAdapter {
private final List<DynamicFragment> mFragmentList = new ArrayList<>();
private final List<Integer> mFragmentIdList = new ArrayList<>();
public ViewPagerAdapter(#NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
#NonNull #Override public Fragment createFragment(int position) {
return DynamicFragment.newInstance(String.valueOf(position),mFragmentIdList.get(position));
}
public void addFrag(DynamicFragment fragment, String title, int id) {
mFragmentList.add(fragment);
Log.e("fragment id " ,""+id);
Log.e("fragment array " ,title+ mFragmentList.size());
mFragmentIdList.add(id);
notifyDataSetChanged();
}
public void removeFrag(int pos) {
Log.e("deleted frag",mFragmentIdList.get(pos)+" "+pos);
mFragmentList.remove(pos);
mFragmentIdList.remove(pos);
notifyDataSetChanged();
}
public List<Integer> getmFragmentIdList() {
return mFragmentIdList;
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#Override public int getItemCount() {
return mFragmentList.size();
}
}
Fragment
This is my dynamic fragment which i want to add and remove
public class DynamicFragment extends Fragment {
private static final String ARG_SECTION_NUMBER = "section_number";
private static final String ARG_ID = "id";
private String sectionNumber;
int id;
TextView textView;
EditText editText1,editText2;
public DynamicFragment() {
// Required empty public constructor
}
public static DynamicFragment newInstance(String sectionNumber, int id) {
DynamicFragment fragment = new DynamicFragment();
Bundle args = new Bundle();
args.putString(ARG_SECTION_NUMBER, sectionNumber);
args.putInt(ARG_ID, id);
fragment.setArguments(args);
Log.e("StoreDynamic","dynamic new instance");
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView= inflater.inflate(R.layout.fragment_dynamic, container, false);
textView=rootView.findViewById(R.id.textView);
editText1=rootView.findViewById(R.id.et1);
editText2=rootView.findViewById(R.id.et2);
sectionNumber = getArguments().getString(ARG_SECTION_NUMBER);
id=getArguments().getInt(ARG_ID);
editText1.setText(id+" "+sectionNumber);
textView.setText(id+" "+sectionNumber);
return rootView;
}
}
I ran into the same problem and the solution to this question: remove fragment in viewPager2 use FragmentStateAdapter, but still display worked for me. Mostly the part about overriding the getItemId function to return something other than the position to identify the item because the item's position changes when adding and removing fragments.
You need to overwrite the methods getItemId() and containsItemId()
as shown in this post: https://stackoverflow.com/a/57944197/870242
items variable holds your fragment (mFragmentList )
private val pageIds= items.map { it.hashCode().toLong() }
override fun getItemId(position: Int): Long {
return items[position].hashCode().toLong() // make sure notifyDataSetChanged() works
}
override fun containsItem(itemId: Long): Boolean {
return pageIds.contains(itemId)
}

Android fragment won't load data until second time the tab is clicked

I am developing an Android app to track coffee consumption and I have 5 tabs as seen in the attached images. In one of the tabs(fragment), I have a viewPager that is used to display data in pie charts. I am having a strange issue where the data only is display when I click on the fragment the second time. Initially, it looks like this Pie chart on the first click. When I click on the second time, it adds the information. Piechart on the second click. The package used to display data is https://github.com/PhilJay/MPAndroidChart.
I am not sure exactly where this is going wrong but I have attached the code for the TrackingFragment and some of the CostFragment(fragment inside fragment). The data is fetched from parse server client but I am fairly certain that the problem is with the initialization of the fragments.
Appreciate any help.
TrackingFragment
public class TrackingFragment extends Fragment {
private static ArrayList<String> drinkNames;
private static String TAG = "TrackingFragment";
CaffeineFragment caffeineFragment;
CostFragment costFragment;
QuantityFragment quantityFragment;
public TrackingFragment() {
// Required empty public constructor
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
costFragment = new CostFragment();
caffeineFragment = new CaffeineFragment();
quantityFragment = new QuantityFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_tracking, container, false);
ViewPager pager = view.findViewById(R.id.viewPager);
initViewPager(pager);
TabLayout tabLayout = view.findViewById(R.id.tabLayout);
tabLayout.setupWithViewPager(pager);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setTabMode(TabLayout.MODE_FIXED);
return view;
}
private void initViewPager(ViewPager pager) {
FragmentAdapter adapter = new FragmentAdapter(getChildFragmentManager());
adapter.addFragment(costFragment, "Price");
adapter.addFragment(caffeineFragment, "Caffeine");
adapter.addFragment(quantityFragment, "Quantity");
pager.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
static class FragmentAdapter extends FragmentPagerAdapter{
private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> fragmentTitleList = new ArrayList<>();
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public int getCount() {
return fragmentList.size();
}
public void addFragment(Fragment frag, String title){
fragmentList.add(frag);
fragmentTitleList.add(title);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
}
}
CostFragment
public class CostFragment extends Fragment {
private static final String TAG = "CostFragment";
public static ArrayList<Double> drinksPrice = new ArrayList<>();
public static ArrayList<String> drinksName = new ArrayList<>();
private PieChart piechart;
public static ArrayList<String> dates = new ArrayList<>();
private BarChart barchart;
private ArrayList<PieEntry> pieEntries = new ArrayList<>();
private ArrayList<BarEntry> barEntries = new ArrayList<>();
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tracking_layout_fragment, container, false);
return view;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate: " + drinksPrice.toString());
getPieData();
getBarData();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
piechart = view.findViewById(R.id.pieChart);
barchart = view.findViewById(R.id.barChart);
piechart.setRotationEnabled(true);
getParseInfo();
addBarData();
addPieData();
}
getParseInfo() and addPieData()
private void getParseInfo() {
drinksName.clear();
drinksSize.clear();
dates.clear();
ParseQuery parseQuery = ParseQuery.getQuery(Drink.class);
parseQuery.whereEqualTo("username", ParseUser.getCurrentUser().getUsername());
parseQuery.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> objects, ParseException e) {
if (e == null) {
for (ParseObject object : objects) {
drinksSize.add(object.getInt("size"));
drinksName.add(object.getString("itemName"));
dates.add(getDate(object.getCreatedAt()));
}
}else{
Log.i(TAG, "exception: " + e.getMessage());
}
}
});
}
private void addPieData() {
PieDataSet pieDataSet = new PieDataSet(pieEntries, "Each item volume (ml)");
pieDataSet.setSliceSpace(1);
pieDataSet.setValueTextSize(14);
pieDataSet.setColors(getColours());
PieData data = new PieData((pieDataSet));
piechart.setData(data);
piechart.animateY(1000);
piechart.setExtraRightOffset(10f);
Legend legend = piechart.getLegend();
legend.setVerticalAlignment(Legend.LegendVerticalAlignment.CENTER);
legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT);
legend.setOrientation(Legend.LegendOrientation.VERTICAL);
piechart.getDescription().setEnabled(false);
piechart.setDrawEntryLabels(false);
piechart.invalidate();
}
private void getPieData(){
for(int i = 0;i<drinksName.size();i++){
pieEntries.add(new PieEntry(drinksSize.get(i).floatValue(),drinksName.get(i)));
}
}
Move following lines from onViewCreated() to onCreateView():
piechart = view.findViewById(R.id.pieChart);
barchart = view.findViewById(R.id.barChart);
piechart.setRotationEnabled(true);
getParseInfo();
After that in your getParseInfo() after for loop add following lines as:
parseQuery.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> objects, ParseException e) {
if (e == null)
{
for (ParseObject object : objects) {
drinksSize.add(object.getInt("size"));
drinksName.add(object.getString("itemName"));
dates.add(getDate(object.getCreatedAt()));
}
getPieData();
getBarData();
}else{
Log.i(TAG, "exception: " + e.getMessage());
}
}
});
This is happening because when you call getParseInfo() that will go to background and before you get result getPieData() is called. So to avoid this apply suggested changes.

Struggling to integrate RecyclerView with ViewModel to maintain state eg on Rotate

Am unsure how to correctly use ViewModel with RecyclerView to maintain state on configuration change, eg rotation. With code as is, rotating the screen does not restore the list to the pre-rotate state, it reverts to the initial blank screen.
Here is my MainActivity with the ViewModel and RecyclerView both set up in onCreate. I'm using dummy data atm to try to get it working, see addDummyComposerData() which adds the data to the ViewModel and calls NotifyDataSetChanged() on the Adapter:
public class MainActivity extends AppCompatActivity {
private TabLayout mMainTabs;
private RecyclerView mRecyclerList;
private RecyclerView.LayoutManager mListLayoutManager;
private MyListAdapter mListAdapter;
private List<Composer> mComposerList = new ArrayList<>();
private MainActivityViewModel mViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
mViewModel.SetDataListComp(mComposerList);
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
mRecyclerList = (RecyclerView)findViewById(R.id.recycler_list);
mListLayoutManager = new LinearLayoutManager(getApplicationContext());
mRecyclerList.setLayoutManager(mListLayoutManager);
mListAdapter = new MyListAdapter(this,mViewModel.GetDataListComp());
mRecyclerList.setAdapter(mListAdapter);
mMainTabs = (TabLayout) findViewById(R.id.main_tabs);
mMainTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
switch (tab.getPosition()){
case 0://composer
addDummyComposerData();
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
mComposerList.clear();
mListAdapter.notifyDataSetChanged();
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
switch (tab.getPosition()){
case 0://composer
addDummyComposerData();
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
private void addDummyComposerData(){
mComposerList.clear();
Composer composer = new Composer("Albinoni, Tomaso (1671-1751)");
mComposerList.add(composer);
composer = new Composer("Bach, Johann Sebastian (1685-1750)");
mComposerList.add(composer);
//etc etc
mViewModel.SetDataListComp(mComposerList);
mListAdapter.notifyDataSetChanged();
}
}
Here is the ViewModel class:
public class MainActivityViewModel extends ViewModel {
private MusicQuery mCurrentQuery;
private List<Composer> mDataListComp;
public void SetCurrentQuery(MusicQuery query){
mCurrentQuery = query;
}
public MusicQuery GetCurrentQuery(){
return mCurrentQuery;
}
public void SetDataListComp (List<Composer> list){
mDataListComp = list;
}
public List<Composer> GetDataListComp(){
return mDataListComp;
}
}
And the ListAdapter:
public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.MyViewHolder> {
private List<Composer> composerList;
private int selectedPos = RecyclerView.NO_POSITION;
private MainActivity mMainActivityContext;
private MainActivityViewModel mMainActivityViewModel;
public class MyViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = "UI Output";
private String selectedText;
public TextView mComposerView;
public MyViewHolder(View v) {
super(v);
mComposerView = (TextView) itemView.findViewById(R.id.composer_name);
}
}
public MyListAdapter(Context context, List<Composer> composerList){
mMainActivityContext = (MainActivity) context;
this.composerList = composerList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View mComposerView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.composer_layout,viewGroup,false);
//mComposerView.setOnClickListener(mClickListener);
return new MyViewHolder(mComposerView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position){
Composer mComposer = composerList.get(position);
holder.mComposerView.setText(mComposer.getName());
holder.itemView.setSelected(selectedPos == position);
}
#Override
public int getItemCount(){
if(composerList != null){
return composerList.size();
} else {
return 0;
}
}
}
Should I be referencing the ViewModel from inside the Adapter?
You should remove the mViewModel.SetDataListComp(mComposerList); line inside the onCreate() method. This is resetting the list on rotation.
Some explanation:
When the screen rotates, the activity is being destroyed and recreated.
This means that the onCreate() method will be called again.
And because of this, the above line is resetting the list data of the ViewModel.
Only store the list in the ViewModel as this is where the data should be stored, and survives te rotation change. So remove the private List<Composer> mComposerList = new ArrayList<>(); from the MainActivity. There is no need to store the list in the Activity, only the ViewModel should modify it.
Solution:
As for initializing the list, we can do the same in the ViewModel (or we can initialize it in the constructor):
// Option 1: Initializing it directly
private List<Composer> mComposerList = new ArrayList<>();
// Option 2: Initializing it via the constructor
public MainActivityViewModel() {
mComposerList = new ArrayList<>();
}
Next, just modify the list when needed with the viewModel.SetDataListComp(<list>) method that you already have. It should update the list inside the view model. Now, when the activity is rotated, the viewmodel still has the list data and can pass it again to the mainactivity (with the viewModel.GetDataListComp()method).

ViewPager setCurrentItem(position) not working after orientation change

i'm facing an issue i can't seem to resolve and i'd like some help.
My app is composed by an activity containing a fragment. After the user taps on a suggestion, activity's method onSuggestionClicked(String cardId) is called and activity's content is replaced with a new PagerFragment_new.
#Override
public void onSuggestionClicked(String cardId) {
Log.d("activity","ID:\t" + cardId);
PagerFragment_new pagerFragment = PagerFragment_new.getInstance(
Injection.provideMtgDatabase(getApplicationContext()),
activityPresenter.getAllCardExpansionIds(cardId),
this);
ActivityUtils.replaceFragment(getSupportFragmentManager(), pagerFragment, R.id.MTGRecallActivity_container);
}
PagerFragment_new contains a ViewPager to display CardInfoFragment_new
public class PagerFragment_new extends MtgRecallFragment_new implements PagerFragmentContract.View {
private View fragmentView;
private ViewPager viewPager;
private PagerFragmentContract.Presenter presenter;
private ScreenSlidePageAdapter_new adapter;
private MtgDatabase database;
private String[] printingsIds;
private ScreenSlidePageAdapter_new.ActivePresenterListener listener;
public PagerFragment_new() {
//Required empty constructor
}
public static PagerFragment_new getInstance(MtgDatabase database, String[] printingsIds, ScreenSlidePageAdapter_new.ActivePresenterListener listener) {
PagerFragment_new instance = new PagerFragment_new();
instance.setDatabase(database);
instance.setPrintingsIds(printingsIds);
instance.listener = listener;
return instance;
}
//other stuff
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
setRetainInstance(true);
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
setHasOptionsMenu(true);
fragmentView = inflater.inflate(R.layout.fragment_pager, container, false);
viewPager = (ViewPager) fragmentView.findViewById(R.id.PAGERFRAG_VIEWPAGER);
adapter = new ScreenSlidePageAdapter_new(getChildFragmentManager(), printingsIds, database, viewPager);
adapter.setListener(listener);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);
return fragmentView;
}
#Override
public void setPresenter(PagerFragmentContract.Presenter presenter) {
this.presenter = presenter;
}
}
The adapter to manage ViewPager's element's is ScreenSlidePageAdapter_new, it is responsible of instantiating CardInfoFragments.
public class ScreenSlidePageAdapter_new extends FragmentStatePagerAdapter {
private String[] printingsIds;
private String firstItemId;
private MtgDatabase database;
public ActivePresenterListener activity;
private ViewPager viewPager;
public ScreenSlidePageAdapter_new(FragmentManager fm, String[] printingsIds, MtgDatabase database, ViewPager viewPager) {
super(fm);
this.printingsIds =printingsIds;
this.database = database;
this.firstItemId = printingsIds[0];
this.viewPager = viewPager;
}
#Override
public Fragment getItem(int position) {
//todo create a bundle with cardId
Bundle bundle = new Bundle();
bundle.putString("CardID", printingsIds[position]);
bundle.putString("FirstItemId", firstItemId);
CardInfoFragment_new cardInfoFragment_new = CardInfoFragment_new.getInstance(viewPager);
cardInfoFragment_new.setArguments(bundle);
CardInfoPresenter cardInfoPresenter = new CardInfoPresenter(cardInfoFragment_new, database);
activity.setActivePresenter(cardInfoPresenter);
return cardInfoFragment_new;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
public void setListener(ActivePresenterListener listener) {
this.activity = listener;
}
#Override
public int getCount() {
return printingsIds.length;
}
public interface ActivePresenterListener {
void setActivePresenter(BasePresenter presenter);
}}
Finally, here's the code of CardInfoFragment:
public class CardInfoFragment_new extends MtgRecallFragment_new
implements CardInfoContract.View,
CardPrintingsAdapter.OnItemClickListener {
private ViewPager viewPager;
public CardInfoContract.Presenter presenter;
private View fragmentView;
private MTGCard mtgCard;
RecyclerView printingsRecyclerView;
CardPrintingsAdapter cardPrintingsAdapter;
private String firstItemId; //the first id
public CardInfoFragment_new() {
//required empty constructor
}
private void setViewPager(ViewPager viewPager) {
this.viewPager = viewPager;
}
public static CardInfoFragment_new getInstance(ViewPager viewPager) {
CardInfoFragment_new cardInfoFragment_new = new CardInfoFragment_new();
cardInfoFragment_new.setViewPager(viewPager);
return cardInfoFragment_new;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
setRetainInstance(true);
presenter.start();
String cardId = getArguments().getString("CardID");
firstItemId = getArguments().getString("FirstItemId");
mtgCard = presenter.getCardData(cardId);
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
fragmentView = inflater.inflate(R.layout.fragment_card_info_new, container, false);
setUpCardInfoView(fragmentView);
return fragmentView;
}
public void setUpCardInfoView(final View view){
//others view set-up action omitted
cardprintingsContainer = (LinearLayout) view.findViewById(R.id.CARDINFO_printingsContainer);
printingsRecyclerView = (RecyclerView)fragmentView.findViewById(R.id.CARDINFO_printings_recyclerView);
final List<String> printings = presenter.getCardPrintings(firstItemId, mtgCard.getName());
if (printings.size() < 40) {
setupPrintings(printings,printingsRecyclerView);
}
else {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout printingsRow = new LinearLayout(getContext());
printingsRow = (LinearLayout)inflater.inflate(R.layout.card_info_printings_row,printingsRow,true);
printingsRow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setupPrintings(printings, printingsRecyclerView);
}
});
ImageView printingssetImage = (ImageView)printingsRow.findViewById(R.id.CARDINFO_printingsRow_setImage);
printingssetImage.setVisibility(View.INVISIBLE);
TextView printingssetName = (TextView)printingsRow.findViewById(R.id.CARDINFO_printingsRow_setName);
printingssetName.setText("Click to see all the "+printings.size()+ " versions");
cardprintingsContainer.addView(printingsRow);
}
}
public void setupPrintings(List<String> printings, RecyclerView recyclerView) {
cardPrintingsAdapter =
new CardPrintingsAdapter(getContext(), printings);
cardPrintingsAdapter.setOnItemClickListener(this);
UnscrollableLayoutManager layoutManager = new UnscrollableLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
recyclerView.setAdapter(cardPrintingsAdapter);
recyclerView.setLayoutManager(layoutManager);
}
#Override
public void onItemClick(View itemView, int position) {
int i = viewPager.getAdapter().getCount();
viewPager.setCurrentItem(position);
}
#Override
public void onDestroy() {
presenter = null;
mtgCard = null;
super.onDestroy();
}}
When user click on a RecyclerView item, CardInfoFragment's method onItemClick(View itemView, int position) is called and the viewpager is set to current position. Everything as expected.
THE PROBLEM
When the orientation changes, fragments are able to retain their states, but viewPager.setCurrentItem(position) does nothing, and i'm not able to change displayed fragment.
I already checked:
ViewPager instance and adapters instances corresponds to those passed to Fragments
onItemClick is fired
this questions question1 question2
If i use
android:configChanges="orientation|screenSize"
in manifest it works even after config changes, but i don't want to use this option since is discouraged.
Thanks in advance!
By default, when certain key configuration changes happen on Android (a common example is an orientation change), Android fully restarts the running Activity to help it adjust to such changes.
When you define android:configChanges="keyboardHidden|orientation" in your AndroidManifest, you are telling Android: "Please don't do the default reset when the keyboard is pulled out, or the phone is rotated; I want to handle this myself. Yes, I know what I'm doing".
So use configChanges.
The use of this is discouraged, IF you don't know how to use it. But for a case like this, I would suggest you use it. IF not, still the activity will be recreated. which just takes time, resources, and I don't see why you would want that to happen.

Categories

Resources