提问人:Chinez 提问时间:1/12/2021 最后编辑:LeoChinez 更新时间:1/21/2021 访问量:922
如何从我的 Activity 中调用我的 Fragment 的 TextView 和 Button?
How to Call my Fragment's TextViews and Button from my Activity?
问:
首先,我还是 Android 开发的新手,我之前在这里问过类似的问题 如何让我的 Fragment 使用我的 Activity Data?以及如何将数据从 Activity 发送到 Fragment?(Android)但似乎大多数人不太明白我的意思,所以让我更好地解释一下。 假设我有一个 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" />
和一个按钮:
<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" />
在我的 FirstFragment 布局 .xml 中,然后我想从我的 MainActivity 调用它们,即从天气 API 和 .
Android Studio 不允许您像那样使用它们,所以我收到此错误:current_temp = findViewById(R.id.textView10);
current_temp.setText(getString(R.string.blank, response.body().getCurrent().getTemp() + " ℃"));
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
java.lang.NullPointerException:尝试调用虚拟方法“void” android.view.View.setOnClickListener(android.view.View$OnClickListener)' 在 null 对象引用上
每当我运行该应用程序时。我在运行之前没有收到任何其他错误,所以原因是我没有实例化任何方法或回调或接口来告诉 Activity 使用我的片段的 TextView 和 Button。这就是我几天来一直试图解决的问题,但大多数人只是误解了我,并开始建议我应该学习 ViewModel 或 LiveData。我看了几个关于 ViewModel 的教程,包括 Codinginflow,他们从未谈论过将活动链接到片段 TextView,他们只是让一个片段将数据发送到另一个片段,这不是我想要的。 我的请求类似于此 Android:无法从 Activity 更新 Fragment 中的文本视图。NullPointerException 尝试并失败,因此我需要有关如何应用它的分步过程。
全码: 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) {
}
});
}
});
}
}
第一个片段
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);
}
}
将整个功能带到 Fragment 中也行不通,因为只能从 Activity 调用 retrofit。但如果您有任何可靠的建议,我将不胜感激。
答:
我建议您有两种方法可以将数据更新到活动片段的文本视图。
1. 从Activity调用方法更新片段的文本视图(简单方式)
- 在 fragment 类中创建一个方法,以将数据更新到文本视图。
updateData()
- 您可以在活动类中声明一个参数,并在向活动添加片段时分配给此参数。
fragment
- 当您在活动中从 api 接收数据时,调用
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
}
}
家活动.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. 使用 ViewModel
创建一个带有 livedata 参数的 SharedViewModel 类。
在活动中,创建一个 SharedViewModel 参数,如下所示:
onCreate()
SharedViewModel viewModel = new SharedViewModel(this).get(SharedViewModel .class);
在 fragment 中,创建一个 SharedViewModel 参数,如下所示:
onActivityCreated()
SharedViewModel viewModel = new SharedViewModel(requireActivity()).get(SharedViewModel .class);
最后,Activity 和 Fragment 中具有相同的 ViewModel 实例,因为两者都使用活动上下文。当您从 api 接收数据时,在 activity 中更新您的 livedata 参数,fragment 也会接收 livedata 参数事件,然后您可以更新文本视图。
onChanged
评论
fragment.getView(). findViewById(R.id.textView9).setText(response.body().getTimezone());
在设置片段中设置要更新的数据,然后调用 然后相应地更新视图。bundle
arguments
newInstance
从活动
FirstFragment.newInstance("yourData","yourData");
在您的 Fragment 中
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
评论
有很多方法可以实现你要做的事情,我将使用 RxJava。
- 添加您的项目:
RxJava
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
- 创建一个 POJO 类来保存您的数据,然后以后可以使用总线传输这些数据
RxJava
public class WeatherReport {
private String time_zone;
private String time_field;
private String current_temp;
private String current_output;
private String rise_time;
private String set_time;
private String temp_out;
private String Press_out;
private String Humid_out;
private String Ws_out;
private String Visi_out;
public String getCurrent_output() {
return current_output;
}
public String getCurrent_temp() {
return current_temp;
}
public String getHumid_out() {
return Humid_out;
}
public String getPress_out() {
return Press_out;
}
public String getRise_time() {
return rise_time;
}
public String getSet_time() {
return set_time;
}
public String getTemp_out() {
return temp_out;
}
public String getTime_field() {
return time_field;
}
public String getTime_zone() {
return time_zone;
}
public String getVisi_out() {
return Visi_out;
}
public String getWs_out() {
return Ws_out;
}
public void setCurrent_output(String current_output) {
this.current_output = current_output;
}
public void setCurrent_temp(String current_temp) {
this.current_temp = current_temp;
}
public void setHumid_out(String humid_out) {
Humid_out = humid_out;
}
public void setPress_out(String press_out) {
Press_out = press_out;
}
public void setRise_time(String rise_time) {
this.rise_time = rise_time;
}
public void setSet_time(String set_time) {
this.set_time = set_time;
}
public void setTemp_out(String temp_out) {
this.temp_out = temp_out;
}
public void setTime_field(String time_field) {
this.time_field = time_field;
}
public void setTime_zone(String time_zone) {
this.time_zone = time_zone;
}
public void setVisi_out(String visi_out) {
Visi_out = visi_out;
}
public void setWs_out(String ws_out) {
Ws_out = ws_out;
}
}
- 创建 RxJava 总线
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public class RxJavaBus {
private static final BehaviorSubject<WeatherReport> behaviorSubject
= BehaviorSubject.create();
public static BehaviorSubject<WeatherReport> getSubject() {
return behaviorSubject;
}
}
- 现在,从您的活动(或从您想要传输数据的任何位置)使用如下:
RxBus
WeatherReport weatherReport = new WeatherReport();
weatherReport.setCurrent_output("Some DATA");
weatherReport.setCurrent_temp("Some DATA");
weatherReport.setHumid_out("Some DATA");
weatherReport.setPress_out("Some DATA");
weatherReport.setRise_time("Some DATA");
weatherReport.setSet_time("Some DATA");
weatherReport.setTime_field("Some DATA");
weatherReport.setVisi_out("Some DATA");
weatherReport.setTemp_out("Some DATA");
weatherReport.setWs_out("Some DATA");
weatherReport.setTime_zone("some DATA");
RxJavaBus.getSubject().onNext(weatherReport);
- 现在,您可以在片段(或您想要的任何位置)中接收数据,如下所示:
RxJavaBus.getSubject().subscribe(weatherReportFromActivity -> {
weatherReportFromActivity.getCurrent_output();
weatherReportFromActivity.getCurrent_temp();
weatherReportFromActivity.getHumid_out();
weatherReportFromActivity.getPress_out();
weatherReportFromActivity.getRise_time();
weatherReportFromActivity.getSet_time();
weatherReportFromActivity.getWs_out();
weatherReportFromActivity.getTime_zone();
weatherReportFromActivity.getVisi_out();
weatherReportFromActivity.getTime_field();
weatherReportFromActivity.getTemp_out();
});
评论
String myReport = weatherReportFromActivity.getCurrent_output();
textView
textView
subscribe
textView
fragment
你收到一个 NullPointerException,因为返回 null。findViewById(R.id.button2)
- 您需要使用片段的视图作为根。调用使用活动的视图作为根 - 它正在搜索,它没有 button2。您可以使用 访问片段的版本,但是:
activity.findViewById(...)
activity_home.xml
fragment.getView().findViewById(R.id._)
- 您需要确保片段在生命周期中完成了其步骤。所以它必须被启动和充气。否则,该按钮将再次不存在。
onCreateView()
您可以通过在片段生命周期中将其设置为回调来确保在正确的时间和视图中调用设置。
//This class can be anywhere - it can be global, private inner, anonymous
class SetupCallback extends FragmentLifeCycleCallback {
@Override
void onFragmentViewCreated(FragmentManager fm, Fragment f, View v, Bundle savedInstanceState) {
ButtonView button = v.findViewById(R.id.button2);
if(button != null) { //This gets called for all fragments so check for null
button.setOnClickListener(new OnClickListener {...});
}
//Repeat for any other elements in the view e.g.
TextView someText = v.findViewById(R.id.someTextView);
someText.setText(R.string.message);
}
}
//In the activity setup - I usually put in onCreate()
SupportFragmentManager.supportFragmentManager = getSupportFragmentManager();
supportFragmentManager.registerFragmentLifecycleCallbacks(new SetupCallback());
//anonymous version:
//supportFragmentManager.registerFragmentLifecycleCallbacks(new FragmentLifecycleCallback() { /*contents of class*/ });
或者,您可以在 Activity 中初始化 Retrofit 和 Weather 服务,将它们与构造函数或捆绑包一起传递到片段中,然后使用它们来设置 .onClickListener
onCreateView()
RxJava 和 SharedView 方法是让侦听器卡在所有片段生命周期调用上的替代方案,而不是使用使片段更具响应性的替代架构。
这需要依赖关系。如果您使用的是 gradle,那将是:androidx.lifecycle.common
dependencies {
implementation 'androidx.lifecycle:lifecycle-common:2.3.0-rc1'
}
评论
创建一个类来获取改造客户端,如下所示
class RetrofitClient {
private Retrofit retrofit;
private String BASE_URL = "";
public ServiceAPI getRetrofitInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit.create(WeatherService.class);
}
现在你可以像这样在 fragment 中调用 api
WeatherService service = RetrofitClient().getRetrofitInstance()
void getCurrentData() {
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) {
}
});
}
});
}
评论
first fragment
firstfragment