我目前正在一个项目中,在该项目中,我从一个网页检索了文本数据,该文本数据显示在声明为片段的3个不同选项卡之间。
每个选项卡底部都有一个按钮刷新,每当用户按下该按钮时,应用程序就会从网站上检索最新数据并将其显示在三个选项卡之一中。
应用选择标签以将文本写入其中的逻辑如下:
将数据写入选项卡1(最左侧)。如果选项卡1已经包含数据,则写入选项卡2。如果选项卡2已经包含数据,则写入选项卡3。如果所有选项卡都包含数据,则覆盖选项卡1中的数据并重新开始循环。
这是我遇到问题之前的代码。我只是发布相关代码,以避免泛滥此消息。
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是简单副本:
<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>
因此,我一直在尝试找出解决问题的方法,并且已经做了很多阅读,但是我已经被困在这里一周了,这是我项目的最后一部分。
这是问题所在。您可以在我的MainActivity
类的receiveString()
方法中看到以下行:
dataTextTab3.setText(response.substring(positionFactor * 169 + 16 + 3, positionFactor * 169 + 16 + 3 + 144));
在同一方法中,我之前在其他两行TextViews dataTextTab1
和dataTextTab2
中使用了同一行。
这是我要解析要从网站接收的数据的部分。数字对应于确定将文本设置为的子字符串的索引的公式。
对于我的其他两个TextView来说,它工作得很好,但是当我到达第三个TextView dataTextTab3
时,我得到了NullPointerException,而且我无法解决这个问题。
StackTrace:
E / AndroidRuntime:致命异常:主要过程: com.myapps.toualbiamine.weathertracker,PID:22340 java.lang.NullPointerException:尝试调用虚拟方法 'void android.widget.TextView.setText(java.lang.CharSequence)'在 空对象引用
网址:com.myapps.toualbiamine.weathertracker.MainActivity $ 2。 onResponse(MainActivity.java:139)在 com.myapps.toualbiamine.weathertracker.MainActivity $ 2 .onResponse(MainActivity.java:108)在 com.android.volley.toolbox.StringRequest。 deliveryResponse(StringRequest.java:60)在 com.android.volley.toolbox.StringRequest。 deliveryResponse(StringRequest.java:30)在 com.android.volley.ExecutorDelivery $ ResponseDeliveryRunnable。 在以下位置运行(ExecutorDelivery.java:99) android.os.Handler.handleCallback(Handler.java:790)在 android.os.Handler.dispatchMessage(Handler.java:99)位于 android.os.Looper.loop(Looper.java:164)在 android.app.ActivityThread.main(ActivityThread.java:6494)在 java.lang.reflect.Method.invoke(本机方法)位于 com.android.internal.os.RuntimeInit $ MethodAndArgsCaller。 在以下位置运行(RuntimeInit.java:438) com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
另一件事,如果我从第一个选项卡刷新3次以一次逻辑填充所有选项卡,则会收到该错误,但是,如果我刷新第一个选项卡,则转到第二个选项卡并刷新它,然后转到第三个并刷新它,我没有得到NullPointerException。
是否有办法避免发生NPE?我想确保用户可以从第一个选项卡刷新3次,而不会导致应用崩溃。
非常感谢您,社区!求求你,我现在对你充满了希望,成为我需要的英雄。
PSA:“我为什么要获得NPE”不是重复的,因为我经历了大多数帖子,并且我明确声明并实例化了我的变量。
答案 0 :(得分:1)
启动MainActivity时,通常它将实例化可见的Fragment(tab1)和下一个片段(tab2),但不会超出该范围,直到您切换选项卡。在您致电onCreate
之后,这种情况会在以后发生。这意味着在您的onCreate
中,dataTextTab3
的值为空
您不应该在活动中保存对“片段”中视图的引用,因为不能保证它们一直存在。
如果要强制它们存在,可以调用viewPager.setOffscreenPageLimit(3);
以允许在启动时实例化更多的屏幕外片段。