Fragment in ViewPager is not displaying anything in its RecyclerView on rotation - android

I have 4 Fragments in a ViewPager that is integrated with a TabLayout. Each of those Fragments holds a RecyclerView because I'm displaying an unknown amount of list items. The items are loaded by date, so I have two buttons that let you change days, and then the appropriate data in the list items is loaded based on the date that is set. The buttons are part of the outer activity.
Everything so far works perfectly BUT once I rotate the device the fragments don't load anything. Nothing is displayed inside the Fragments. The views in the outer activity are displayed fine though. The weird thing is that if I touch one of the buttons that changes the date, the Fragments are loaded and the data is displayed correctly. But I need everything to be displayed immediately after rotation not just when I click one of those buttons. Here is my code:
public class MainActivity extends AppCompatActivity implements DatePickerFragment.DateDialogInterface
{
private final static String COLOR_CONTROL_CHOSEN = "color_control_chosen";
private static final String DIALOG_DATE = "dialog_date";
private static final String TAG = "MainActivity";
private static final String SET_LOCATION = "set_location";
public static final String LEAGUE_POSITION = "league_position";
private Toolbar mToolbar;
private ViewPager mViewPager;
private TabLayout mTabLayout;
private ImageButton mPrevDateButton, mNextDateButton;
private TextView mDateTextView, mLeagueTextView;
private String mTodaysDate;
private Date mLastSelectedDate, mTodaysDateObj;
private ProgressBar mProgressBar;
private FragmentPagerAdapter mAdapterViewPager;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.outnix_toolbar);
mViewPager = (ViewPager) findViewById(R.id.main_activity_view_pager);
mTabLayout = (TabLayout) findViewById(R.id.main_activity_tab_layout);
mDateTextView = (TextView) findViewById(R.id.display_date_text_view);
mLeagueTextView = (TextView) findViewById(R.id.league_title_header);
mPrevDateButton = (ImageButton) findViewById(R.id.previous_date_button);
mNextDateButton = (ImageButton) findViewById(R.id.next_date_button);
mProgressBar = (ProgressBar) findViewById(R.id.loading_circle);
mProgressBar.setVisibility(View.GONE);
setTodaysDate();
mDateTextView.setText("Today");
setSupportActionBar(mToolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayShowTitleEnabled(false);
NavDrawerFragment navDrawerFrag = (NavDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_nav_drawer);
navDrawerFrag.setUp((DrawerLayout) findViewById(R.id.nav_drawer), mToolbar);
setUpViewPagerAdapterOnCreate();
mTabLayout.setupWithViewPager(mViewPager);
mDateTextView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
FragmentManager fm = getSupportFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
Bundle args = new Bundle();
args.putSerializable("ARG_DATE", mLastSelectedDate);
dialog.setArguments(args);
dialog.show(fm, DIALOG_DATE);
}
});
THESE TWO LISTENERS ARE WHAT DISPLAY THE DATA AFTER A ROTATION CHANGE
//////////////////////////////////////////////////////////////////
mPrevDateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
mProgressBar.setVisibility(View.VISIBLE);
updateDate(-1);
setupViewPagerAdapter(getViewPagerPos());
}
});
mNextDateButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mProgressBar.setVisibility(View.VISIBLE);
updateDate(1);
setupViewPagerAdapter(getViewPagerPos());
}
});
}
////////////////////////////////////////////////////////////////////
/* #Override
protected void onResume()
{
super.onResume();
setupViewPagerAdapter(getViewPagerPos());
}*/
private void setUpViewPagerAdapterOnCreate() //only called when opened from LauncherActivity
{
setupViewPagerAdapter(0);
int touchColorPixel = getIntent().getIntExtra(COLOR_CONTROL_CHOSEN, ContextCompat.getColor(this, R.color.blue));
if(touchColorPixel == ContextCompat.getColor(this, R.color.orange))
mViewPager.setCurrentItem(0);
else if(touchColorPixel == ContextCompat.getColor(this, R.color.blue))
mViewPager.setCurrentItem(1);
else
mViewPager.setCurrentItem(3);
}
private void setupViewPagerAdapter(int currentPos)
{
mAdapterViewPager = new MainPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mAdapterViewPager);
mViewPager.setOffscreenPageLimit(4);
mViewPager.setCurrentItem(currentPos);
}
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putSerializable(DIALOG_DATE, mLastSelectedDate); //save the last date selected (or last date displayed) on rotation change.
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
setDateFromDialog((Date) savedInstanceState.getSerializable(DIALOG_DATE)); //restore the last date selected to the new rotation change
}
public ProgressBar getProgressBar()
{
return mProgressBar;
}
#Override
public void setDateFromDialog(Date date)
{
String newDate = getFormattedDate(date);
updateTodaysDate();
mLastSelectedDate = date;
setupViewPagerAdapter(getViewPagerPos());
if(newDate.equals(mTodaysDate))
mDateTextView.setText("Today");
else
mDateTextView.setText(newDate);
}
private int getViewPagerPos()
{
return mViewPager.getCurrentItem();
}
}
The 2 buttons mPrevDateButton and mNextDateButtonare the buttons that when clicked "fix" the problem. Inside their click listeners, the updateDate() method is only doing stuff related to a TextView object and is why I didn't include the definitions (I'm trying to keep the post as short as possible). So I know for sure it has something to do with my setupViewPagerAdapter() method. I've tried adding this method in onCreate() and onResume() but the Fragments still stay blank.
Here is my ViewPager adapter code:
public class MainPagerAdapter extends FragmentPagerAdapter
{
private final String[] mFragmentTitleList = {"My Games", "All Games", "All Places", "My Places"}; //tab title names
private final static int NUM_FRAGMENTS = 4;
public MainPagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
switch(position)
{
case 0:
return MyGamesFragment.newInstance();
case 1:
return AllGamesFragment.newInstance();
case 2:
return AllPlacesFragment.newInstance();
case 3:
return MyPlacesFragment.newInstance();
default:
return null;
}
}
#Override
public int getCount()
{
return NUM_FRAGMENTS;
}
#Override
public CharSequence getPageTitle(int position)
{
return mFragmentTitleList[position];
}
}
Here is one of the 4 fragments. They pretty much all have similar design.
public class AllGamesFragment extends Fragment
{
private SwipeRefreshLayout mSwipeRefreshLayout;
private RecyclerView mAllGamesRecyclerView;
private GameAdapter mGameAdapter;
private List<Game> mGameList = new ArrayList<>();
private MainActivity mMainActivity; //keeps a reference to MainActivity to access public methods
public static AllGamesFragment newInstance()
{
return new AllGamesFragment();
}
#Override
public void onAttach(Context context)
{
super.onAttach(context);
mMainActivity = (MainActivity) context;
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRetainInstance(true);
mMainActivity.getProgressBar().setVisibility(View.VISIBLE);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.recycler_view_all_games, container, false);
mAllGamesRecyclerView = (RecyclerView) view.findViewById(R.id.all_games_recycler_view);
mAllGamesRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
{
#Override
public void onRefresh()
{
getGamesData();
mSwipeRefreshLayout.setRefreshing(false);
}
});
return view;
}
#Override
public void onResume()
{
super.onResume();
getGamesData();
}
private void setupAdapter()
{
if(isAdded())
{
mGameAdapter = new GameAdapter(mGameList);
mAllGamesRecyclerView.setAdapter(mGameAdapter);
}
}
private void getGamesData()
{
new GetDataTask(mMainActivity.getLastSelectedDate()).execute();
}
private class GetDataTask extends Scraper.GameDataTask
{
GetDataTask(Date date)
{
super(date, getActivity());
}
#Override
protected void onPostExecute(List<Game> games)
{
mGameList = games;
setupAdapter();
mMainActivity.getProgressBar().setVisibility(View.GONE);
}
}
private class GameAdapter extends RecyclerView.Adapter<GameHolder>
{
private List<Game> mGames;
public GameAdapter(List<Game> games)
{
mGames = games;
}
#Override
public GameHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
LayoutInflater inflater = LayoutInflater.from(getActivity());
View view = inflater.inflate(R.layout.list_game_item, parent, false);
return new GameHolder(view, getActivity());
}
#Override
public void onBindViewHolder(GameHolder holder, int position)
{
holder.bindGameData(mGames.get(position));
}
#Override
public int getItemCount()
{
return mGames.size();
}
}
I've looked at so many posts on here, changed my code many times and I still can't get it to work. I'm just baffled that it "fixes" when I click one of the date buttons. If you would like me to post more code of something let me know. Sorry for the long post but I feel the context is needed. I really appreciate any help!

I figured it out! I had to switch to a FragmentStatePagerAdapter rather than use a FragmentPagerAdapter. I believe it has something to do with the Fragments being destroyed and recreated again.
I also read that another way would be to wrap the ViewPager in a Fragment and when you create its adapter you use getChildFragmentManager() rather than getSupportFragmentManager() like this:
mAdapterViewPager = new MyPagerAdapter(getChildFragmentManager());
I didn't want to have to add an entire Fragment around my ViewPager just to fix this issue so I switched to using FragmentStatePagerAdapter and it worked! Hope this helps someone else out there.

Making viewPager adapter extends FragmentStatePagerAdapter and not FragmentPagerAdapter should solve the problem.

Related

Android TabLayout with ViewPager duplicates fragment contents when rotating

My app has a tablayout with a view pager. Each page has a fragment. There are 4 different fragments, three of them are basically the same for now (I'm in the development phase right now). One of them has a RecyclerView with a basic list.
I am implementing the Two-pane template in the fragment with the RecyclerView.
Everything seems to be works]ing well. While I move across the tabs the fragments are loaded fine.
But, when I rotate the device and tap on the first tab, and then go back to the one with the recyclerview, I can see the previous intance below. See attached images.
I decided to use static final instances of the fragments in the page adapter and in the recyclerview fragment.
How can I get rid of this problem?
Thanks in advance stackers!
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon1).setText(R.string.dashboard));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon2).setText(R.string.fragment2));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon3).setText(R.string.fragmentDualPane));
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.icon4).setText(R.string.frag4));
final ViewPager viewPager = findViewById(R.id.pager);
final PagerAdapter pageAdapter = new TabPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pageAdapter);
tabLayout.setupWithViewPager(viewPager);
} // protected void onCreate
} // public class MainActivity
TabPagerAdapter has static final intances of the fragments
public class TabPagerAdapter extends FragmentPagerAdapter {
static final Fragment tabs[] = {new DashboardFragment(),
new Fragment2(),
new ExpensesFragment(),
new Fragment4()
};
public TabPagerAdapter(#NonNull FragmentManager fm) {
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
} // public TabPagerAdapter
#NonNull
#Override
public Fragment getItem(int position) {
if (position<tabs.length)
return tabs[position];
else
return null;
} // public Fragment getItem
#Override
public int getCount() {
return this.tabs.length;
} // public int getCount
} // class TabPagerAdapter
General fragment template for dashboard, fragment2, and fragment4
public class DashboardFragment extends Fragment {
public DashboardFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_dashboard, container, false);
}
}
This is the code for the fragment with the dual pane. Look that it uses the OnItemSelected implementation of fragments communications.
This fragment loads another fragment with the recyclerview.
public class ExpensesFragment extends Fragment
implements IOnItemSelected {
#Override
public void onAccountSelected(Account item) {
System.out.println("Clicking on " + item.getTitle() + ", and isTwoPane=" + isTwoPane);
} // public void onAccountSelected
public static final String TAG="Expenses Fragment";
private boolean isTwoPane = false; // Let's assume we're on a phone
private FragmentManager fragmentManager;
private View fragmentView = null;
public ExpensesFragment() {
// Required empty public constructor
} // ExpensesFragment()
public static final ExpensesListFragment lListFragment = new ExpensesListFragment();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
fragmentView = inflater.inflate(R.layout.fragment_expenses, container, false);
isTwoPane = fragmentView.findViewById(R.id.expensesDetailContainer) != null;
fragmentManager = getChildFragmentManager();
if (savedInstanceState==null) {
if ( !lListFragment.isAdded() ) {
fragmentManager.
beginTransaction().
add(R.id.expensesListContainer,lListFragment).
commit();
} // if ( !lListFragment.isAdded() )
} // if (savedInstanceState==null)
if ( isTwoPane ) {
fragmentManager.
beginTransaction().
replace(R.id.expensesDetailContainer,new EmptyFragment()).
commit();
} // if ( isTwoPane )
return fragmentView;
} // onCreateView
} // ExpensesFragment
And this is the fragment with the recyclerview:
public class ExpensesListFragment extends Fragment {
private IOnItemSelected mCallback;
private RecyclerView rv;
private RecyclerView.LayoutManager rvlm;
private RecyclerAdapterAccounts rva;
public ExpensesListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCallback = (IOnItemSelected)getParentFragment();
} // onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.fragment_expenses_list, container, false);
if ( isVisible() ) return fragmentView;
FragmentManager fragmentManager = getChildFragmentManager();
// Setting the recyclerview environment
rv = fragmentView.findViewById(R.id.expensesRV); // recycler view
rvlm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(rvlm);
rva = new RecyclerAdapterAccounts();
rva.setCallBackFunction(mCallback);
rv.setAdapter(rva);
// Setting the floating action button and snackbar
FloatingActionButton fab = fragmentView.findViewById(R.id.fabAdd);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Load a Create Item frag", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
return fragmentView;
} // onCreateView
} // public class ExpensesListFragment
The RecyclerAdapterAccounts creates a generic set of data:
public class RecyclerAdapterAccounts extends
RecyclerView.Adapter<RecyclerAdapterAccounts.ViewHolderAccounts> {
private IOnItemSelected callBackFunction;
public IOnItemSelected getCallBackFunction() {return callBackFunction;}
public void setCallBackFunction(IOnItemSelected callBackFunction) {this.callBackFunction = callBackFunction;}
class ViewHolderAccounts extends RecyclerView.ViewHolder {
ImageView icon, isRepeating, isAlert;
TextView title, total;
public Account getAccount() {return account;}
public void setAccount(Account account) {this.account = account;}
Account account;
public ViewHolderAccounts(View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.list_item_ico_account);
isRepeating = itemView.findViewById(R.id.list_item_isrepeating);
isAlert = itemView.findViewById(R.id.list_item_isalert);
title = itemView.findViewById(R.id.list_item_title_account);
total = itemView.findViewById(R.id.list_item_desc_account);
account = null; // The account needs to be set using the setter/getter method
} // ViewHolderAccounts
} // class ViewHolderAccounts
List<Account> accts = new ArrayList<Account>();
ViewGroup parent;
#NonNull
#Override
public ViewHolderAccounts onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_account,parent,false);
this.parent = parent;
ViewHolderAccounts vh = new ViewHolderAccounts(v);
return vh;
} // onCreateViewHolder
#Override
public void onBindViewHolder(#NonNull ViewHolderAccounts holder, int position) {
// Look into the list the item with id=position
Optional<Account> la = accts.stream()
.filter(ac->ac.getId()==(long)position)
.findFirst();
if ( la.isPresent() ) {
int res = parent.getResources().getIdentifier(la.get().getIcon(), "drawable", "com.almonisolutions.elgddt");
holder.icon.setImageResource(res);
holder.isRepeating.setImageResource(R.drawable.automatic);
holder.isAlert.setImageResource(R.drawable.notifications);
holder.title.setText(la.get().getTitle());
holder.total.setText(la.get().getDescription());
holder.setAccount(la.get());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (holder.getAccount() != null) {
callBackFunction.onAccountSelected(holder.getAccount());
} // if (account != null)
} // public void onClick
});
} // if
} // onBindViewHolder
#Override
public int getItemCount() {
return accts.size();
} // getItemCount
RecyclerAdapterAccounts() {
super();
for(int i=0;i<16;i++) {
Account la = new Account();
la.setId((long) i);
la.setTitle("The item number " + i);
la.setDescription("$" + (1000*i));
switch(i%3) {
case 0: la.setIcon("imaged"); break;
case 1: la.setIcon("person_old"); break;
case 2: la.setIcon("pet"); break;
default: la.setIcon("add");
} // switch
accts.add(la);
} // for
} // RecyclerAdapterAccounts
} // class RecyclerAdapterAccounts
At first, In the ExpensesFragment I was getting an Exception that throw the message "Fragment already added". When I changed the ExpensesListFragment to static final, that error was gone.
Again, to recreate the error, you need to run in portrait mode, move through the tabs. Finish on anyone but the first one. Them rotate the device. Tap on the first tab. Then tap on the 3rd one, the one with the recyclerview. Swipe through the list and you will see it is double.
Any help will be appreciated.
Thanks in advance!!!
So I found the answer. ADM (see comment above) sent me to a previous article where part of the solution was to extend ViewPager and override instantiateItem. But I did not want to extend ViewPager.
However, in the same article was another link to this other article where there was the following explanation:
Blockquote By default, [FragmentPagerAdapter] will only preload one Fragment in front and behind the current position (although it does not destroy them unless you are using FragmentStatePagerAdapter).
So, I made TabPagerAdapter extend FragmentStatePagerAdapter instead of FragmentPageAdapter... and that was it!!
Thanks ADM for pointing to the right series of articles.

Communicating between 2 fragments in a Viewpager - Updating RecyclerView in Fragment B from Fragment A

I'm building a simple stock display app that allows you to fetch stocks of interest (frag A), store them in a TinyDB, and display them in a recyclerView (frag B).
The framework used to work fine - until I decided to incorporate a viewpager and Tablayout host. I cannot get the RecyclerView in Frag B to display new data live. This is because the activity viewpager initializes both fragments at launch, meaning you can't call the onCreateView code again, I believe.
Communicating between two fragments through an Activity has been touched before on this site, but I found the best example to be this one:
(https://github.com/kylejablonski/InterfaceDemo),
which uses two interfaces, one to communicate from Frag A to Activity, and another one to communicate from Activity to Frag B. But I have a serious problem -
Currently, clicking both the "clear portfolio" and "add stock" to portfolio buttons in Frag A result in an empty screen in Frag B, which means something is being called yet new data is not being displayed/associated with the Adapter
Activity (https://github.com/EXJUSTICE/Investr/blob/master/app/src/main/java/com/xu/investo/ViewHolderActivity.java)
public class ViewHolderActivity extends AppCompatActivity implements CommunicateToActivity,CommunicateToFragment{
//Job of ViewHolderActivity is to allow swiping between list and MainFragment/Fragment
TabLayout tablayout;
ViewPager viewPager;
List<HistoricalQuote> historicaldata;
Bundle bundle;
Adapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_holder);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
viewPager =(ViewPager)findViewById(R.id.viewpager);
tablayout= (TabLayout)findViewById(R.id.tabs);
tablayout.setupWithViewPager(viewPager);
setupViewPager(viewPager);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
//----------------------- Interface code-----------
#Override
public void communicateup(){
communicatedown();
//call communicate down STRAIGHT, since we call it from mainfrag
}
#Override
public void communicatedown(){
//This line works
ListFragment currentFragment =(ListFragment)adapter.instantiateItem(viewPager,1);
currentFragment.refreshUI();
}
private void setupViewPager(ViewPager viewPager) {
adapter = new Adapter(getSupportFragmentManager());
adapter.addFragment(new MainFragment(), "Add Stock");
adapter.addFragment(new ListFragment(), "Portfolio");
viewPager.setAdapter(adapter);
}
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public Adapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Frag A (MainFragment)
(https://github.com/EXJUSTICE/Investr/blob/master/app/src/main/java/com/xu/investo/MainFragment.java)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view =inflater.inflate(R.layout.content_main,container,false);
stocknames = new ArrayList<String>();
stocktickers = new ArrayList<String>();
tinyDB = new TinyDB(getContext());
/*
menu.setDisplayShowHomeEnabled(true);
//menu.setLogo("INSERT LOGO HERE");
menu.setDisplayUseLogoEnabled(true);
menu.setTitle(" Stock Selector");
*/
fetch =(Button) view.findViewById(R.id.fetchBtn);
enterID =(EditText)view.findViewById(R.id.enterID);
display =(TextView)view.findViewById(R.id.display);
mCalendar = Calendar.getInstance();
clear = (Button)view.findViewById(R.id.clearportfolio);
fetch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//TODO all the main page should do is add stocktickers and names to portfolio
//Fetch id and the dates
id =enterID.getText().toString();
/*to = Calendar.getInstance();
from = Calendar.getInstance();
to.setTime(dateto);
from.setTime(datefrom);
*/
FetchXDayData getData = new FetchXDayData();
getData.execute(id);
}
});
clear.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
tinyDB.clear();
recyclerinterface.communicateup();
}
});
return view;
}
//----------------------------INTERFACE CODE
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
recyclerinterface = (CommunicateToActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement RecyclerUpdateInterface");
}
}
///-------End of Oncreate---------------------------------------------------------------
//Called by AsyncTask, moving result to main thread
public void moveResultToUI(Stock result){
this.stock = result;
Toast.makeText(getActivity(),"Stock "+stock.getName()+" successfully added to portofolio",Toast.LENGTH_LONG).show();
//reverse the list of course, stock names and tickrs to portfolio
stocknames.add(stock.getName());
stocktickers.add(stock.getSymbol());
/*DEBUG Test code, Test. 30012017 WORKS
for (int i =0;i<historicaldata.size();i++){
HistoricalQuote current = historicaldata.get(i);
Toast toast = Toast.makeText(this,current.getClose().toString(),Toast.LENGTH_SHORT);
toast.show();
}
*/
//
if (stock != null){
display.setText("Name: "+stock.getName() +"\n"+"Price: "+ stock.getQuote().getPrice()+"\n"+ "Change(%)"+stock.getQuote().getChangeInPercent());
/*SMA = getSMA(10);
decision=checkSimpleCrossover(SMA,stock.getQuote().getPrice().longValue());
decisionView.setText("SMA: " + SMA + "\n"+decision);
*/
tinyDB.putListString("names",stocknames);
tinyDB.putListString("tickers",stocktickers);
//call interface activity comming up to Activity, then down to next fragment
recyclerinterface.communicateup();
}else{
Toast error = Toast.makeText(getActivity(),"Network Problem",Toast.LENGTH_SHORT);
error.show();
}
}
Frag B (ListFragment)
(https://github.com/EXJUSTICE/Investr/blob/master/app/src/main/java/com/xu/investo/ListFragment.java)
public class ListFragment extends Fragment {
private HistoricalQuote[] hisstocks;
private Stock[] stocks;
private RecyclerView recyclerView;
private StockAdapter mAdapter;
public ArrayList<String> stocknames;
public ArrayList<String>stocktickers;
TinyDB tinyDB;
#Override
public void onCreate(Bundle savedInstanceState){
//exists only to set the options menu
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
//fetching arraylists
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_list, container, false);
//Convert the arraylist into an array for arrayadapter
stocknames = new ArrayList<String>();
stocktickers = new ArrayList<String>();
tinyDB = new TinyDB(getContext());
stocknames = tinyDB.getListString("names");
stocktickers= tinyDB.getListString("tickers");
if (!stocknames.isEmpty()){
for (int i =0;i<stocknames.size();i++){
Toast toast= Toast.makeText(getActivity(),stocknames.get(i),Toast.LENGTH_SHORT);
toast.show();
}
}
recyclerView = (RecyclerView)view.findViewById(R.id.recylerView);
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL));
//http://stackoverflow.com/questions/24618829/how-to-add-dividers-and-spaces-between-items-in-recyclerview
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setItemAnimator(new DefaultItemAnimator());
//layoutManager necessary because it positions views on screen, in this case linearly
if (stocknames.isEmpty() ||stocknames ==null){
recyclerView.setVisibility(View.GONE);
}else{
updateUI();
}
return view;
}
public void refreshUI(){
stocknames.clear();
stocktickers.clear();
stocknames = tinyDB.getListString("names");
stocktickers= tinyDB.getListString("tickers");
if (mAdapter == null) {
mAdapter = new StockAdapter(stocknames,stocktickers);
recyclerView.setAdapter(mAdapter);
} else {
recyclerView.invalidate();
mAdapter.notifyDataSetChanged();
}
}
public void updateUI() {
//updateUI must be called EXPLICITLY!
stocknames = tinyDB.getListString("names");
stocktickers= tinyDB.getListString("tickers");
if (mAdapter == null) {
mAdapter = new StockAdapter(stocknames,stocktickers);
recyclerView.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
}
private class StockAdapter extends RecyclerView.Adapter<StockHolder>{
private ArrayList<String>stocknames;
private ArrayList<String>stocktickers;
public StockAdapter(ArrayList<String>names,ArrayList<String> tickers){
this.stocknames=names;
this.stocktickers=tickers;
}
#Override
public StockHolder onCreateViewHolder(ViewGroup parent, int viewType){
LayoutInflater layoutinflater = LayoutInflater.from(getActivity());
View view= layoutinflater.inflate(R.layout.row,parent,false);
return new StockHolder (view);
}
//Bind datato stockholder depending on position in arraylist
public void onBindViewHolder(StockHolder holder, int position){
String stockname = stocknames.get(position);
String stockticker =stocktickers.get(position);
holder.bindStock(stockname,stockticker);
}
#Override
public int getItemCount (){
return stocknames.size();
}
}
private class StockHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private String stockname;
private String stockticker;
private TextView nametextView;
private TextView tickertextView;
public StockHolder(View itemView){
super(itemView);
itemView.setOnClickListener(this);
nametextView =(TextView)itemView.findViewById(R.id.name);
tickertextView= (TextView)itemView.findViewById(R.id.ticker);
}
#Override
public void onClick(View v){
Intent launchGraph= new Intent(v.getContext(),GraphActivity.class);
launchGraph.putExtra("stockticker",stockticker);
launchGraph.putExtra("stockname",stockname);
startActivity(launchGraph);
//Animations?
}
//Actual binder method, maybe add a current
public void bindStock(String stocknom, String stocktick){
this.stockname=stocknom;
this.stockticker = stocktick;
nametextView.setText(stockname);
tickertextView.setText(stockticker);
}
}
Thanks in advance.
EDIT: Solved issue by creating a new adapter and linking it to new arraylists pulled from the TinyDB, thereby effectively swapping adapters.
Solved the issue by creating a whole new RecyclerView adapter, to which new arraylist data was binded to, and the whole recyclerview was then set to use this new adapter. All of this was done in a single step from FragA, using interfaces shown in the code in the solution.
Method shown below:
public void refreshUI(){
tinyDB = null;
tinyDB = new TinyDB(getContext());
newnames = tinyDB.getListString("names");
newtickers= tinyDB.getListString("tickers");
mAdapter = new StockAdapter(newnames,newtickers);
recyclerView.setAdapter(mAdapter);
}

ViewPager and dynamic fragments : re-instaciation gives NPE

I faced an issue with ViewPager and ViewPagerAdapter in Android.
I use a viewpager with 2 static fragments (one using a textEdit and the second one using a listview). Theyr are working pretty good.
But i have a problem with the third fragment which is dynamic.
It uses the camera and has to be instanciated, destroyed, re-instanciated following a scenario. So, the ViewPagerAdapter could contain 2 or 3 framents.
The problem appears when I re-instaciate the third fragment, I got a NPE after OnCreateView() (the main layout view is null after this method, but is not null inside the method).
There is the code for the main activity :
mViewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager();
mTabLayout = (TabLayout) findViewById(R.id.tabs);
mTabLayout.setupWithViewPager(mViewPager);
The setupViewPager() :
private void setupViewPager() {
mViewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
mTabFragmentDocument = new TabFragmentDocument();
mTabFragmentDocument.setApp(this);
mTabFragmentText = new TabFragmentText();
mTabFragmentText.setApp(this);
mViewPagerAdapter.addFragment(mTabFragmentText, AbstractDefiner.TEXT);
mViewPagerAdapter.addFragment(mTabFragmentDocument, AbstractDefiner.DOCUMENT);
mViewPager.setAdapter(mViewPagerAdapter);
}
To create the third fragment :
mTabFragment = new TabFragment();
mTabFragment .setApp(this);
mViewPagerAdapter.addFragment(mTabFragment, "THIRD");
mViewPagerAdapter.notifyDataSetChanged();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mViewPager.setCurrentItem(2);
mViewPagerAdapter.notifyDataSetChanged();
mTabFragment .setParams(tmp[1], tmp[2], tmp[3], tmp[4]);
mTabFragment .setupView();
mTabFragment .startWork();
}
}, 1000);
And to destroy it :
mViewPager.setCurrentItem(0);
mViewPager.removeViewAt(2);
mTabFragment .onDestroy();
mViewPagerAdapter.remove(2);
mViewPagerAdapter.notifyDataSetChanged();
mTabFragment = null;
Then, the Adapter code :
static class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public void remove(int index) {
mFragmentList.remove(index);
mFragmentTitleList.remove(index);
}
#Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Finally, the code of the third fragment :
public class TabFragment extends Fragment implements SurfaceHolder.Callback,
GLSurfaceView.Renderer {
private static final String LOGCAT = "WEB_RTC_VISIO";
private HomeActivity mApp;
private String p1;
private String p2;
private String p3;
private String p4;
private VideoSource mLocalVideoSource;
private VideoRenderer.Callbacks mLocalRenderer;
private VideoRenderer.Callbacks mRemoteRenderer;
private GLSurfaceView mVideoView;
private SurfaceView mDrawView;
private SurfaceHolder mDrawHolder;
private ImageView mCursor;
private String mBgBytesString;
private ImageView mImgView;
private View mV;
public TabFragment() {
// Required empty public constructor
}
public void setApp(HomeActivity app) {
mApp = app;
}
public void setParams(String p1, String p2, String p3,
String p4) {
this.p1= p1;
this.p2= p2;
this.p4= p4;
this.p3= p3;
}
#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
mV = inflater.inflate(R.layout.third_layout, container, false);
return mV;
}
#Override
public void onDetach() {
super.onDetach();
}
public void setupView() {
RequestUserPermission requestUserPermission = new RequestUserPermission(mApp);
requestUserPermission.verifyStoragePermissions();
mImgView = (ImageView) mV.findViewById(R.id.img_display); // NPE HERE
TextView mTest1= (TextView) mV.findViewById(R.id.test1);
mRequestLabelTextView.setText("test 1");
TextView mTest2= (TextView) mV.findViewById(R.id.test2);
mEquipmentSerialTextView.setText("test 2");
// View that displays the view from the camera
mVideoView = (GLSurfaceView) mV.findViewById(R.id.gl_surface);
// View that displays the cursor and drawing associated
mDrawView = (SurfaceView) mV.findViewById(R.id.draw_surface);
mDrawHolder = mDrawView.getHolder();
mDrawHolder.setFormat(PixelFormat.TRANSPARENT);
mDrawHolder.addCallback(this);
// Image of the cursor
mCursor = (ImageView) mV.findViewById(R.id.mouseCursor);
// Some more inits
}
public void startWork() {
//SOME WORK
}
}
So, the first instanciation is ok, but at the second, I got the NPE on getting the ImageView...
Someone can help me understanding this problem please ?
Thanks in advance !
Try using FragmentStatePagerAdapter not FragmentPagerAdapter it will solve many problems. Hope it will gonna work for you too.

Issue viewing Fragment through tabs

I've tried to post a similar questions without any answer, though of posting again.
The issue I am having is with viewing Fragment through Tabs, I have 3 Tabs in an AppBar displayed with ViewPager & FragmentPagerAdapter.
The problem is when the activity start, one of the tabs does not behave in a consistent way, upon logging in to the app & activity for the first time it would display the content of the Fragment ( Video clips ) but during switching between activities in the app, and going back to the Activity containing the 3 Tabs, choosing that specific Tab does not display anything, althou in the Logcat, i could tell it is retrieving Data related to the Vid Clip that should be displayed, also going back that Activity i see in the Logcat that even when i choose a different Tab, ( pictures ) Data from the 2 others Tab is being retrieved !?
The setup is as follows;
Main activity;
public class Member extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.member);
Firebase.setAndroidContext(this);
ButterKnife.bind(this);
// Adding Toolbar to Main screen
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Setting ViewPager for each Tabs
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
// Set Tabs inside Toolbar
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
}
private void setupViewPager(ViewPager viewPager) {
Adapter adapter = new Adapter(getSupportFragmentManager());
adapter.addFragment(new PicContentMMFragment(), "Pictures");
adapter.addFragment(new VidContentFragment(), "Videos");
adapter.addFragment(new LocationsContentFragment(), "Locations");
viewPager.setAdapter(adapter);
}
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public Adapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
Well, as one of the users commented on the post pointed out that the problem could be from the specific fragemnt itself and not the way Tabs and Viewpager following is the VidContentFragment;
public class VidContentFragment extends android.support.v4.app.Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final RecyclerView recyclerView = (RecyclerView) inflater.inflate(
R.layout.recycler_view, container, false);
Log.i("MyTag_onCreate","vidContentFragment_Loaded");
ContentAdapter adapter = new ContentAdapter(recyclerView.getContext());
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return recyclerView;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public VideoView video;
public TextView authorName;
public TextView ratingValue;
public TextView locationValue;
public RatingBar ratingB;
public Button submitRating;
public LinearLayout placeNameHolder;
public int newRating;
public ViewHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.item_card_vid, parent, false));
authorName = (TextView) itemView.findViewById(R.id.card_title);
ratingValue = (TextView) itemView.findViewById(R.id.ratingValue);
locationValue = (TextView) itemView.findViewById(R.id.locationValue);
video = (VideoView) itemView.findViewById(R.id.placeVid);
ratingB = (RatingBar)itemView.findViewById(R.id.ratingBarMM);
ratingB.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() {
#Override
public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
newRating = (int) ratingB.getRating();
}
});
submitRating = (Button) itemView.findViewById(R.id.submitRatingMM);
submitRating.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
public_id = publicID[getPosition()];
Log.i("MyTag_Rating",public_id);
public static class ContentAdapter extends RecyclerView.Adapter<ViewHolder> {
// Set numbers of List in RecyclerView.
private Context mContext;
public ContentAdapter(Context context) {
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()), parent);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.authorName.setText(author[position]);
holder.ratingValue.setText(ratingV[position]);
holder.locationValue.setText(locationV[position]);
Uri video = Uri.parse(mVideos[position]);
holder.video.setVideoURI(video);
holder.video.setMediaController(new MediaController(mContext));
holder.video.requestFocus();
holder.video.seekTo(1000);
holder.video.pause();
try{
String url1 = mVideos[position];
URL ulrn = new URL(url1);
HttpURLConnection con = (HttpURLConnection)ulrn.openConnection();
InputStream is = con.getInputStream();
Bitmap bmp = BitmapFactory.decodeStream(is);
if (null != bmp)
Palette.generateAsync(bmp, new Palette.PaletteAsyncListener() {
public void onGenerated(Palette palette) {
int bgColor = palette.getVibrantColor(mContext.getResources().getColor(android.R.color.black));
holder.placeNameHolder.setBackgroundColor(bgColor);
}
});
else
Log.e("MyTag_BMP","The Bitmap is NULL");
}catch (Exception e){
}
}
#Override
public int getItemCount() {
return LENGTH;
}
}
}
Some part of the code (related to retrieving data from firebase) is left out to shorten it.
Although the other 2 fragments are organized the same way, the behavior of this fragment is inconsistent ?
Here is the version of the adapter with viewholder that I have for a recyclerview you said would likely be helpful. You'll need to refactor to adjust for your situation. I have gutted it mostly to help show just the bare interactions.
public class AnimalAdapter extends RecyclerView.Adapter<AnimalAdapter.AnimalCardViewHolder>{
private List<Animals> animalList;
public AnimalAdapter adapter = this;
Context context;
public AnimalAdapter(List<Animals> animalList, Context context) {
this.animalList = animalList;
this.context = context;
this.setInterface((iAdapterInterface) context);
}
#Override
public int getItemCount() {
return animalList.size();
}
#Override
public void onBindViewHolder(AnimalCardViewHolder holder, int position) {
Animals animal = animalList.get(position);
holder.txtName.setText(animal.getName());
holder.txtType.setText(animal.getType());
}
#Override
public AnimalCardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.animal_card_view, parent, false);
return new AnimalCardViewHolder(itemView);
}
public class AnimalCardViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
protected TextView txtName;
protected TextView txtType;
public AnimalCardViewHolder(final View itemView) {
super(itemView);
txtName = (TextView) itemView.findViewById(R.id.txtName);
txtType = (TextView) itemView.findViewById(R.id.txtType);
imgClose = (ImageView) itemView.findViewById(R.id.txtRemove);
listItem = itemView.findViewById(R.id.list_item);
listItem.setOnClickListener(this);
imgClose.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.txtRemove:
break;
case R.id.list_item:
break;
}
}
}
}

How to add custom view in ViewPager for walkthrough

I've used ViewPagerIndicator to get WalkThrough like most popular application. But I can't understand how to add Pictures in ViewPager which shows how to use the application.
What I want is like these Walk Through.
What i got till now.
I dunno
How to add Custom View like ImageView and TextView in ViewPager?
Any guidance would be most welcome.
Since the question really old, I'll just write a short general guideline:
To populate viewpager with content, you add fragments to an adapter and then you set the adapter to the viewpager. You can do pretty much any layout in the fragment, putting there images/text etc..
As Inteist suggest, one can put any layout in fragment and supply that fragment to adapter.
Fragment:
public final class SelectModelFragment extends Fragment {
private static final String KEY_CONTENT = "SelectModelFragment:Content";
private static String TAG = SelectModelFragment.class.getSimpleName();
private SelectModel mSelectModelObj;
private CircularImageView mImageView;
public static SelectModelFragment newInstance(SelectModel obj) {
SelectModelFragment fragment = new SelectModelFragment();
fragment.mSelectModelObj =obj;
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if ((savedInstanceState != null) && savedInstanceState.containsKey(KEY_CONTENT)) {
mSelectModelObj = savedInstanceState.getParcelable(KEY_CONTENT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_select_model, container, false);
mImageView = (CircularImageView)view.findViewById(R.id.fragment_select_model_iv);
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_CONTENT, mSelectModelObj);
}
}
Fragment Adapter :
public class SelectModelAdapter extends FragmentPagerAdapter {
ArrayList<SelectModel> mList;
private int mCount;
private static final String TAG = SelectModelAdapter.class.getSimpleName();
public SelectModelAdapter(FragmentManager fm, ArrayList<SelectModel> mList) {
super(fm);
this.mList = mList;
mCount = mList.size();
}
#Override
public Fragment getItem(int position) {
return SelectModelFragment.newInstance(mList.get(position));
}
#Override
public int getCount() {
return mCount;
}
#Override
public CharSequence getPageTitle(int position) {
return TAG;
}
public void setCount(int count) {
if (count > 0 && count <= 10) {
mCount = count;
notifyDataSetChanged();
}
}
}
Activity: Where ViewPager has Fragment Adapter which feeds Fragment.
public class SelectModelActivity extends BaseSliderActivity {
private ViewPager mPager;
private SelectModelAdapter mAdapter;
private ArrayList<SelectModel> mList;
private void setAdapter() {
mAdapter = new SelectModelAdapter(getSupportFragmentManager(), mList);
mPager.setAdapter(mAdapter);
mIndicator.setViewPager(mPager);
}
}

Categories

Resources