我的天气应用程序上有一个底部导航视图,其中包含3个面板(今天,每小时
以下是问题的说明:
我希望无论单击那些导航视图,数据都保持不变。
我试过使用https://stackoverflow.com/a/60201555/16020235建议但它失败了,只有一个例外:
java.lang.IllegalArgumentException: No view found for id 0x7f0a0116 (com.viz.lightweatherforecast:id/my_nav) for fragment ThirdFragment{90bc0de} (8e129d17-010d-41dc-9311-82e273b4e522 id=0x7f0a0116 tag=3)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:513)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3134)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3068)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:251)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:501)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1391)
at android.app.Activity.performStart(Activity.java:7157)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3037)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1861)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6819)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:497)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:912)
I/weatherforecas: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
我发现很难实现他的代码,关于这个问题的其余建议都是用kotlin编写的。
拜托我该怎么解决这件事?
以下是我的代码:
my_nav.xml(导航布局):
<?xml version="1.0" encoding="utf-8"?>
<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/my_nav"
app:startDestination="@id/firstFragment">
<fragment
android:id="@+id/firstFragment"
android:name="com.viz.lightweatherforecast.FirstFragment"
android:label="fragment_first"
tools:layout="@layout/fragment_first" />
<fragment
android:id="@+id/secondFragment"
android:name="com.viz.lightweatherforecast.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
<fragment
android:id="@+id/thirdFragment"
android:name="com.viz.lightweatherforecast.ThirdFragment"
android:label="fragment_third"
tools:layout="@layout/fragment_third" />
</navigation>
activity_主页。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"
android:id="@+id/layout"
android:background="@drawable/dubai"
tools:context=".Activity.HomeActivity">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_menu" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="409dp"
android:layout_height="599dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:navGraph="@navigation/my_nav"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
家庭活动:
public class HomeActivity extends AppCompatActivity {
// Last update time, click sound, search button, search panel.
TextView time_field;
MediaPlayer player;
ImageView Search;
EditText textfield;
// For scheduling background image change(using constraint layout, start counting from dubai, down to statue of liberty.
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);
// use home activity layout.
time_field = findViewById(R.id.textView9);
Search = findViewById(R.id.imageView4);
textfield = findViewById(R.id.textfield);
// find the id's of specific variables.
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
// host 3 fragments along with bottom navigation.
final NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
assert navHostFragment != null;
final NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView, navController);
// For scheduling background image change
constraintLayout = findViewById(R.id.layout);
constraintLayout.setBackgroundResource(R.drawable.dubai);
_t = new Timer();
_t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// run on ui thread
runOnUiThread(() -> {
if (count < drawable.length) {
constraintLayout.setBackgroundResource(drawable[count]);
count = (count + 1) % drawable.length;
}
});
}
}, 5000, 5000);
Search.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// make click sound when search button is clicked.
player = MediaPlayer.create(HomeActivity.this, R.raw.click);
player.start();
getWeatherData(textfield.getText().toString().trim());
// make use of some fragment's data
FirstFragment firstFragment = (FirstFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
firstFragment.getWeatherData(textfield.getText().toString().trim());
}
});
}
});
}
}
编辑
片段类:
public class FirstFragment extends Fragment {
// User current time, current temperature, current condition, sunrise, sunset, temperature, pressure, humidity, wind_speed, visibility, clouds
TextView current_temp, current_output, rise_time, set_time, temp_out, Press_out, Humid_out, Ws_out, Visi_out, Cloud_out;
// 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
View rootView = inflater.inflate(R.layout.fragment_first, container, false);
// For displaying weather data
current_temp = rootView.findViewById(R.id.textView10);
current_output = rootView.findViewById(R.id.textView11);
rise_time = rootView.findViewById(R.id.textView25);
set_time = rootView.findViewById(R.id.textView26);
temp_out = rootView.findViewById(R.id.textView28);
Press_out = rootView.findViewById(R.id.textView29);
Humid_out = rootView.findViewById(R.id.textView30);
Ws_out = rootView.findViewById(R.id.textView33);
Visi_out = rootView.findViewById(R.id.textView34);
Cloud_out = rootView.findViewById(R.id.textView35);
return rootView;
}
public void getWeatherData(String name) {
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<Example> call = apiInterface.getWeatherData(name);
call.enqueue(new Callback<Example>() {
@Override
public void onResponse(@NonNull Call<Example> call, @NonNull Response<Example> response) {
try {
assert response.body() != null;
current_temp.setVisibility(View.VISIBLE);
current_temp.setText(response.body().getMain().getTemp() + " ℃");
current_output.setVisibility(View.VISIBLE);
current_output.setText(response.body().getWeather().get(0).getDescription());
rise_time.setVisibility(View.VISIBLE);
rise_time.setText(response.body().getSys().getSunrise() + " ");
set_time.setVisibility(View.VISIBLE);
set_time.setText(response.body().getSys().getSunset() + " ");
temp_out.setVisibility(View.VISIBLE);
temp_out.setText(response.body().getMain().getTemp() + " ℃");
Press_out.setVisibility(View.VISIBLE);
Press_out.setText(response.body().getMain().getPressure() + " hpa");
Humid_out.setVisibility(View.VISIBLE);
Humid_out.setText(response.body().getMain().getHumidity() + " %");
Ws_out.setVisibility(View.VISIBLE);
Ws_out.setText(response.body().getWind().getSpeed() + " Km/h");
Visi_out.setVisibility(View.VISIBLE);
Visi_out.setText(response.body().getVisibility() + " m");
Cloud_out.setVisibility(View.VISIBLE);
Cloud_out.setText(response.body().getClouds().getAll() + " %");
} catch (Exception e) {
Log.e("TAG", "No City found");
current_temp.setVisibility(View.GONE);
current_output.setVisibility(View.GONE);
rise_time.setVisibility(View.GONE);
set_time.setVisibility(View.GONE);
temp_out.setVisibility(View.GONE);
Press_out.setVisibility(View.GONE);
Humid_out.setVisibility(View.GONE);
Ws_out.setVisibility(View.GONE);
Visi_out.setVisibility(View.GONE);
Cloud_out.setVisibility(View.GONE);
Toast.makeText(getActivity(), "No City found", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
t.printStackTrace();
}
});
}
}
我没有粘贴所有内容,因为我正在跟踪https://stackoverflow.com/help/minimal-reproducible-example
错误似乎是每次您在底部导航栏中选择一个项目时,片段都会重新实例化,从而创建一个新片段(类型相同,但该片段的新对象)。
我建议您在开始时创建一个包含所有片段的对象,如下所示
private final Fragment[] fragments = {
new HomeFragment(),
new MyNotesFragment(),
new PreferencesFragment()
};
然后对于底部导航侦听器部分,我们将根据单击的按钮更改片段,通过传递已经创建的片段实例而不是创建新实例。
bottom_navigation_view.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
// Checking which bottom_nav items is clicked by comparing with string names..
if (item.getTitle().toString().equals("Home")) {
// Code to change the fragments
final FragmentTransaction ft =
getFragmentManager().beginTransaction();
// we passed the already made instance of the home fragment rather than creating a new one
ft.replace(R.id.my_fragment, fragments[0], "HomeFrag");
ft.commit();
} else if (item.getTitle().toString().equals("My Notes")) {
// Code to change the fragments
final FragmentTransaction ft =
getFragmentManager().beginTransaction();
ft.replace(R.id.my_fragment, fragments[1], "MyNotesFrag");
ft.commit();
} else if (item.getTitle().toString().equals("Preferences")) {
// Code to change the fragments
final FragmentTransaction ft =
getFragmentManager().beginTransaction();
ft.replace(R.id.my_fragment, fragments[2], "PrefFrag");
ft.commit();
}
return true;
}
});
(虽然这是ViewPager库的概念,但其工作原理相同)
我想,这是因为你的FirstFrament每次都会被重新实例化。要保存状态,请修改FirstFrament的onCreate()
,如下所示:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mParam1 = savedInstanceState.getString(ARG_PARAM1);
mParam2 = savedInstanceState.getString(ARG_PARAM2);
} else if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
并将onSaveInstanceState()添加到第一个片段中:
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ARG_PARAM1, mParam1);
outState.putString(ARG_PARAM2, mParam2);
}
您的问题是您的First片段
没有正确保存其状态。根据带有片段的保存状态指南:
为了确保用户的状态被保存,Android框架会自动保存和恢复片段和背栈。因此,您需要确保片段中的任何数据也被保存和恢复。
但是,您没有保存传递给getWeatherData的最后一个名称,也没有保存从API调用获得的示例对象,以便在重新创建视图时重新填充视图。
因此需要实际使用该指南中描述的API来保存您的状态。也就是说,您应该提交应用程序架构指南,该指南解释了如何通过使用ViewModels跨配置更改存储数据(如旋转设备)和LiveData将数据从数据加载中分离出来,以便在加载数据时自动填充用户界面。
我们要做的第一件事是将数据加载移动到ViewModel
。该对象在配置更改后仍然存在,这意味着当您旋转设备时,存储在该类中的任何数据都会自动保存。这就是我们如何保存您的示例
类并避免一遍又一遍地调用服务器。
通过使用ViewModel的Saved State模块中的API(具体来说,是SavedStateHandle类),保存在其中的任何数据都将在进程被终止并稍后重新创建(例如,如果设备内存不足等)后幸存下来。这就是我们如何保存最后一个名称,以便自动重新查询您的数据。
在这里,我们的ViewModel处理来自服务器的所有加载,并使用LiveData让我们的UI在加载数据时自动更新。
public class WeatherDataViewModel extends ViewModel {
// This will save the city name
private SavedStateHandle state;
// This is where we'll store our result from the server
private MutableLiveData<Example> mutableWeatherData = new MutableLiveData<>();
public WeatherDataViewModel(SavedStateHandle savedStateHandle) {
state = savedStateHandle;
String savedCityName = state.get("name");
if (savedCityName != null) {
// We already had a previously saved name, so we'll
// start loading right away
loadData();
}
}
// This is what our Fragment will use to get the latest weather data
public LiveData<Example> getWeatherDataLiveData() {
return mutableWeatherData;
}
// When you get a new city name, we'll save that in our
// state, then load the new data from the server
public void setCityName(String name) {
state.set("name", name);
loadData();
}
private void loadData() {
// Get the last name that was set
String name = state.get("name");
// Now kick off a load from the server
ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
Call<Example> call = apiInterface.getWeatherData(name);
call.enqueue(new Callback<Example>() {
@Override
public void onResponse(@NonNull Call<Example> call, @NonNull Response<Example> response) {
// Save the response we've gotten
// This will automatically update our UI
mutableWeatherData.setValue(response.body());
}
@Override
public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
t.printStackTrace();
}
});
}
}
现在,您可以重写您的第一个片段,以使用WeatherDataViewModel作为UI的真实来源:
public class FirstFragment extends Fragment {
private WeatherDataViewModel viewModel;
public FirstFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_first, container, false);
// For displaying weather data
final TextView current_temp = rootView.findViewById(R.id.textView10);
final TextView current_output = rootView.findViewById(R.id.textView11);
final TextView rise_time = rootView.findViewById(R.id.textView25);
final TextView set_time = rootView.findViewById(R.id.textView26);
final TextView temp_out = rootView.findViewById(R.id.textView28);
final TextView Press_out = rootView.findViewById(R.id.textView29);
final TextView Humid_out = rootView.findViewById(R.id.textView30);
final TextView Ws_out = rootView.findViewById(R.id.textView33);
final TextView Visi_out = rootView.findViewById(R.id.textView34);
final TextView Cloud_out = rootView.findViewById(R.id.textView35);
// Get our ViewModel instance
viewModel = new ViewModelProvider(this).get(WeatherDataViewModel.class);
// And whenever the data changes, refresh the UI
viewModel.getWeatherDataLiveData().observe(getViewLifecycleOwner(), data -> {
if (data != null) {
current_temp.setVisibility(View.VISIBLE);
current_temp.setText(data.getMain().getTemp() + " ℃");
current_output.setVisibility(View.VISIBLE);
current_output.setText(data.getWeather().get(0).getDescription());
rise_time.setVisibility(View.VISIBLE);
rise_time.setText(data.getSys().getSunrise() + " ");
set_time.setVisibility(View.VISIBLE);
set_time.setText(data.getSys().getSunset() + " ");
temp_out.setVisibility(View.VISIBLE);
temp_out.setText(data.getMain().getTemp() + " ℃");
Press_out.setVisibility(View.VISIBLE);
Press_out.setText(data.getMain().getPressure() + " hpa");
Humid_out.setVisibility(View.VISIBLE);
Humid_out.setText(data.getMain().getHumidity() + " %");
Ws_out.setVisibility(View.VISIBLE);
Ws_out.setText(data.getWind().getSpeed() + " Km/h");
Visi_out.setVisibility(View.VISIBLE);
Visi_out.setText(data.getVisibility() + " m");
Cloud_out.setVisibility(View.VISIBLE);
Cloud_out.setText(data.getClouds().getAll() + " %");
} else {
Log.e("TAG", "No City found");
current_temp.setVisibility(View.GONE);
current_output.setVisibility(View.GONE);
rise_time.setVisibility(View.GONE);
set_time.setVisibility(View.GONE);
temp_out.setVisibility(View.GONE);
Press_out.setVisibility(View.GONE);
Humid_out.setVisibility(View.GONE);
Ws_out.setVisibility(View.GONE);
Visi_out.setVisibility(View.GONE);
Cloud_out.setVisibility(View.GONE);
Toast.makeText(requireActivity(), "No City found", Toast.LENGTH_SHORT).show();
}
});
return rootView;
}
public void getWeatherData(String name) {
// The ViewModel controls loading the data, so we just
// tell it what the new name is - this kicks off loading
// the data, which will automatically call through to
// our observe() call when the data load completes
viewModel.setCityName(name);
}
}
通过这些更改,您将发现您的片段现在可以正确处理:
您将注意到我们如何使用新的ViewModelProvider(this)。get(WeatherDataViewModel.class)-它创建了一个绑定到这个片段本身的WeatherDataViewModel。如果ViewModel加载的数据仅用于该片段中,则这是最好的。
如果您还想在活动中使用相同的数据,您的活动可以使用新的ViewModelProvider(this)。get(WeatherDataViewModel.class)创建一个作用域为整个活动的WeatherDataViewModel。然后,任何片段都可以使用新的ViewModelProvider(requireActivity())。获取(WeatherDataViewModel.class)以获取活动拥有的视图模型。这可能意味着您根本不需要片段上的方法,相反,您的活动将直接调用viewModel。setCityName(name)本身和所有片段都会立即更新(当它们从同一个ViewModel读取时)。
我的应用程序有问题。我有一个底部导航视图,包含3个不同的片段,尽管目前只有主要的内容。当我试图从左侧的两个片段中的一个移动到主视图时,问题就出现了,即当底部导航视图被隐藏时。我附上了主代码。 PD:我有25.3.1版本的所有库(如果有用的话)。 感谢您的关注。 activity\u main。xml 主要活动。Java语言 我也给你添加了两张图片。 Ofertas片段 主片段
但是,当我初始化BottomNavigationView时,我会得到: 我正在从一个片段初始化BottomNativigationView。我猜这就是问题所在,但我想不出解决办法。 下面是为片段设置导航的活动的BottomNavigationView xml。
我正在将我的应用程序转换为使用一个活动并添加了BottomNavigationView,并努力防止在片段之间导航时重新创建片段,进行不必要的api调用。但是我无法使它工作: 不显示片段 图标未切换 触摸底部菜单项不会切换片段 触摸所选项目会使应用崩溃,并带有 TypeCastException: 活动的布局: 底部导航菜单: 在主活动中: 导航扩展: 屏幕为空白,未呈现任何片段视图。 有人能帮我解
下面是代码:home.xml Home.java 我是不是也应该贴上HomFrag代码? 这三张照片给出了关于问题的所有信息,正如你可以看到的第一张图片显示了应用程序的启动。然后我点击其他页面,点击返回,我得到的结果在第二个图像,和第三个空白屏幕时,我再次点击返回。我不明白问题的原因,如果有谁以前遇到过这个问题,请帮助。
我遇到了一个问题,当深度链接到第二级片段时,导航UI没有选择正确的底部导航视图项。 我的意思是: 选择fragment_reading_lists:选择正确的底部导航项 从fragment_reading_lists导航- 当我深入链接到fragment\u discover\u landing时会发生什么?选择默认的底部导航项目。 是否有方法通知底部导航适配器在此实例中应选择哪个项目? 这是我的
我想在切换项目时切换到底部导航视图的图标。 我有浅蓝色图标和深蓝色图标的选定项目。我正在为每个导航项目使用选择器drawable,但我看到下面这样的图像,灰色为非活动图像,蓝色为活动图像 这是我的代码 bottom_nav_menu 家 和选择器 计划程序选择器 设置选择器 收藏夹选择器 首页选择器 活动代码 我做错了什么?请帮助。。。 编辑:我正在使用以下绘图工具 在此处输入链接描述 在此处输入