Get information from current fragment in TableLayout - java

I'm using TabLayout and ViewPager.I successfully integrated it and used from fragments.This is my mainTabActivity java class
public class MainTabActivity extends AppCompatActivity {
private CustomTabLayout tabLayout;
private ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple_tabs);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (CustomTabLayout)findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
findViewById(R.id.tab_ticket).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent=new Intent(MainTabActivity.this, TicketsActivity.class);
startActivity(intent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
});
findViewById(R.id.tab_main).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.e("BottomClickAction","BottomClickAction");
}
});
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new OneWayFragment(), "ONE");
adapter.addFragment(new TwoFragment(), "ROUND");
viewPager.setAdapter(adapter);
}
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);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
}
Also i have custom bottom bar.Now i try to explain my problem.In my fragments contains some elements,fro example spinners,Textviews or etc.My question is:How i get information from current fragment when i click bottom bar? I mean,spinner'data information,Textview's gettext or etc.
How i can solve my problem? Is it a possible to don't use static variables ?

You can find the fragment OneWayFragment in viewpager like this:
public OneWayFragment getOneWayFragment() {
return (OneWayFragment) getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + R.id.viewPager + ":" + 0);
}
public TwoFragment getTwoFragment() {
return (TwoFragment) getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + R.id.viewPager + ":" + 1);
}
You can access public method from OneWayFragment or TwoFragment a get data you want.
For ex, create method getText from OneWayFragment:
public String getData() {
return tv.getText().toString();
}
And in your MainTabActivity. For ex you click button has id tab_main and you get data from OneWayFragment.
findViewById(R.id.tab_main).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.e("BottomClickAction","BottomClickAction");
OneWayFragment fragment = getOneWayFragment();
if(fragment != null) {
String data = fragment.getData();
}
}
});

Create own method like getFragmentData() in fragments. Access this method whenever you want. In your case:
1) take references of your fragment
_oneWayaFrgament= new OneWayFragment()
adapter.addFragment(_oneWayaFrgament, "ONE");
2) call _oneWayaFrgament.getFragmentData()
similarly apply your conditional logic on TwoFragment.
Please call getFragmentData() by identifying current fragment.

yes you can have public variables and methods in your fragments and then you can set and get your variables or call your methods from your activity.
for example in your fragments:
public String myString;
public Button someButton;
or
public void fillMyTextView(){//some codes};
and in your activity you can call those methods or access your views like this:
myFragment.fillMyTextView("hello");
myFragment.someButton.setText("my btn");

I would suggest you create singleton as state object(as you want as the state of the views, you can go with integer or short or byte for state marks memory wise) use as you want, and nullify once done usage. This way you can be access any data, in any fragment or activity, at any point of time. You can go with other implementations also like interface kinds as design and architectural way.But depends on the how important states are.

Related

java.lang.NullPointerException: Attempt to invoke virtual method, causing app to crash

I'm particularly new to this field. I'm trying to create a login page using fragments and it crashed when I tried to run it. I checked the logcat, turns out its a NullPointerException. It highlighted the error here:
register = (Button) findViewById(R.id.RegisterUser);
register.setOnClickListener(this);
Here is my code
this is mainactivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView register;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
register = (Button) findViewById(R.id.RegisterUser);
register.setOnClickListener(this);
ViewPager viewPager = findViewById(R.id.viewPager);
AuthenticationPagerAdapter pagerAdapter = new
AuthenticationPagerAdapter(getSupportFragmentManager());
pagerAdapter.addFragmet(new LoginFragment());
pagerAdapter.addFragmet(new RegisterFragment());
viewPager.setAdapter(pagerAdapter);
}
public void onClick(View v){
switch (v.getId()){
case R.id.RegisterUser:
startActivity(new Intent(this, RegisterUser.class));
break;
}
}
class AuthenticationPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragmentList = new ArrayList<>();
public AuthenticationPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
#Override
public int getCount() {
return fragmentList.size();
}
void addFragmet(Fragment fragment) {
fragmentList.add(fragment);
}
}
}
It seems you define the global field private TextView register; but you want to cast it to Button register = (Button) findViewById(R.id.RegisterUser);
Please check your XML file and make sure what's your element TextView or Button.
can you please check your activity_main.xml file
check into that is a Button or Textview

is there any way to pass an arraylist from an onDataChange to a fragment ViewPagerAdapter inner Class?

Don't know how to send the arraylist that's been filled in the onDataChanged to the method that's called to create a new fragment to pass it with a bundle object
i've tried messing with the constructor to expect it as a parameter and in the constructor's body appears as "not empty" but after that i don't know how to send it to the addFragment method since i've tried and seems "null".
i've tried to access to it as MainActivity.this.arraylist but it says is empty.
i've made it static and it still says it's empty
public class MainActivity extends AppCompatActivity {
private TabLayout tablayout;
private ViewPager viewpager;
private Temperatura temperatura;
private Humedad humedad;
private Viento viento;
private Info informacion;
List<String> Lecturabundle = new ArrayList<>();
DatabaseReference mDatabase;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDatabase = FirebaseDatabase.getInstance().getReference();
tablayout = findViewById(R.id.tablayout);
viewpager = findViewById(R.id.viewpager);
temperatura = new Temperatura();
humedad = new Humedad();
viento = new Viento();
informacion = new Info();
tablayout.setupWithViewPager(viewpager);
ViewPagerAdapter viewpageradapter = new ViewPagerAdapter(getSupportFragmentManager(), 0);
mDatabase.child("lectura").child("hora").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Lecturabundle.add(dataSnapshot.getValue().toString());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
viewpageradapter.addFragment(temperatura, "Temperatura");
viewpageradapter.addFragment(humedad, "Humedad");
viewpageradapter.addFragment(viento, "Viento");
viewpageradapter.addFragment(informacion, "info");
viewpager.setAdapter(viewpageradapter);
}
private class ViewPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments = new ArrayList<>();
private List<String> fragmentTitle = new ArrayList<>();
public ViewPagerAdapter(#NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
#NonNull
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
public void addFragment(Fragment fragment, String title, List<String> recibeArrayList){
HERE'S WHERE I NEED LECTURABUNDLE TO BE SO I CAN SEND IT TO EACH FRAGMENT BUT I DON'T KNOW HOW TO DO IT THE RIGHT WAY SINCE WHAT I'VE TRIED GETS ME TO A POINT WHERE WHEN IT'S FINALLY HERE ISEMPTY() SAYS TRU
Bundle b = new Bundle();
b.putStringArrayList("recibe", (ArrayList<String>) Lecturabundle);
fragment.setArguments(b);
fragments.add(fragment);
fragmentTitle.add(title);
}
#Override
public int getCount() {
return fragments.size();
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return fragmentTitle.get(position);
}
}
}
i need Lecturabundle to be in the addFragment so i can pass it as a bundle to the fragments
The right way is using Bundle to pass it to your fragment.
But in your case, you are creating/initializing fragment in onCreate and the onDataChanged method gets called after some time you can say it may take a couple of seconds to minutes to get called depending on Data.
So if you want to send that data to your fragment what you can do is you can create a public method in your fragment class let's say name it as newDataInserted(list) with list as parameter. now you can call that method inside onDataChanged() using your fragment instance you created earlier.
For example
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Lecturabundle.add(dataSnapshot.getValue().toString());
if(temperature != null) {
temperatura.newDataInserted(Lecturabundle);
}
}

Starting Fragment method from MainActivity causes Exception

I work on an app which contains Fragments and a ViewPager. I fetch location data in my MainActivity and want to use it inside the fragments, so I created a method to pass the variables and process with them. Unfortunately I get a NullPointerException on all view objects, like the progressBar or the CollapsingToolbarLayout inside of the Fragment when the method is called. I guess the fragment is not correctly started just by calling a method, but how can I change it, so the view objects are created? Or is there another way to first fetch location data and use it inside the fragments afterwards?
My MainActivity:
public class MainActivity extends AppCompatActivity implements Serializable {
private ViewPager viewPager;
BottomNavigationView bottomNavigationView;
FragmentA FragmentA;
FragmentB FragmentB;
FragmentC FragmentC;
MenuItem prevMenuItem;
public double lat;
public double lon;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.viewpager);
bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_umkreis:
viewPager.setCurrentItem(0);
break;
case R.id.navigation_karte:
viewPager.setCurrentItem(1);
break;
case R.id.navigation_einstellungen:
viewPager.setCurrentItem(2);
break;
}
return false;
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
if (prevMenuItem != null) {
prevMenuItem.setChecked(false);
}
else
{
bottomNavigationView.getMenu().getItem(0).setChecked(false);
}
Log.d("page", "onPageSelected: "+position);
bottomNavigationView.getMenu().getItem(position).setChecked(true);
prevMenuItem = bottomNavigationView.getMenu().getItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
setupViewPager(viewPager);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
callsFragment = new CallsFragment();
chatFragment = new ChatFragment();
contactsFragment = new ContactsFragment();
adapter.addFragment(callsFragment);
adapter.addFragment(chatFragment);
adapter.addFragment(contactsFragment);
viewPager.setAdapter(adapter);
int limit = adapter.getCount();
viewPager.setOffscreenPageLimit(limit);
}
public void start() {
//Location data fetched here
FragmentB fb = new FragmentB();
fb.start(lat, lon);
}
}
My FragmentB:
public class FragmentB extends Fragment {
private ProgressBar progressBar;
private double lat;
private double lon;
String title;
private BaseAdapter mItemsAdapter;
private final List<HashMap<String, String>> mItemList = new ArrayList<>();
ArrayList<HashMap<String, String>> resultList = new ArrayList<>();
public FragmentB() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_b, container, false);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
progressBar.setVisibility(View.VISIBLE);
((ListView) view.findViewById(R.id.list))
.setAdapter(mItemsAdapter);
return view;
}
public void start(double latGet, double lonGet){
lat = latGet;
lon = lonGet;
new GetContacts().execute();
}
private class GetContacts extends AsyncTask<Void, Void, Void>{
#Override
protected void onPreExecute() {
super.onPreExecute();
// Showing progress dialog
}
#Override
protected Void doInBackground(Void... arg0) {
//Fetch And Process Data -> Result into resultList
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// Dismiss the progress dialog
final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) getActivity().findViewById(R.id.toolbar_layout);
AppBarLayout appBarLayout = (AppBarLayout) getActivity().findViewById(R.id.appBar);
progressBar.setVisibility(View.GONE);
//Updating parsed JSON data into ListView
mItemsAdapter = new SimpleAdapter(
getActivity(), mItemList,
R.layout.list_item, new String[]{"brand", "price",
"dist", "street", "houseNumber", "postcode", "place"}, new int[]{R.id.brand,
R.id.price, R.id.dist, R.id.street, R.id.houseNumber, R.id.postCode, R.id.place});
mItemList.addAll(resultList);
mItemsAdapter.notifyDataSetChanged();
}
}
}
In the following method from MainActivity, you try to pass the location data to an instance of FragmentB:
public void start() {
//Location data fetched here
FragmentB fb = new FragmentB();
fb.start(lat, lon);
}
The resulting crash is due to the fact that you only instantiate the Fragment, nothing else. Methods like onCreateView() or onAttach() will only be called if the Fragment is (about to be) added to the View hierarchy. So for this instance of FragmentB, methods like getContext() or getActivity() will return null. That's why you get NullPointerExceptions.
Make sure that you only call start() on a Fragment which has been added to the View hierarchy. Or modify the method so that it stores the location data in some appropriate structure which can be accessed from e.g. onViewCreated(), onStart() or onResume()
See the docs for more information on the Fragment lifecycle

Launching Fragment B from Activity based on data collected from Fragment A with less boilerplate code

I have the following setup:
I have an Activity that launches a FragmentA.
FragmentA contains a recyclerView and an adapter.
I have an interfaceA in the adapter which is implemented in FragmentA so that I get notified which position was clicked.
I have a second interfaceB that I created in the FragmentA, which is implemented in the Activity that launched FragmentA in step 1.
Finally, I'm launching FragmentB from the Activity based on data I get from interfaceB.
Everything is working fine, however the flow is tedious, and demands a lot of boilerplate code.
THE GOAL is to have the activity launch fragmentB that contains data from a single clicked item from the recyclerView within FragmentA.
Question: Can it be achieved differently?
Code Below:
Activity launches FragmentA:
Fragment fragment = fragmentManager.findFragmentByTag(FragmentA.class.getName());
if (fragment == null) {
fragment = Fragment.instantiate(this, FragmentA.class.getName());
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction
.replace(R.id.fragmentLayout, fragment, FragmentA.class.getName())
.addToBackStack(FragmentA.class.getName())
.commit();
Inside FragmentA we have recyclerView, and interfaceA implemented in the adapter:
Adapter Class:
public class AdapterA extends RecyclerView.Adapter< AdapterA.ViewHolderA> {
//instances
private Context context;
private List<Data> dataList;
private OnItemClickListener onItemListClickListener;
//Constructor
public AdapterA (Context context, List<Data> dataList, OnItemClickListener onItemListClickListener {
this.context = context;
this.dataList = dataList;
this.onItemListClickListener = onItemListClickListener;
}
onCreateViewHolder....
onBindViewHolder....
getItemCount...
class ViewHolderA RecyclerView.ViewHolder {
//instances..
//Constructor...
}
}
interface class interfaceA:
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
interface class interfaceB:
public interface SingleItemEventListener {
void onSingleItemClicked(int position);
}
FragmentA class:
//Instances
private AdapterA adapter;
private RecyclerView recyclerView;
private onSingleItemClicked singleItemEventListener;
onAttach...
onCreateView...
#Override
public void onStart() {
super.onStart();
//Setting adapter
onSetAdapter();
}
private void onSetAdapter() {
List<Data> dataList;
dataList = getData();
adapter = new AdapterA(context, dataList, new OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
singleItemEventListener.onSingleItemClicked(position);
}
});
In the Activity, we are implementing onSingleItemClicked callback to receive the event and launch FragmentB with data received from the interface callback:
ActivityA implements SingleItemEventListener {
#Override
public void onSingleItemClicked(int position) {
Data data = getData(position);
if (data != null) {
Bundle bundle = new Bundle();
bundle.putParcelable("single_data_key", data);
Fragment fragmentB = fragmentManager.findFragmentByTag(FragmentB.class.getName());
if (fragmentB == null && bundle != null) {
fragmentB = Fragment.instantiate(this, FragmentB.class.getName(), bundle);
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction
.replace(R.id.FragmentLayout, fragmentB, FragmentB.class.getName())
.addToBackStack(FragmentB.class.getName())
.commit();
}
}
}
Add ViewModel to your activity and use it to communicate between all you components, activity and both fragments.
You can get access to the activity's ViewModel from your fragment
MyViewModel model = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
Use LiveData for communication, post an action to it from your fragment and listen to it in your activity to start another fragment.
You can do the same task using only single interface rather than two or you can share view models between fragments and activity.
Method 1 : Using single interface
Define Interface as
public interface FragmentCallbackListener {
void onItemClick(View view, int position);
}
Let your Activity implement it :
ActivityA extend Activity implements FragmentCallbackListener {
#Override
public void onItemClick(View view, int position) {
....
}
}
Fragments which are attached to activity implementing FragmentCallbackListener should override onAttach() of fragment as:
public class FragmentA extends Fragment {
private FragmentCallbackListener mListener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the listener. If not, it throws an exception
try {
mListener = (FragmentCallbackListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement FragmentCallbackListener");
}
}
}
Pass same listener instance to Adapter :
adapter = new AdapterA(context, dataList, mListener);
Method 2 : Using shared view model
Define view model as :
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Data> selected = new MutableLiveData<Data>();
public void select(Data data) {
selected.setValue(data);
}
public LiveData<Data> getSelected() {
return selected;
}
}
Inside FragmentA
public class FragmentA extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
...
adapter = new AdapterA(context, datalist, model);
}
}
Inside adapter's view onClickListener :
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
model.select(view.getTag()); //or use getData(position) to return selected data
}
});
Inside ActivityA
ActivityA extend Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(this).get(SharedViewModel.class);
model.getSelected().observe(this, data -> {
// launch the new fragment B with selected data
});
}
}
For more details, read https://developer.android.com/training/basics/fragments/communicating
Without an external library, have you considered using a broadcast?
It still requires boilerplate, but depending on your opinion can be a bit cleaner (the activity wont need to to be an instance of any interface, you don't need any 'god object' models)
In FragmentA's adapter, when an item is clicked you send a broadcast with an explicit action and the arguments as extras. In the activity you register (and unregister) a broadcast receiver (no need to alter manifest). Finally the receiver starts FragmentB as usual.
Fragment A / Adapter
Intent item = new Intent(MY_CONSTANT);
item.putExtra(MY_EXTRA_CONSTANT, position);
getActivity().sentBroadcast(intent);
Activity
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int position = intent.getIntExtra(MY_EXTRA_CONSTANT, -1);
//TODO: Move your FragmentB transaction here
}
};
void onPause() {
unregisterReceiver(myReceiver);
}
void onResume() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MY_CONSTANT);
registerReceiver(myReceiver, intentFilter);
}
You can achieve it without using Interface, BroadcastReceiver and ViewModel.
Check below code:
ActivityA.java
public class ActivityA extends AppCompatActivity {
ActivityTestBinding mBinding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_test);
pushFragment(new FragmentA());
}
/**
* #param fragment pass fragment you want to replace
*/
public void pushFragment(Fragment fragment) {
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped && manager.findFragmentByTag(backStateName) == null) {
// if fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, fragment, backStateName);
ft.addToBackStack(backStateName);
ft.commit();
}
}
#Override
public void onBackPressed() {
try {
if (getSupportFragmentManager().getBackStackEntryCount() <= 1) {
finish();
} else {
getSupportFragmentManager().popBackStack();
}
} catch (Exception e) {
super.onBackPressed();
}
}
}
Here pushFragment method is generalized for all fragment.
And in onBackPressed() it will check BackStackEntryCount. If there is 1 or less count that means only last fragment is in backstack and finish(); will be called and exit application. If there is >1 count in BackStackEntryCount then it will pop last fragment.
In your AdapterA.java:
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (context instanceof ActivityA) {
FragmentB mFragmentB = new FragmentB();
Bundle bundle = new Bundle();
bundle.putParcelable("single_data_key", dataList.get(getAdapterPosition()));
mFragmentB.setArguments(bundle);
((ActivityA) context).pushFragment(mFragmentB);
}
}
});
Here you already passing context of ActivityA. So you can check that if (context instanceof ActivityA) then you can call pushFragment method of ActivityA by using Type Casting.
So this is less boilerplate code you can integrate in your Android Project.

Refresh fragments after coming back from an activity

I'm working on an activity which uses a TabLayout to house two fragments. Each of the fragments uses RecyclerView. The activity also has an options menu. When any of the options is clicked, a new activity will start. One of the new activities has a "Save" button which when clicked will update the first activity's data set and return to the first activity. However, the first activity's fragments don't show the changed data set even though the data set itself changes.
RecordAddActivity.java (First Activity)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FileUtils.initDataDir(this);
MySQLiteHelper db = new MySQLiteHelper(this);
Bundle bundle = getIntent().getExtras();
siteID = bundle.getLong(Constants.INTENT_EXTRA_SITE_ID);
site = db.getSiteByID(siteID);
trapFileName = site.getId() + "_" + site.getName() + ".csv";
inputString = site.getName();
setContentView(R.layout.activity_add_record_new);
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new PagerAdapter(getSupportFragmentManager());
insectsAddRecordFragment = new InsectsAddRecordFragment();
naturalPestsAddRecordFragment = new NaturalPestsAddRecordFragment();
if (!doesRecordExist) {
insectsAddRecordFragment.setInsects(site.getInsectsNames());
naturalPestsAddRecordFragment.setNaturalPests(site.getNaturalPestsNames());
} else {
IsDefaultInsectTypes isDefaultInsectTypes = new IsDefaultInsectTypes(trapFileName);
insectsAddRecordFragment.setInsects(isDefaultInsectTypes.getInsectNamesList());
naturalPestsAddRecordFragment.setNaturalPests(isDefaultInsectTypes.getPestNamesList());
site.setInsects(isDefaultInsectTypes.getInsectNamesList());
site.setNaturalPests(isDefaultInsectTypes.getPestNamesList());
}
adapter.addFragment(insectsAddRecordFragment, "Serangga Perosak");
adapter.addFragment(naturalPestsAddRecordFragment, "Musuh Semulajadi");
viewPager.setAdapter(adapter);
final TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayoutbar);
tabLayout.setTabTextColors(Color.BLACK, Color.WHITE);
tabLayout.setupWithViewPager(viewPager);
tabLayout.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager) {
#Override
public void onTabSelected(TabLayout.Tab tab) {
super.onTabSelected(tab);
tabLayout.requestFocus();
hideKeyboard(viewPager);
}
});
}
//The options menu which start the new activity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_edit_insects_type){
final int EDIT_INSECT_TYPE_REQUEST = 1;
Intent intent = new Intent(RecordAddActivity.this, EditInsectsTypeActivity.class);
intent.putExtra(Constants.INTENT_EXTRA_REPORT_TRAP_FILE, trapFileName);
intent.putExtra(Constants.INTENT_EXTRA_SITE_OBJECT, site);
startActivityForResult(intent, EDIT_INSECT_TYPE_REQUEST);
return true;
}
}
//The FragmentPagerAdapter
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragmentList = new ArrayList<>();
private List<String> mFragmentTitleNames = new ArrayList<>();
public PagerAdapter(FragmentManager fm) {
super(fm);
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleNames.add(title);
}
public void swapItems(Fragment insectFragment, Fragment naturalPestFragment) {
mFragmentList.clear();
mFragmentList.add(insectFragment);
mFragmentList.add(naturalPestFragment);
Log.d(TAG, "Items swapped");
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleNames.get(position);
}
}
What I've tried:
Using onResume()
#Override
public void onResume() {
Log.d(TAG, "onResume");
super.onResume(); // Always call the superclass method first
doesRecordExist = FileUtils.doesRecordExists(trapFileName);
if (doesRecordExist) {
IsDefaultInsectTypes isDefaultInsectTypes = new IsDefaultInsectTypes(trapFileName);
insectsAddRecordFragment = new InsectsAddRecordFragment();
naturalPestsAddRecordFragment = new NaturalPestsAddRecordFragment();
//The log shows that the data set has changed
Log.d(TAG, "Nama serangga:" + isDefaultInsectTypes.getInsectNamesList());
//Trying to update the fragments
insectsAddRecordFragment.setInsects(isDefaultInsectTypes.getInsectNamesList());
naturalPestsAddRecordFragment.setNaturalPests(isDefaultInsectTypes.getPestNamesList());
adapter.swapItems(insectsAddRecordFragment, naturalPestsAddRecordFragment);
}
}
Using a Global variable to update the fragment
EditInsectsTypeActivity.java (The new activity)
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_save) {
Globals2 allowRefresh = Globals2.getInstance();
ArrayList<String> insectsNameList = insectsViewFragment.getInsectsNameList();
ArrayList<String> pestsNameList = naturalPestsViewFragment.getInsectsNameList();
FileUtils.updateSiteInsects(this, trapFileName, insectsNameList, pestsNameList);
allowRefresh.setData(true);
Log.d(TAG, "allowRefresh: " + allowRefresh.getData());
finish();
Utils.showToast(getBaseContext(), "Rekod disimpan");
}
return true;
}
InsectsAddRecordFragment.java (The fragment to be refreshed/updated)
#Override
public void onResume() {
Log.d(TAG, "onResume");
super.onResume(); // Always call the superclass method first
Globals2 allowRefresh = Globals2.getInstance();
IsDefaultInsectTypes isDefaultInsectTypes = new IsDefaultInsectTypes("27_Cgtv.csv");
Log.d(TAG, "Nama serangga:" + isDefaultInsectTypes.getInsectNamesList());
setInsects(isDefaultInsectTypes.getInsectNamesList());
Log.d(TAG, "allowRefresh: " + allowRefresh.getData());
if (allowRefresh.getData()) {
allowRefresh.setData(false);
getFragmentManager().beginTransaction().detach(this).attach(this).commit();
}
}
I've spent hours trying to fix this without any luck. Any help will be much appreciated.
So here's what you need to do, in your Activity's onPause() method update the list in your InsectsAddRecordFragment like so:
public void onPause()
{
... // your logic for data-set changed.
if (doesRecordExist)
insectsAddRecordFragment.setInsects(isDefaultInsectTypes.get‌​InsectNamesList());
...
}
And within setInsects of InsectsAddRecordFragment something similar to this.
public void setInsects() {
// Here adapter is the Adapter for the RecyclerView
adapter.setAdapterList(isDefaultInsectTypes.get‌​InsectNamesList());
// notifyDataSetChanged() is important here because without this the Adapter has no idea
// that data has changed.
adapter.notifyDataSetChanged();
}
While in your RecyclerViewAdapter for InsectsAddRecordFragment's RecyclerView items. Do the following.
// ArrayList<String> list is just an example you may have some different data set.
public void setAdapterList(ArrayList<String> list) {
this.adapterList = list;
}
Using this approach there is no need recreate the fragment, instead you can just update the dataset within the Adapter.
I'd also suggest reading some blogs and how-to's on Fragments, RecyclerView and RecyclerView.Adapter before you work any further.
Override:
protected void onActivityResult(int requestCode, int resultCode, Intent data)
In the first Activity.
[EDIT] More info on startActivityForResult() here:
https://developer.android.com/training/basics/intents/result.html

Categories