如何从我的 Activity 中调用 Fragment 的 TextViews 和 Button?
Posted
技术标签:
【中文标题】如何从我的 Activity 中调用 Fragment 的 TextViews 和 Button?【英文标题】:How to Call my Fragment's TextViews and Button from my Activity? 【发布时间】:2021-04-16 14:42:37 【问题描述】:首先,我还是 android 开发的新手,我在 How to make my Fragment make use of my Activity Data? 和 How to send data from Activity to Fragment?(Android) 之前在这里问过类似的问题 但似乎大多数人都不太明白我的意思,所以让我解释一下。 假设我有一个 TextView:
<TextView
android:id="@+id/textView3"
android:layout_
android:layout_
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_
android:layout_
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 layout.xml 中,然后我想从我的 MainActivity 即 current_temp = findViewById(R.id.textView10);
中调用它们以从天气 API 和 @ 获取数据即 current_temp.setText(getString(R.string.blank, response.body().getCurrent().getTemp() + " ℃"));
987654329@。
Android Studio 不允许你像那样使用它们,所以我得到这个错误:
java.lang.NullPointerException: 尝试调用虚方法 'void android.view.View.setOnClickListener(android.view.View$OnClickListener)' 在空对象引用上
每当我运行应用程序时。在运行之前我没有收到任何其他错误,所以原因是我没有实例化任何方法或回调或接口来告诉 Activity 使用我的片段的 TextViews 和 Button。这就是我几天来一直试图解决的问题,但大多数人只是误解了我并开始建议我应该学习 ViewModel 或 LiveData。我看了几个关于 ViewModel 的教程,包括 Codinginflow,他们从来没有谈到将活动链接到片段 TextViews,他们只是让一个片段将数据发送到另一个片段,这不是我想要的。 我的请求类似于这个Android: Can't update textview in Fragment from Activity. NullPointerException 尝试过但失败了,所以我需要一步一步的程序来了解如何应用它。
完整代码: 家庭活动
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 调用改造。但如果您有任何中肯的建议,我将不胜感激。
【问题讨论】:
看看这里:***.com/a/35747982/6383029 所以你想从activity中访问fragment中的button和textview? 我建议您将与first fragment
相关的整个功能带入 firstfragment
类,假设您有 3 个或更多片段,那么在活动中保留所有三个片段的功能会很困难并且会阻止使用片段,将与特定片段相关的事物放入该框架中是最好的方法。
@rahat 我不能这样做,因为我不能在我的片段中调用改造,并且活动也有一些文本视图
@Chinez “在活动中使用它们”是什么意思?您希望它从活动中更新吗?
【参考方案1】:
我建议你有两种方法可以将数据从 Activity 更新到 Fragment 的文本视图。
1.从Activity调用方法更新Fragment的Text View(简单方式)
在片段类中创建方法updateData()
将数据更新到文本视图。
你在activity类中声明了一个fragment
参数,并在将fragment添加到activity时赋值给这个参数。
当你在activity中接收到来自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_
android:layout_>
<TextView
android:id="@+id/temp"
android:layout_
android:layout_
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_
android:layout_
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_
android:layout_
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_
android:layout_>
<FrameLayout
android:id="@+id/container"
android:layout_
android:layout_ />
</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。使用视图模型
使用 livedata 参数创建一个 SharedViewModel 类。
在活动中,在 onCreate()
上创建一个 SharedViewModel 参数,如下所示:
SharedViewModel viewModel = new SharedViewModel(this).get(SharedViewModel .class);
在片段中,在 onActivityCreated()
上创建一个 SharedViewModel 参数,如下所示:
SharedViewModel viewModel = new SharedViewModel(requireActivity()).get(SharedViewModel .class);
最后,您在活动和片段中拥有相同的 ViewModel 实例,因为它们都使用活动上下文。当您从 api 接收数据时,更新活动中的 livedata 参数,fragment 也接收到 livedata 参数onChanged
事件,然后您可以更新 Text View。
【讨论】:
对于没有 1,你没有告诉我怎么做,没有 2 你没有告诉我把它们放在哪里,我把它们都放在了 oncreate 和得到无法解析符号'SharedViewModel'跨度> 好的。在第一个中,我在“yourData”中添加了什么?例如,查看我的文本视图? 我是像我的文本视图和按钮那样写,比如 time_zone、time_field、click 还是我只是使用“yourData”呢? @Chinez "yourData" 是来自 api 的响应数据。您的所有文本视图都在片段上,对吗?并且您想从活动中的 api 发送数据到片段以使用此数据更新文本视图字段?如果可以的话,你必须把所有的文本视图放到fragment中,然后在activity中调用api,将api数据传递给“你的数据”。 @Chinez 通常,您不应该在活动中调用片段视图。相反,您从片段中调用一个函数,该函数使用数据更新所有文本视图。如果您仍想从活动中调用片段视图,则必须按照第 1 步和第 2 步进行操作。然后当您收到来自 api 的响应时,您可以像这样调用视图fragment.getView(). findViewById(R.id.textView9).setText(response.body().getTimezone());
【参考方案2】:
有很多方法可以实现你想做的事情,我将使用 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();
);
【讨论】:
首先,我一步一步地按照你的所有方法。除了步骤(5)之外,一切都运行良好。当我将它放在我的片段的 oncreate 方法中时,他们说:''的结果被忽略,例如'getCurrent_output()'的结果被忽略。另外,您没有解决按钮 (findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() )。那么在使用这个 rxjava 之前我应该删除什么? 是的,它会忽略结果。要获得结果,您需要存储例如String myReport = weatherReportFromActivity.getCurrent_output();
,然后使用它来设置您的textView
。或者您可以直接将您的textView
放在subscribe
方法中
从未使用过变量“myReport”
您要更新的文本视图在哪里?
你不能调用 Fragment 的视图,除非你有它的引用。在fragment
中使用textView
更新您的代码【参考方案3】:
您收到 NullPointerException 是因为 findViewById(R.id.button2)
返回 null。
activity.findViewById(...)
调用使用活动的视图作为根 - 它正在搜索没有按钮 2 的 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 服务,使用构造函数或捆绑包将它们传递给片段,然后使用它们在 onCreateView()
中设置 onClickListener
。
RxJava 和 SharedView 方法是让侦听器卡在所有片段生命周期调用上的替代方法,而不是让片段更具反应性的替代架构。
这需要androidx.lifecycle.common
依赖。如果您使用的是 gradle,那将是:
dependencies
implementation 'androidx.lifecycle:lifecycle-common:2.3.0-rc1'
【讨论】:
我很欣赏你解释它的方式。但是在使用上述代码后,我在片段和活动中都遇到了错误。 '无法解析符号 FragmentLifeCycleCallBack',无法解析符号 'ButtonView' 和 ''if' 语句的主体为空'(在片段中)。然后在activity中'cannot resolve symbol SupportFragmentManger'和'cannot resolve symbol registerFragmentLifecycleCallbacks'。 除了按钮之外,我还有来自片段类的文本视图,我在我的活动中调用它,例如 current_output.setText(response.body().getCurrent().getWeather()。 get(0).getDescription());我不明白 //Do your setup listener here part 我在哪里创建类 SetupCallback extends FragmentLifeCycleCallback from。因为我在我的片段的 oncreateView 中尝试过 @Chinez 的编辑回答了那些 cmets 吗?【参考方案4】:创建一个类来获取改造客户端,如下所示
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);
现在你可以像这样在片段中调用 apis
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)
);
);
【讨论】:
好的,但是请你用Java重写同样的代码吗? var、val 和 fun 在我的代码上不起作用 我从片段类中得到 7 个错误 - getRetrofitInstance 在 RetrofitClient.java 中具有私有访问权限,从未使用变量 getCurrentData,无法解析符号 lat、lon、appid 和 RetrofitClient 类中的 2 个错误 -无法解析符号 ServiceAPI 并且从未使用私有 getRetrofitInstance 我在@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 中调用了我的API lat 和 log 是 API 的参数(纬度和经度)。还更新了我的其他问题的答案。【参考方案5】:在bundle
设置片段arguments
中设置您要更新的数据并调用newInstance
,然后相应地更新您的视图。
来自活动
FirstFragment.newInstance("yourData","yourData");
在你的片段中
if (getArguments() != null)
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
【讨论】:
我不知道如何 我有 12 个 TextView 和一个按钮,并且只提供了两个数据(在“yourdata”、“yourdata”中)或者我该怎么做? 将您在 API 回调中获得的响应对象作为片段中的参数发送,然后使用数据更新您的视图 为了让我从 API 回调中获得响应,我必须将活动链接到我想要实现的片段以上是关于如何从我的 Activity 中调用 Fragment 的 TextViews 和 Button?的主要内容,如果未能解决你的问题,请参考以下文章
如何从Xamarin Android Activity中调用MvxViewModel?
从 Fragment 开始ActivityForResult() 并完成子 Activity,不会在 Fragment 中调用 onActivityResult()