I am currently working on a project in which I have text data retrieved from a web page displayed between 3 different tabs declared as fragments.
There's a button refresh at the bottom of each tab & whenever the user presses that button, the app retrieves the most recent data from the web site & displays it in one of the three tabs.
The logic on which the app chooses the tab to write the text into is the following:
Write data into Tab 1 (leftmost). If Tab 1 already contains data, write into Tab 2. If Tab 2 already contains data, write into Tab 3. If all tabs contain data, overwrite data in Tab 1 & start the cycle over.
Here is my code before getting into my problem. I am only posting the relevant code to avoid flooding this message.
MainActivity:
public class MainActivity extends AppCompatActivity implements
Tab1.OnFragmentInteractionListener, Tab2.OnFragmentInteractionListener,
Tab3.OnFragmentInteractionListener{
final static String url = "http://159.203.78.94/rpilog/weatherstation.txt";
public static TextView dataTextTab1;
public static TextView dataTextTab2;
public static TextView dataTextTab3;
static Context context;
public static TabLayout tabLayout;
public static int tabPosition;
public static boolean tab1Used = false;
public static boolean tab2Used = false;
public static boolean tab3Used = false;
public static int positionFactor = -1;
static String TAG;
public static String dataFromURL;
public static TextView test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Text placeholders for the data received from the URL.
dataTextTab1 = (TextView) findViewById(R.id.dataTextTab1);
dataTextTab2 = (TextView) findViewById(R.id.dataTextTab2);
dataTextTab3 = (TextView) findViewById(R.id.dataTextTab3);
context = getApplicationContext();
//Creating the tabs.
tabLayout = (TabLayout) findViewById(R.id.tablayout);
tabLayout.addTab(tabLayout.newTab().setText("No data"));
tabLayout.addTab(tabLayout.newTab().setText("No data"));
tabLayout.addTab(tabLayout.newTab().setText("No data"));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
//ViewPager & PagerAdapter object to allow sliding tabs.
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
public static void receiveString(){
RequestQueue queue = Volley.newRequestQueue(context);
//Requests a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
dataFromURL = response;
if (positionFactor * 169 + 16 + 3 + 144 <= response.length()) {
if(tab1Used == false) {
tabPosition = 0;
TabLayout.Tab currentTab = tabLayout.getTabAt(tabPosition);
currentTab.setText(response.substring(positionFactor * 169, positionFactor * 169 + 16));
dataTextTab1.setText(response.substring(positionFactor * 169 + 16 + 3, positionFactor * 169 + 16 + 3 + 144));
tab1Used = true;
Log.e(TAG, "1ST TAB REACHED");
}
else if(tab1Used == true && tab2Used == false){
tabPosition = 1;
TabLayout.Tab currentTab = tabLayout.getTabAt(tabPosition);
currentTab.setText(response.substring(positionFactor * 169, positionFactor * 169 + 16));
dataTextTab2.setText(response.substring(positionFactor * 169 + 16 + 3, positionFactor * 169 + 16 + 3 + 144));
tab2Used = true;
Log.e(TAG, "2ND TAB REACHED");
}
else if(tab1Used == true && tab2Used == true && tab3Used == false){
tabPosition = 2;
TabLayout.Tab currentTab = tabLayout.getTabAt(tabPosition);
currentTab.setText(response.substring(positionFactor * 169, positionFactor * 169 + 16));
dataTextTab3.setText(response.substring(positionFactor * 169 + 16 + 3, positionFactor * 169 + 16 + 3 + 144));
tab3Used = true;
Log.e(TAG, "3RD TAB REACHED");
}
if(tab1Used == true && tab2Used == true && tab3Used == true){ //If there's data in all tabs => overwrite oldest.
tabPosition = 0;
TabLayout.Tab currentTab = tabLayout.getTabAt(tabPosition);
currentTab.setText(response.substring(positionFactor * 169, positionFactor * 169 + 16));
dataTextTab1.setText(response.substring(positionFactor * 169 + 16 + 3, positionFactor * 169 + 16 + 3 + 144));
Log.e(TAG, "1ST TAB OVER AGAIN");
tab2Used = false;
tab3Used = false;
}
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, "Error in data retrieval", Toast.LENGTH_LONG).show();
}
});
queue.add(stringRequest);
}
public void refresh(View view){ //Refresh action for FAB = onClick fct.
MainActivity.positionFactor++;
Toast.makeText(context, "Page refreshed", Toast.LENGTH_SHORT).show();
receiveString();
}
@Override
public void onFragmentInteraction(Uri uri) {
}
public static void showInfoPopup() { //Show popup info = onClick fct.
if (Menu.infoPopupDialog != null) {
Menu.infoPopupDialog.setContentView(R.layout.popup_layout);
Menu.infoPopupDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
Menu.infoPopupDialog.show();
}
}
public static void showLocationPopup(){ //Show popup location = onClick fct.
if(Menu.locationPopupDialog != null){
Menu.locationPopupDialog.setContentView(R.layout.popup_location);
Menu.locationPopupDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
Menu.locationPopupDialog.show();
}
}
PagerAdapter:
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNoOfTabs;
public PagerAdapter(FragmentManager fm, int numberOfTabs){
super(fm);
this.mNoOfTabs = numberOfTabs;
}
@Override
public Fragment getItem(int position) {
switch(position){
case 0:
Tab1 tab1 = new Tab1();
return tab1;
case 1:
Tab2 tab2 = new Tab2();
return tab2;
case 2:
Tab3 tab3 = new Tab3();
return tab3;
default:
return null;
}
}
@Override
public int getCount() {
return mNoOfTabs;
}
}
Tab1:
public class Tab1 extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public Tab1() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Tab1.
*/
// TODO: Rename and change types and number of parameters
public static Tab1 newInstance(String param1, String param2) {
Tab1 fragment = new Tab1();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_tab1, container, false);
MainActivity.dataTextTab1 = rootView.findViewById(R.id.dataTextTab1);
return rootView;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
Tab2:
public class Tab2 extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public Tab2() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Tab2.
*/
// TODO: Rename and change types and number of parameters
public static Tab2 newInstance(String param1, String param2) {
Tab2 fragment = new Tab2();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_tab2, container, false);
MainActivity.dataTextTab2 = rootView.findViewById(R.id.dataTextTab2);
return rootView;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
Tab3:
public class Tab3 extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public Tab3() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Tab3.
*/
// TODO: Rename and change types and number of parameters
public static Tab3 newInstance(String param1, String param2) {
Tab3 fragment = new Tab3();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_tab3, container, false);
MainActivity.dataTextTab3 = rootView.findViewById(R.id.dataTextTab3);
return rootView;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context="com.myapps.toualbiamine.weathertracker.MainActivity">
<!--<android.support.v7.widget.Toolbar-->
<!--android:id="@+id/toolbar"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="50dp"-->
<!--android:background="@color/colorPrimary"-->
<!--android:minHeight="?attr/actionBarSize"-->
<!--android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"-->
<!--app:popupTheme="@style/ThemeOverlay.AppCompat.Light">-->
<!--<TextView-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:text="Swipe to switch tabs"-->
<!--android:textSize="30dp"/>-->
<!--</android.support.v7.widget.Toolbar>-->
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@color/textColor"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="@+id/pager"
android:background="@color/background">
</android.support.v4.view.ViewPager>
</LinearLayout>
fragment_tab3.xml - fragment_tab1.xml & fragment_tab2.xml are simple copies:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.myapps.toualbiamine.weathertracker.Tab1"
android:orientation="vertical"
android:layout_weight="1"
android:background="@drawable/bg"
android:backgroundTintMode="multiply"
android:backgroundTint="@color/background">
<!-- TODO: Update blank fragment layout -->
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Data"
android:textSize="40dp"
android:layout_gravity="center"
android:layout_marginTop="40dp"
android:textColor="@color/titleColor" />
<TextView
android:id="@+id/dataTextTab3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="120dp"
android:text="Data will come here"
android:textSize="15dp"
android:layout_gravity="center"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/floatingActionButton4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="28dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:clickable="true"
app:backgroundTint="@color/background"
app:fabSize="normal"
app:srcCompat="@drawable/ic_refresh_black_24dp"
android:onClick="refresh"/>
</RelativeLayout>
</LinearLayout>
So, I've been trying to figure out a way around my problem & have done a lot of reading but I've been stuck here for now a week & it's the last part of my project.
Here is the issue. You can see that in my MainActivity
class, in the method receiveString()
, there is the following line:
dataTextTab3.setText(response.substring(positionFactor * 169 + 16 + 3, positionFactor * 169 + 16 + 3 + 144));
I used the same line for my two other TextViews dataTextTab1
& dataTextTab2
a few lines before in this same method.
This is the part where I parse the data that I want received from the website. The numbers correspond to a formula to determine the indexes of the substring I set the text to.
It works perfectly fine for my two other TextViews but when I reach the third TextView, dataTextTab3
, I get a NullPointerException & I can't get my head around it to figure it out.
StackTrace:
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.myapps.toualbiamine.weathertracker, PID: 22340 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.myapps.toualbiamine.weathertracker.MainActivity$2. onResponse(MainActivity.java:139) at com.myapps.toualbiamine.weathertracker.MainActivity$2 .onResponse(MainActivity.java:108) at com.android.volley.toolbox.StringRequest. deliverResponse(StringRequest.java:60) at com.android.volley.toolbox.StringRequest. deliverResponse(StringRequest.java:30) at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable. run(ExecutorDelivery.java:99) at android.os.Handler.handleCallback(Handler.java:790) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller. run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
One more thing, I get that error if I refresh 3 times from the first tab to logically fill all the tabs at once, however, if I refresh the first tab, then go to the second one & refresh it, and then go to the third one & refresh it, I do not get the NullPointerException.
Is there a way to avoid that NPE whatever happens? I would like to make sure that the user can refresh 3 times from the first tab without having the app crashing.
Thank you very much, Community! Please, I place a lot of hope into you right now, be the hero I need.
PSA: not duplicate of "why am I getting a NPE" since I went through most of the posts & I am explicitly declaring and instantiating my variables.