How to make my Fragment make use of my Activity Data?

孤街醉人 提交于 2021-02-11 12:48:53

问题


I have an Activity hosting 3 Fragments, my activity contains some data that I want my first fragment to use. I tried invoking the following method so that the activity can communicate with the fragment:

FirstFragment fragmentFirst = (FirstFragment)
                getSupportFragmentManager().findFragmentById(R.id.firstFragment);
        assert fragmentFirst != null;
        fragmentFirst.doSomething("some param");

For the activity class and:

public void doSomething(String param) {
        // do something in fragment
    }

For the fragment class, but it brought out an: Attempt to invoke virtual method 'void com.tex.lightweatherforecast.FirstFragment.doSomething(java.lang.String)' on a null object reference error at runtime.

My Activity code is:

public class HomeActivity extends AppCompatActivity {
    public static String BaseUrl = "http://api.openweathermap.org/";
    public static String AppId = "9c547bfc852923c3b30d0d62a5ae35e8";
    public static String lat = "9.0574";
    public static String lon = "7.4898";
    // User Timezone name, current time, current temperature, current condition, sunrise, sunset, temperature, pressure, humidity, wind_speed, visibility, UV Index
    TextView time_zone, time_field, current_temp, current_output, rise_time, set_time, temp_out, Press_out, Humid_out, Ws_out, Visi_out, UV_out;
    ConstraintLayout constraintLayout;
    public static int count=0;
    int[] drawable =new int[]{R.drawable.dubai,R.drawable.central_bank_of_nigeria,R.drawable.eiffel_tower,R.drawable.hong_kong,R.drawable.statue_of_liberty};
    Timer _t;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        FirstFragment fragmentFirst = (FirstFragment)
                getSupportFragmentManager().findFragmentById(R.id.firstFragment);
        assert fragmentFirst != null;
        fragmentFirst.doSomething("some param");

        time_zone = findViewById(R.id.textView9);
        time_field = findViewById(R.id.textView4);
        current_temp = findViewById(R.id.textView10);
        current_output = findViewById(R.id.textView11);
        rise_time = findViewById(R.id.textView25);
        set_time = findViewById(R.id.textView26);
        temp_out = findViewById(R.id.textView28);
        Press_out = findViewById(R.id.textView29);
        Humid_out = findViewById(R.id.textView30);
        Ws_out = findViewById(R.id.textView33);
        Visi_out = findViewById(R.id.textView34);
        UV_out = findViewById(R.id.textView35);

        BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
        NavController navController = Navigation.findNavController(this, R.id.fragment);
        NavigationUI.setupWithNavController(bottomNavigationView, navController);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getCurrentData();
                constraintLayout = findViewById(R.id.layout);
        constraintLayout.setBackgroundResource(R.drawable.dubai);
        _t = new Timer();
        _t.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() { // run on ui thread
                    @Override
                    public void run() {
                        if (count < drawable.length) {

                            constraintLayout.setBackgroundResource(drawable[count]);
                            count = (count + 1) % drawable.length;
                        }
                    }
                });
            }
        }, 5000, 5000);
    }

            void getCurrentData() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BaseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    WeatherService service = retrofit.create(WeatherService.class);
    Call<WeatherResponse> call = service.getCurrentWeatherData(lat, lon, AppId);
        call.enqueue(new Callback<WeatherResponse>() {
            @Override
            public void onResponse(@NonNull Call<WeatherResponse> call, @NonNull Response<WeatherResponse> response) {
                if (response.code() == 200) {
                    WeatherResponse weatherResponse = response.body();
                    assert weatherResponse != null;

                    assert response.body() != null;
                    time_zone.setText(response.body().getTimezone());
                    time_field.setText(response.body().getCurrent().getDt());
                    current_temp.setText(response.body().getCurrent().getTemp() + " ℃");
                    current_output.setText(response.body().getCurrent().getWeather().get(0).getDescription());
                    rise_time.setText(response.body().getCurrent().getSunrise() + " AM");
                    set_time.setText(response.body().getCurrent().getSunset() + " PM");
                    temp_out.setText(response.body().getCurrent().getTemp() + " ℃");
                    Press_out.setText(response.body().getCurrent().getPressure() + " hpa");
                    Humid_out.setText(response.body().getCurrent().getHumidity() + " %");
                    Ws_out.setText(response.body().getCurrent().getWindSpeed() + " Km/h");
                    Visi_out.setText(response.body().getCurrent().getVisibility() + " m");

                }
            }

            @Override
            public void onFailure(@NonNull Call<WeatherResponse> call, @NonNull Throwable t) {
            }
        });
            }
        });
    }
}

Any help will be Appreciated


回答1:


I think you are getting this error because the fragment is not active. and if it's active and this approach worked, it's pretty bad approach, there are other ways in when we use the new LiveData and ViewModel arch style.

If you think that DataSource(LiveData usually) will be needed by children(Fragments) make this DataSource owned by the Parent (activity) and keep watching that DataSource inside the fragment(i.e. make observers of this LiveData inside different fragments)

If you need to make a callback from the Fragment to the activity use Contracts(Interfaces) and implement that contract inside the activity(this contract might contain functions like doSomething()) and call that from inside the fragment like this:

(((ContractName)activity).doSomething(passData))



回答2:


If you want to pass activity data in your first destination fragment, avoid setting up graph in activity layout file.

Remove graph implementation from activity layout. And implement it as below in activity's onCreate method.

    NavController navController = Navigation.findNavController(this, R.id.nav_controller);
    Bundle bundle = new Bundle();
    bundle.putString("key", "value");
    navController.setGraph(navController.getGraph(), bundle);

Update your graph file by referring below code.

<navigation 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:id="@+id/navigation_graph"
        app:startDestination="@id/homeFragment">

    <fragment android:id="@+id/homeFragment"
          android:name="com.java.HomeFragment"
          android:label="home_fragment"
          tools:layout="@layout/fragment_home">

            <argument 
                android:name="key"
                app:argType="string"/>

    </fragment>

    ...
    ...

</navigation>

In your home fragment, get arguments value.

 String data = getArguments().getString("key");


来源:https://stackoverflow.com/questions/65599314/how-to-make-my-fragment-make-use-of-my-activity-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!