I try to create dynamic ViewPager content output. I complete this with hardcoded data, but can't with variable.
First I create activity and get data from previous activity and resources.
public class result extends FragmentActivity {
private MyAdapter mAdapter;
private ViewPager mPager;
int[] uan;
String [] categoryQuest;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_result);
mAdapter = new MyAdapter (getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
Resources testCat = getResources();
categoryQuest = testCat.getStringArray(R.array.category);
Bundle bndResult = this.getIntent().getExtras();
uan = bndResult.getIntArray("strDef");
}
Secondary, I try to get uan array data and put it into fragment.
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter (FragmentManager fm) {
super (fm);
}
#Override
public int getCount() {
return 2;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new detail(R.string.A1, uan[0]);
case 1:
return new detail(R.string.B1, uan[1]);
default:
return null;
}
}
Explain me, how to get access to UAN array from public void onCreate from public static class MyAdapter.
Create a method on your adapter and pass the data into it:
private int[] mUan;
public void setUan(int[] uan) {
mUan = uan;
}
Then change getItem to use mUan and populate the data in your activity's onCreate.
Related
I store information if user is logged in SharedPreferences.
In my ViewPager adapter I want to check if userIsLoggedIn. Depending on that I will display different fragments.
My question is: in order to get data from sharedPreferences I need a context. How do I get it from my ViewPager adapter class?
Thank you.
public class ViewPageAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private FragmentManager mFragmentManager;
public ViewPageAdapter(#NonNull FragmentManager fm) {
super(fm);
mFragmentManager = fm;
}
#NonNull
#Override
public Fragment getItem(int position) {
switch (position){
case 0: return new FragmentInsta();
case 1: return new FragmentList();
case 2: return new FragmentHostUnlogged();
default:
return new FragmentList();
}
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public int getItemPosition(#NonNull Object object) {
if (object instanceof FragmentHostUnlogged)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
}
You should use FragmentStatePagerAdapter . FragmentPagerAdapter is deprecated.
class ViewPageAdapter extends FragmentStatePagerAdapter {
Context context;
ViewPagerAdapter(FragmentManager manager,Context c) {
super(manager,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
context=c;
}
Read Context from official guideline.
Interface to global information about an application environment. This
is an abstract class whose implementation is provided by the Android
system.
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), getApplicationContext());
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)
}
I am a java-illiterate, and still trying to develop a app for my personal use.
I have started with android-studio's "Tabbed-Activity", and mostly unaltered except a fragment and a bundle in MainActivity.
Here are my codes:
MainActivity
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private static final String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//FloatingActionButton fab = findViewById(R.id.fab);
Bundle bundle = new Bundle();
bundle.putDouble("loclat", 25.4358);
bundle.putDouble("loclang",81.8463);
Fragment SunFragment = new SunFragment();
SunFragment.setArguments(bundle);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
}
}
SectionsPagerAdapter
public class SectionsPagerAdapter extends FragmentPagerAdapter {
#StringRes
private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2, R.string.tab_text_3};
private final Context mContext;
public SectionsPagerAdapter(Context context, FragmentManager fm) {
super(fm);
mContext = context;
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
//return PlaceholderFragment.newInstance(position + 1);
switch (position) {
case 0:
return new SunFragment();
//return PlaceholderFragment.newInstance(pos + 5);
case 1:
return PlaceholderFragment.newInstance(1);
//return SecondFragment.newInstance();
//return PlaceholderFragment.newInstance(pos + 1);
default:
return PlaceholderFragment.newInstance(2);
}
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(TAB_TITLES[position]);
}
#Override
public int getCount() {
// Show 2 total pages.
return 3;
}
}
And finally, The SunFragment, where I want my bundled data from MainActivity:
public class SunFragment extends Fragment {
List<SunSession> sunsList;
Typeface sunfont;
Double Dlat;
Double Dlang;
//to be called by the MainActivity
public SunFragment() {
// Required empty public constructor
}
private static final String KEY_LOCATION_NAME = "location_name";
public String TAG ="SunFragment";
public String location;//="No location name found";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retrieve location and camera position from saved instance state.
if (savedInstanceState != null) {
location = savedInstanceState.getCharSequence(KEY_LOCATION_NAME).toString();
System.out.println("OnCreate location "+location);
// Dlat = getArguments().getDouble("loclat");
//Dlang = getArguments().getDouble("loclang");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_sun, container, false);
//onSaveInstanceState(new Bundle());
if (getArguments() != null) {
Dlat = getArguments().getDouble("loclat");
Dlang = getArguments().getDouble("loclang");
} else {
Dlat=23.1;
Dlang=79.9864;
}
Log.e("Lat", Double.toString(Dlat));
I have followed this blog to make it, but no value is passed. Kindly help.
NB. This is a better described, and detailed question of my earlier question, which I understand, needs more code to be shown.
In your MainActivity, your sunFragment is unused. Remove this part:
/*Bundle bundle = new Bundle();
bundle.putDouble("loclat", 25.4358);
bundle.putDouble("loclang",81.8463);
Fragment SunFragment = new SunFragment();
SunFragment.setArguments(bundle);*/
You have to set bundle to fragment inside your SectionsPagerAdapter
case 0:
Bundle bundle = new Bundle();
bundle.putDouble("loclat", 25.4358);
bundle.putDouble("loclang",81.8463);
Fragment sunFragment = new SunFragment();
sunFragment.setArguments(bundle);
return sunFragment;
But if you need to set the bundle to fragment from MainActivity. Then use a callback in that purpose.
This way you can pass data from your mainActivity to fragment
MainActivity onCreate method
adapter = new Adapter(getChildFragmentManager());
SquadsTeamListFragment teamA =
SquadsTeamListFragment.newInstance(teamAData,teamBData
adapter.addFragment(teamA, teamAName);
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
use this adapter in your main activity
static class Adapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
Adapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position){
return mFragmentTitleList.get(position);
}
}
receive data to fragment (SquadsTeamListFragment .java)
public static SquadsTeamListFragment newInstance(String playerList, String
oppPlayerList) {
SquadsTeamListFragment fragment = new SquadsTeamListFragment();
Bundle args = new Bundle();
args.putString(ARG_TEAM_DATA, playerList);
args.putString(ARG_OPP_TEAM_DATA, oppPlayerList);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
playerList = getArguments().getString(ARG_TEAM_DATA);
oppPlayerList = getArguments().getString(ARG_OPP_TEAM_DATA);
}
}
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).
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!