Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
197 views
in Technique[技术] by (71.8m points)

java - How to Call my Fragment's TextViews and Button from my Activity?

To start with, I'm still new to Android development, I've asked similar questions here before How to make my Fragment make use of my Activity Data? and How to send data from Activity to Fragment?(Android) But it seems that most people did not quite understand what I mean, so let me explain better. Suppose I have a TextView:

<TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="@string/current"
            android:textColor="#FF3D19"
            android:textSize="24sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

and a Button:

<Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="14dp"
            android:layout_marginTop="92dp"
            android:shadowColor="#FFFFFF"
            android:text="Click"
            android:textColor="#FF5722"
            app:layout_constraintStart_toStartOf="@+id/imageView3"
            app:layout_constraintTop_toTopOf="@+id/imageView3" /> 

in my FirstFragment layout.xml, then i want to call them from my MainActivity i.e current_temp = findViewById(R.id.textView10); to get data i.e current_temp.setText(getString(R.string.blank, response.body().getCurrent().getTemp() + " ℃")); from a weather API and findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {. Android Studio will not allow you to just use them like that, so i get this error:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.setOnClickListener(android.view.View$OnClickListener)' on a null object reference

whenever I run the app. I don't get any other error before running, so the reason is that I've not instantiated any method or maybe callback or interface that will tell the Activity to make use of my fragment's TextViews and Button. So that's what I've been stuck with for days trying to fix, but most people just misunderstood me and started suggesting I should learn ViewModel or LiveData. I watched several tutorials on ViewModel including Codinginflow, they never talked about linking activity to fragment TextViews, they only made a fragment send data to another fragment which is not what I want. My request is similar to this Android: Can't update textview in Fragment from Activity. NullPointerException tried and failed, So I need step by step procedures on how to apply it.

Full Code: HomeActivity

public class HomeActivity extends AppCompatActivity {
    public static String BaseUrl = "http://api.openweathermap.org/";
    public static String AppId = "";
    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);

        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(getString(R.string.blank, response.body().getCurrent().getTemp() + " ℃"));
                            current_output.setText(response.body().getCurrent().getWeather().get(0).getDescription());
                            rise_time.setText(getString(R.string.blank, response.body().getCurrent().getSunrise() + " AM"));
                            set_time.setText(getString(R.string.blank, response.body().getCurrent().getSunset() + " PM"));
                            temp_out.setText(getString(R.string.blank, response.body().getCurrent().getTemp() + " ℃"));
                            Press_out.setText(getString(R.string.blank, response.body().getCurrent().getPressure() + " hpa"));
                            Humid_out.setText(getString(R.string.blank, response.body().getCurrent().getHumidity() + " %"));
                            Ws_out.setText(getString(R.string.blank, response.body().getCurrent().getWindSpeed() + " Km/h"));
                            Visi_out.setText(getString(R.string.blank, response.body().getCurrent().getVisibility() + " m"));
                        }
                    }

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

FirstFragment

public class FirstFragment 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;

public FirstFragment() {
    // 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 SecondFragment.
 */
// TODO: Rename and change types and number of parameters
public static FirstFragment newInstance(String param1, String param2) {
    FirstFragment fragment = new FirstFragment();
    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
    return inflater.inflate(R.layout.fragment_first, container, false);
}
}

Taking the whole functionality to Fragment won't work too, because retrofit can only be called from Activity. But if you have any solid suggestions, I'll appreciate it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I suggest you two ways can update data to Text View of Fragment from Activity.

1. Call method update Text View of fragment from Activity (simple way)

  • Create a method updateData()in fragment class to update data to Text View.
  • You declare a fragment parameter in activity class and assign to this parameter when add fragment to activity.
  • When you receive data from api in activity, call fragment.updateData()

fragment_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <TextView
        android:id="@+id/temp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="TextView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/hud"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="TextView"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/temp" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

home_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

FirstFragment.kt

class FirstFragment : Fragment(R.layout.fragment_layout) {

    private lateinit var temp: TextView
    private lateinit var hud: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        temp = view.findViewById(R.id.temp)
        hud = view.findViewById(R.id.hud)
    }

    fun setDataToView(data: WeatherResponse) {
        temp.text = data.temp
        hud.text = data.hud
    }
}

HomeActivity.kt

class HomeActivity: AppCompatActivity() {

    private val fragment = FirstFragment()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.home_activity)

        addFragment()

        fragment.view?.findViewById<Button>(R.id.button2)?.setOnClickListener {
            getCurrentData()
        }


    }

    private fun addFragment(){
        val fm = supportFragmentManager
        fm.beginTransaction().replace(R.id.container, fragment, "FirstFragment").commit()
    }

    private fun getCurrentData(){
        //Your retrofit code in here. I only show code in onResponse()
        //.....
        @Override
        public void onResponse(@NonNull Call<WeatherResponse> call, @NonNull Response<WeatherResponse> response) {
            if (response.code() == 200) {
                fragment.setDataToView(response)
            }
        }

        //....
    }
}

2. Using ViewModel

  • Create a SharedViewModel class with a livedata parameter.

  • In activity, on onCreate() you create a SharedViewModel paramerter like below:

    SharedViewModel viewModel = new SharedViewModel(this).get(SharedViewModel .class);

  • In fragment, on onActivityCreated() you create a SharedViewModel paramerter like below:

    SharedViewModel viewModel = new SharedViewModel(requireActivity()).get(SharedViewModel .class);

  • Finally, you have same ViewModel instance in activity and fragment because both using activity context. When you receive data from api, update your livedata parameter in activity, fragment also receives livedata parameter onChanged event and then you can update Text View.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...