0

I am a beginner. I am programming a weather app in android. I want to call fragment method in activity, but it have a error

And my main activity code here:

public class MainActivity extends FragmentActivity {
    ViewPager viewpager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewpager = (ViewPager)findViewById(R.id.viewPager);
        MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager());
        viewpager.setAdapter(adapter);
    }

    private class MyFragmentAdapter extends FragmentPagerAdapter{

        public MyFragmentAdapter(FragmentManager fm) {
            super(fm);
            // TODO Auto-generated constructor stub
        }

        @Override
        public Fragment getItem(int position) {
            // TODO Auto-generated method stub
            switch (position) {
            case 0:
                return new WeatherCurrentFragment();
            case 1:
                return new WeatherForeCastFragment();
            }
            return null;
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return 2;
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.weather, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        if(item.getItemId() == R.id.change_city){
            showInputDialog();
        }
        return super.onOptionsItemSelected(item);
    }

    private void showInputDialog(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Change city");
        final EditText input = new EditText(this);
        input.setInputType(InputType.TYPE_CLASS_TEXT);
        builder.setView(input);
        builder.setPositiveButton("Go", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                changeCity(input.getText().toString());
            }
        });
        builder.show();
    }

    public void changeCity(String city){
         WeatherCurrentFragment cf = (WeatherCurrentFragment)getSupportFragmentManager().findFragmentById(R.id.currentweather);
         cf.updateWeatherData(city);
         WeatherForeCastFragment ff = (WeatherForeCastFragmen)getSupportFragmentManager().findFragmentById(R.id.forecast);
         ff.updateWeatherData(city);
         new CityPreference(this).setCity(city);
    }
}

And is show an error

07-18 08:59:34.755: E/AndroidRuntime(2114): java.lang.NullPointerException: Attempt to invoke virtual method 'void example.giaodien1.WeatherCurrentFragment.updateWeatherData(java.lang.String)' on a null object reference

As you see I want to call method one from CurrentWeatherFragment and one from ForecastWeatherFragment when I click button.

mixel
  • 25,177
  • 13
  • 126
  • 165
viet khoa
  • 31
  • 4

2 Answers2

2

Code following:

getSupportFragmentManager().findFragmentById(R.id.currentweather);

return a null object reference.

This question may help you, findFragmentById return null

By the way normally callback interface is used instead of calling a method that belongs to fragment in an activity like this:

public interface OnCityChangedListener(){
    public void onCityChanged(String city);
}

Implement this interface in Fragment:

public MyFragment extends Fragment implements OnCityChangedListener

and override this method in Fragment:

@override
public void onCityChanged(String city){
    updateWeatherData(city);
}

Instantiate fragments in activity, use an ArrayList to save them in activity, pass this ArrayList to constructor of PageAdapter,

public MyFragmentAdapter(FragmentManager fm, ArrayList<Fragment> fs) {
    super(fm);
    this.fs = fs;
}

When clicking button to change city:

public void changeCity(String city){
    fs.onCityChanged(city);
}
Community
  • 1
  • 1
grantonzhuang
  • 557
  • 4
  • 6
0

The thing is that FragmentPagerAdapter is managing fragments via FragmentManager that is passed to FragmentPagerAdapter constructor.

So than you call getSupportFragmentManager().findFragmentById() there is no guarantee that it does not return null.

Better designed approach is to make CityPreference singleton object and instantiate it in Application.onCreate() method:

public class WeatherApplication extends Application {
    private static WeatherApplication instance;
    private CityPreference cityPreference;

    public static WeatherApplication getInstance() {
        return instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        cityPreference = new CityPreference(this);
    }

    public CityPreference getCityPreference() {
        return cityPreference;
    }    
}

Then you should add listeners for "city changed" event:

class CityPreference {
    public CityPreference(Context context) {
        // initialize SharedPreferences or any other storage
    }

    private final List<CityChangedListener> listeners = new ArrayList<>();

    public void addListener(CityChangedListener listener) {
        listeners.add(listener);
    }

    public void removeListener(CityChangedListener listener) {
        listeners.remove(listener);
    }

    public void setCity(String city) {
        // save city to shared preferences or some other place

        for (listener: listeners) {
            listener.onCityChanged();
        }
    }

    public interface CityChangedListener {
        void onCityChanged();
    }
}

In each of your fragments make these changes:

class WeatherCurrentFragment extends Fragment implements CityChangedListener {
    @Override
    public void onResume() {
        super.onResume();
        onCityChanged(); // force fragment update because city could be changed when fragment was hidden or even not yet created
        WeatherApplication.getInstance().getCityPreference().addListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        WeatherApplication.getInstance().getCityPreference().removeListener(this);
    }

    public void onCityChanged() {
        // update fragment
    }
}

And finally your MainActivity.changeCity() method:

public class MainActivity extends FragmentActivity {
    // other code

    private void changeCity(String city) {
        WeatherApplication.getInstance().getCityPreference().setCity(city);
    }
}
mixel
  • 25,177
  • 13
  • 126
  • 165