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
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.