【问题标题】:NullPointerException on TextView from Fragment来自 Fragment 的 TextView 上的 NullPointerException
【发布时间】:2018-07-12 02:21:10
【问题描述】:

我目前正在从事一个项目,在该项目中,我从一个网页中检索到文本数据,该网页显示在 3 个声明为片段的不同选项卡之间。

每个选项卡底部都有一个按钮刷新,每当用户按下该按钮时,应用程序都会从​​网站检索最新数据并将其显示在三个选项卡之一中。

应用选择要写入文本的选项卡的逻辑如下:

将数据写入 Tab 1(最左侧)。如果 Tab 1 已经包含数据,则写入 Tab 2。如果 Tab 2 已经包含数据,则写入 Tab 3。如果所有选项卡都包含数据,则覆盖 Tab 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类中,在方法@​​987654330@中,有如下一行:

dataTextTab3.setText(response.substring(positionFactor * 169 + 16 + 3, positionFactor * 169 + 16 + 3 + 144));

在之前的几行中,我用同样的方法为我的另外两个 TextView dataTextTab1dataTextTab2 使用了同一行。

这是我解析希望从网站收到的数据的部分。这些数字对应于一个公式,用于确定我将文本设置为的子字符串的索引。

它对我的另外两个 TextView 非常有效,但是当我到达第三个 TextView dataTextTab3 时,我得到一个 NullPointerException 并且我无法理解它。

堆栈跟踪:

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。 交付响应(StringRequest.java:60)在 com.android.volley.toolbox.StringRequest。 交付响应(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(Native Method) 在 com.android.internal.os.RuntimeInit$MethodAndArgsCaller。 运行(RuntimeInit.java:438)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

还有一件事,如果我从第一个选项卡刷新 3 次以一次逻辑地填充所有选项卡,我会收到该错误,但是,如果我刷新第一个选项卡,然后转到第二个选项卡并刷新它,然后转到第三个并刷新它,我没有收到 NullPointerException。

有没有办法避免这种 NPE 发生?我想确保用户可以从第一个选项卡刷新 3 次而不会导致应用崩溃。

非常感谢社区!拜托,我现在对你寄予厚望,成为我需要的英雄。

PSA:不重复“我为什么要获得 NPE”,因为我浏览了大部分帖子,并且我明确声明并实例化了我的变量。

【问题讨论】:

  • 不,我知道如何解决常规 NPE,或者如何声明和实例化变量。这不是这里的问题。
  • 您在调试时检查过 dataTextTab3 = (TextView) findViewById(R.id.dataTextTab3); 中究竟得到了什么
  • @AmanChhabra,我得到一个空引用,因为没有创建 TextView,即使我实例化并正确声明了所有内容。

标签: java android nullpointerexception


【解决方案1】:

当您启动 MainActivity 时,通常它会实例化可见片段 (tab1) 和下一个片段 (tab2),但在您切换选项卡之前不会超出此范围。这会在您已经调用onCreate 之后发生。这意味着在您的onCreate 中,dataTextTab3 的值为 null

你不应该在你的活动中持有对片段中视图的引用,因为它们不能保证一直存在。

如果你想强制它们存在,你可以调用viewPager.setOffscreenPageLimit(3);,让更多的离屏片段在启动时被实例化。

【讨论】:

  • 有没有办法在创建第一个选项卡时强制创建dataTextTab3,即使用户必须稍等片刻?
  • 你可以尝试设置viewPager.setOffscreenPageLimit(3); 我不知道这是否会强制它自动实例化更多的屏幕外页面。
  • 兄弟,你就是那个男人。解决了。它神奇地强制实例化所有片段,我现在可以访问我的变量。非常感谢。
  • 很高兴它有帮助。我也编辑了答案以包含该修复程序。
猜你喜欢
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
  • 1970-01-01
  • 1970-01-01
  • 2015-07-10
相关资源
最近更新 更多