尝试通过接口修改片段GUI项时出现空指针异常

时间:2021-12-17 15:43:42

I have an activity containing a ViewPager in which are located 3 fragments. When activity get location info (in the onConnected method), I would like it to pass the information to a fragment which should change the content of one its TextView.

我有一个包含ViewPager的活动,其中包含3个片段。当活动获取位置信息时(在onConnected方法中),我希望它将信息传递给一个片段,该片段应该改变其TextView的内容。

I implemented the information passing using an Interface created in the MainActivity and than the fragment implement the method needed to get the location info and modify the TextView.

我使用在MainActivity中创建的接口实现了信息传递,而不是片段实现获取位置信息和修改TextView所需的方法。

Here comes the problem: the implemented method, when called seems to not find the TextView, and it rises a NullpointerException.

问题出在这里:实现的方法,当被调用时似乎找不到TextView,并且它会出现NullpointerException。

public class FragmentTab4 extends Fragment implements MainActivity.LocationHandler {

    View view;
    TextView textView;
    public TextView location;
    public Context context;
    ListView listViewMessages22;
    MessageLayoutAdapter adapter;
    Button writeMessageButton;
    private Bundle args = new Bundle();
    protected GoogleApiClient mGoogleApiClient;

    private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
    private LocationRequest mLocationRequest;

    //public static final String TAG = FragmentTab4.class.getSimpleName();
    public static final String TAG = MainActivity.class.getSimpleName();

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        // Get the view from fragmenttab2.xml
        view = inflater.inflate(R.layout.fragmenttab4, container, false);
        context = getActivity();
        writeMessageButton = (Button) view.findViewById(R.id.buttonWriteMessage);
        writeMessageButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                Toast.makeText(view.getContext().getApplicationContext(), "Button clicked", Toast.LENGTH_SHORT).show();
                //open popup to write message
                WriteMessageDialog writeMessageDialog = new WriteMessageDialog();
                writeMessageDialog.setArguments(args);
                writeMessageDialog.show(getFragmentManager(), "iuuu" );
            }
        });
        location = (TextView) view.findViewById(R.id.textView_location);
        //Location loc = LocationService.getInstance(getActivity()).getLocation();
        location.setText("Getting location...");

        if(((MainActivity)context).mLastLocation!=null){

            Location ll = ((MainActivity)context).mLastLocation;
            location.setText("Coordinates: "+ll.getLatitude()+" "+ll.getLongitude());
        }else{

            location.setText("Nothing...");
        }
        //location.setText("Coordinats: "+loc.getLatitude()+" "+loc.getLongitude());
        //textView = (TextView) view.findViewById(R.id.textView);
        listViewMessages22 = (ListView) view.findViewById(R.id.listViewMessages20);
        List<Message> bacheca1 = new ArrayList<Message>();

        //adapter = new MessageLayoutAdapter(container.getContext(), R.layout.message, bacheca1);
        Log.d(TAG,"Adapter ok.............");
        //listViewMessages22.setAdapter(adapter);
        Log.d(TAG," ok .............");

        return view;
    }

    @Override
    public void handleNewLocation(Location loc) {

        //location.setText("Coordinates: "+loc.getLatitude()+" "+loc.getLongitude());
        Log.d(TAG,"Siamo giunti in Fragment4 - handleNewLocation "+loc.getLatitude()+" "+loc.getLongitude());
        location = (TextView) view.findViewById(R.id.textView_location);
    }

Here is the MainActivity Class

这是MainActivity类

public class MainActivity extends FragmentActivity implements
    GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener,
    LocationListener{

    ActionBar actionBar;
    ViewPager viewPager;
    Context context1;
    private LocationRequest mLocationRequest;
    public Location mLastLocation;
    ViewPagerAdapter viewPagerAdapter;

    public static final String TAG = MainActivity.class.getSimpleName();
    protected GoogleApiClient mGoogleApiClient;
    private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;

    LocationHandler mLocationHandler;
    LocationService locator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context1 = getApplicationContext();
        viewPager = (ViewPager) findViewById(R.id.pager);
        viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(viewPagerAdapter);

        // Create the LocationRequest object
        mLocationRequest = LocationRequest.create().setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY).setInterval(10 * 1000)        // 10 seconds, in milliseconds
            .setNumUpdates(20)
            .setFastestInterval(1 * 1000); // 1 second, in milliseconds

        locator = LocationService.getInstance(this);

        if(!checkPlayServices()){

            finish();
        }
        //create GoogleApiClient
        buildGoogleApiClient();
        //locator.setGoogleClient(mGoogleApiClient);

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {

            mLocationHandler = (LocationHandler) viewPagerAdapter.getItem(0);
        } catch (ClassCastException e){

            Log.d(TAG, "=== Problem connecting the fragment to the activity ===");
            e.printStackTrace();
        }
    }

    @Override
    public void onConnected(Bundle bundle) {

        Log.d(TAG, "On connected........||||||||||||");
        getLocation();
        //viewPager.setAdapter(viewPagerAdapter);
        if(mLastLocation!=null){

            mLocationHandler.handleNewLocation(mLastLocation);
            //locator.setLocation(mLastLocation);
        }else{

            mLocationHandler.alertMissingLocation();
        }

        //FragmentTab4 fragment = (FragmentTab4) viewPagerAdapter.getItem(0);
        //fragment.alertMissingLocation();
    }

This is the log output:

这是日志输出:

04-24 17:30:10.121  30668-30668/? D/MainActivity﹕ On Location changed....
04-24 17:30:10.131  30668-30668/? D/MainActivity﹕ requested new loc
04-24 17:30:10.131  30668-30668/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.vlaveglia.appwithtab, PID: 30668
java.lang.NullPointerException
        at        com.example.vlaveglia.appwithtab.bacheca.FragmentTab4.handleNewLocation(FragmentTab4.java:102)
        at com.example.vlaveglia.appwithtab.MainActivity.onLocationChanged(MainActivity.java:169)
        at com.google.android.gms.internal.zzpe$zza.handleMessage(Unknown Source)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:157)
        at android.app.ActivityThread.main(ActivityThread.java:5356)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
        at dalvik.system.NativeStart.main(Native Method)

The ViewPager is

ViewPager是

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
final int PAGE_COUNT = 4;
// Tab Titles
private String tabtitles[] = new String[] { "Bacheca", "Annunci", "Mappa" , "Prova4"};
Context context;
public ViewPagerAdapter(FragmentManager fm) {
    super(fm);
}
@Override
public int getCount() {
    return PAGE_COUNT;
}
@Override
public Fragment getItem(int position) {
    switch (position) {
        // Open FragmentTab1.java
        case 3:
            FragmentTab1 fragmenttab1 = new FragmentTab1();
            return fragmenttab1;
        // Open FragmentTab2.java
        case 1:
            FragmentTab2 fragmenttab2 = new FragmentTab2();
            return fragmenttab2;
        // Open FragmentTab3.java
        case 2:
            FragmentTab3 fragmenttab3 = new FragmentTab3();
            return fragmenttab3;
        case 0:
            FragmentTab4 fragmenttab4 = new FragmentTab4();
            return fragmenttab4;
    }
    return null;
}
@Override
public CharSequence getPageTitle(int position) {
    return tabtitles[position];
}

}

1 个解决方案

#1


0  

This is a concurrency problem. This method public void onConnected(Bundle bundle) is a callback of GoogleApiClient.ConnectionCallbacks. This does not depend on the availability of your Fragment. So handleNewLocation might be called too early before the fragment builds its view.

这是一个并发问题。此方法public void onConnected(Bundle bundle)是GoogleApiClient.ConnectionCallbacks的回调。这不取决于Fragment的可用性。因此,在片段构建其视图之前,可能会过早地调用handleNewLocation。

One way to reduce the risk of fragment not available on receiving location callback is to initiate location request after your fragment has been created. Create a listener like this

降低接收位置回调时片段不可用的风险的一种方法是在片段创建后启动位置请求。像这样创建一个监听器

public interface MyFragmentListener {
   public void onViewCreated();
}

which you call in your Fragment

你在片段中调用的

MyFragmentListener myFragmentListener;

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    myFragmentListener = (MyFragmentListener)activity;
}    

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
   myFragmentListener.onViewCreated();
}

Then you implement this listener in your Activity

然后在Activity中实现此侦听器

public class MainActivity extends FragmentActivity implements    
..., MyFragmentListener {

    @Override
    public void onViewCreated(){
        //request location, I believe it's by invoking this method
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
        mGoogleApiClient.connect();
    }
} 

As long as you don't plan to replace or remove the fragment then this method is ok. If you do then there will be possibility that the fragment is gone when the callback arrives, raising a null pointer exception. So I have personally abandoned this listener concept because as you see listeners are too tightly coupled with Activity or Fragment and their lifecycle. Since you are new to Android, I suggest you spend a bit of time reading Publisher-Subscriber pattern and its implementations like Otto or EventBus.

只要您不打算替换或删除片段,那么此方法就可以了。如果你这样做,那么当回调到来时片段可能会消失,引发空指针异常。所以我个人放弃了这个听众概念,因为你看到听众与Activity或Fragment及其生命周期紧密耦合。由于您是Android新手,我建议您花一点时间阅读Publisher-Subscriber模式及其实现,如Otto或EventBus。

And finally don't reinitialize View (e.g don't do location = (TextView) view.findViewById(R.id.textView_location); many times). Just do once in onCreateView and use the reference directly (e.g location.setText("aaa"))

最后不要重新初始化View(例如,不要做location =(TextView)view.findViewById(R.id.textView_location);很多次)。只需在onCreateView中执行一次并直接使用引用(例如location.setText(“aaa”))

#1


0  

This is a concurrency problem. This method public void onConnected(Bundle bundle) is a callback of GoogleApiClient.ConnectionCallbacks. This does not depend on the availability of your Fragment. So handleNewLocation might be called too early before the fragment builds its view.

这是一个并发问题。此方法public void onConnected(Bundle bundle)是GoogleApiClient.ConnectionCallbacks的回调。这不取决于Fragment的可用性。因此,在片段构建其视图之前,可能会过早地调用handleNewLocation。

One way to reduce the risk of fragment not available on receiving location callback is to initiate location request after your fragment has been created. Create a listener like this

降低接收位置回调时片段不可用的风险的一种方法是在片段创建后启动位置请求。像这样创建一个监听器

public interface MyFragmentListener {
   public void onViewCreated();
}

which you call in your Fragment

你在片段中调用的

MyFragmentListener myFragmentListener;

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    myFragmentListener = (MyFragmentListener)activity;
}    

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
   myFragmentListener.onViewCreated();
}

Then you implement this listener in your Activity

然后在Activity中实现此侦听器

public class MainActivity extends FragmentActivity implements    
..., MyFragmentListener {

    @Override
    public void onViewCreated(){
        //request location, I believe it's by invoking this method
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
        mGoogleApiClient.connect();
    }
} 

As long as you don't plan to replace or remove the fragment then this method is ok. If you do then there will be possibility that the fragment is gone when the callback arrives, raising a null pointer exception. So I have personally abandoned this listener concept because as you see listeners are too tightly coupled with Activity or Fragment and their lifecycle. Since you are new to Android, I suggest you spend a bit of time reading Publisher-Subscriber pattern and its implementations like Otto or EventBus.

只要您不打算替换或删除片段,那么此方法就可以了。如果你这样做,那么当回调到来时片段可能会消失,引发空指针异常。所以我个人放弃了这个听众概念,因为你看到听众与Activity或Fragment及其生命周期紧密耦合。由于您是Android新手,我建议您花一点时间阅读Publisher-Subscriber模式及其实现,如Otto或EventBus。

And finally don't reinitialize View (e.g don't do location = (TextView) view.findViewById(R.id.textView_location); many times). Just do once in onCreateView and use the reference directly (e.g location.setText("aaa"))

最后不要重新初始化View(例如,不要做location =(TextView)view.findViewById(R.id.textView_location);很多次)。只需在onCreateView中执行一次并直接使用引用(例如location.setText(“aaa”))