I'm trying to send intent from fragment to MainActivity. But while doing startActivity(intent) at this point app is working good but every time I click on listview fragment is refreshing and listview starts from 0 index again. What is the best way to send the intent from the fragment?
I have two fragments and one main activity. So the user can see both the fragments at the same time. fist fragment is a list of cities and the second fragment is the description of the city.
Thank you very much for your time and assistance in this matter.
Please check my code below:
public class MainActivity extends AppCompatActivity {
private String cityName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManage();
}
public void fragmentManage() {
CityInformationFragment cityInformationFragment = new
CityInformationFragment();
CityList cityList = new CityList();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment2holder, cityList);
fragmentTransaction.add(R.id.fragment1holder, cityInformationFragment);
fragmentTransaction.commit();
Intent intent = getIntent();
cityName = intent.getStringExtra(CityList.PUT_EXTRA_KEY);
Bundle bundle = new Bundle();
bundle.putString(CityList.PUT_EXTRA_KEY, cityName);
cityInformationFragment.setArguments(bundle);
}
}
My CityList fragment:
public class CityList extends Fragment implements
AdapterView.OnItemClickListener {
String[] cityList;
ListView listView;
protected static final String PUT_EXTRA_KEY = "city";
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, Bundle savedInstanceState) {
// arrayAdapter.addAll(cityList);
cityList = getResources().getStringArray(R.array.cities);
ArrayAdapter<CharSequence> arrayAdapter = new ArrayAdapter <CharSequence>(getActivity(), android.R.layout.simple_list_item_1, cityList);
View view = inflater.inflate(R.layout.citylistframent, container, false);
listView = (ListView) view.findViewById(R.id.cityList);
listView.setAdapter(arrayAdapter);
listView.setOnItemClickListener(this);
return view;
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
TextView textView = (TextView) view;
String city = textView.getText().toString();
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.putExtra(PUT_EXTRA_KEY, city);
startActivity(intent);
}
}
My CityInformationFragment:
public class CityInformationFragment extends Fragment {
String vancouver = "Vancouver, a bustling west coast seaport in British Columbia, is among Canada’s densest, most ethnically diverse cities." +
" A popular filming location, it’s surrounded by mountains, and also has thriving art, theatre and music scenes." +
" Vancouver Art Gallery is known for its works by regional artists, while the Museum of Anthropology houses preeminent First Nations collections.";
String calgary = "Calgary, a cosmopolitan Alberta city with numerous skyscrapers, owes its rapid growth to its status as the centre of Canada’s oil industry. However," +
" it’s still steeped in the western culture that earned it the nickname “Cowtown,”" +
" evident in the Calgary Stampede, its massive July rodeo and festival that grew out of the farming exhibitions once presented here.";
String kamloops = "Kamloops is a Canadian city in British Columbia, where the North and South Thompson rivers meet." +
" Sun Peaks Resort’s hiking trails, bike park and numerous ski runs lie to the northeast. Cougars and bears inhabit the British Columbia Wildlife Park east of town." +
" West, above Kamloops Lake are clay hoodoos (or spires). The riverside Secwepemc Museum & Heritage Park features the remains of a 2,000-year-old village.";
String toronto = "Toronto, the capital of the province of Ontario, is a major Canadian city along Lake Ontario’s northwestern shore." +
" It's a dynamic metropolis with a core of soaring skyscrapers, all dwarfed by the iconic, free-standing CN Tower. " +
"Toronto also has many green spaces, from the orderly oval of Queen’s Park to 400-acre High Park and its trails, sports facilities and zoo.";
String saskatoon = "Saskatoon is a city straddling the South Saskatchewan River in Saskatchewan, Canada. " +
"North along the riverside Meewasin Trail is Wanuskewin Heritage Park, with exhibitions exploring indigenous culture. " +
"On the trail’s southern stretch, native wildlife inhabit the prairie grasslands of Beaver Creek Conservation Area. " +
"East of the river, the Saskatoon Forestry Farm Park & Zoo has manicured gardens and a children’s zoo.";
TextView textView;
String cityName = "";
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.cityinformation, container, false);
textView = view.findViewById(R.id.cityInformation);
cityName = getArguments().getString(CityList.PUT_EXTRA_KEY);
// Toast.makeText(getContext(), cityName, Toast.LENGTH_SHORT).show();
setInformation();
return view;
}
public String getVancouver() {
return vancouver;
}
public String getCalgary() {
return calgary;
}
public String getKamloops() {
return kamloops;
}
public String getToronto() {
return toronto;
}
public String getSaskatoon() {
return saskatoon;
}
public void setInformation() {
if (cityName != null) {
if (cityName.equals("")) {
textView.setText(getKamloops());
} else if (cityName.equals("Calgary")) {
textView.setText(getCalgary());
} else if (cityName.equals("Kamloops")) {
textView.setText(getKamloops());
} else if(cityName.equals("Calgary")) {
textView.setText(getCalgary());
} else if(cityName.equals("Saskatoon")){
textView.setText(getSaskatoon());
} else if(cityName.equals("Toronto")) {
textView.setText(getToronto());
} else if(cityName.equals("Vancouver")){
textView.setText(getVancouver());
}
} else {
textView.setText(getKamloops());
}
}
}
Use eventbus it will solve your problem and you dont need to call activity, fragments are working inside activity read documentation you understand me
Intents are only usable for sending data on an Activity level. To pass data between fragments we need to create our own interfaces.
As describe here Communicating with Other Fragments
You need to create a listener/callback in your Activity and Fragment. Whenever there is a change that you want to pass to Activity, send it via the listener. Read more at Communicating with Other Fragments.
First, define the listener with interface in the Fragment:
public class CityList extends Fragment implements AdapterView.OnItemClickListener {
private CityListListener mListener;
// Host Activity must implement this listener.
public interface CityListListener {
public void onItemClicked(String city);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
// Makes sure that the host activity has implemented the listener
try {
mListener = (CityListListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement CityListListener");
}
}
...
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
TextView textView = (TextView) view;
String city = textView.getText().toString();
// tell the host Activity about the item click.
mListener.onItemClicked(city);
}
}
Now, you need to implement the listener in your Activity:
public class MainActivity extends AppCompatActivity
implements CityList.CityListListener {
...
#Override
public void onItemClicked(String city) {
// This will listen to every item click in CityList Fragment
// Do something about the city.
}
}
Related
I have a bottom navigation view on my weather app containing 3 panels(Today, hourly & Daily). My activity hosts 3 fragments for the 3 panels. Currently, if I search for any city on the today fragment, it gives the data for such cities. Now the problem is that if I click on any bottom nav view(down), it resets the data displayed on the today fragment.
Here is an illustration of the issue:
Data is displayed after a city is searched(the part with a red tick):
Data goes on reset after clicking these bottom nav views(the part with a red tick):
I want the data to remain intact irrespective of clicking those nav views.
I tried using https://stackoverflow.com/a/60201555/16020235 suggestion. But it failed with this
exception:
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)
I find it hard to implement his code and the remaining suggestions on that question are written in kotlin.
Please how can I resolve this?
Here are my codes:
my_nav.xml(navigation layout):
<?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_home.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>
HomeActivity:
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());
}
});
}
});
}
}
EDIT
Fragment class:
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();
}
});
}
}
I didn't paste everything because I'm following https://stackoverflow.com/help/minimal-reproducible-example
Your problem is that your FirstFragment is not saving its state properly. As per the Saving state with fragments guide:
To ensure the user's state is saved, the Android framework automatically saves and restores the fragments and the back stack. Therefore, you need to ensure that any data in your fragment is saved and restored as well.
But you aren't saving the last name that you pass to getWeatherData, nor are you saving your Example object that you get from your API call in order to repopulate your views when they are recreated.
So need to actually use the APIs described in that guide to save your state. Namely, you should file the Guide to app architecture, which explains how you can separate your from your data loading by using ViewModels to store data across configuration changes (like rotating your device) and LiveData to automatically populate your UI whenever your data is loaded.
The first thing we want to do is move the data loading to a ViewModel. This object survives configuration changes which means any data stored in this class is automatically saved when you rotate your device. This is how we can save your Example class and avoid calling the server over and over.
By using the APIs in the Saved State module for ViewModel (specifically, the SavedStateHandle class), any data you save in there will survive your process being killed and later recreated (say, if your device is low on memory, etc.). This is how we can save the last name so that we will automatically requery for your data.
Here, our ViewModel handles all of the loading from the server and uses a LiveData to let our UI automatically update as the data is loaded.
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();
}
});
}
}
Now you can rewrite your FirstFragment to use the WeatherDataViewModel as the source of truth for your 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);
}
}
With these changes, you'll find that your Fragment now correctly handles:
Being put on the Fragment back stack
Configuration changes (i.e., rotating your device)
Process death and recreation (i.e., testing with 'Don't keep activities' on)
You'll note how we use new ViewModelProvider(this).get(WeatherDataViewModel.class) - that creates a WeatherDataViewModel that is tied to this - your Fragment itself. This is best if the data your ViewModel loads is only used in that one Fragment.
If you also wanted to use this same data in your Activity, your activity could use new ViewModelProvider(this).get(WeatherDataViewModel.class) to create a WeatherDataViewModel that is scoped to the entire Activity. Any Fragment could then use new ViewModelProvider(requireActivity()).get(WeatherDataViewModel.class) to get that Activity owned ViewModel. This would potentially mean that you wouldn't need a getWeatherData() method on your Fragment at all - instead, your Activity would directly call viewModel.setCityName(name) itself and all Fragments would just instantly update (as they read from the same ViewModel).
I think, this is because your FirstFragment gets reinstantiated every time. To save state, modify onCreate() of your FirstFragment like following:
#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);
}
}
And add onSaveInstanceState() to your FirstFragment:
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ARG_PARAM1, mParam1);
outState.putString(ARG_PARAM2, mParam2);
}
The mistake seems to be that every time you select an item in the bottom navigation bar the fragment gets re-instantized thus creating a new fragment(of same type, but a new object of that fragment).
I would suggest you create an object of all the fragment at the start like this
private final Fragment[] fragments = {
new HomeFragment(),
new MyNotesFragment(),
new PreferencesFragment()
};
and then for the Bottom Nav Listener part, we will change the fragments according to the button clicked by passing already made instances of fragments rather than creating new ones.
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;
}
});
(Though this is for the ViewPager library the concept works the same)
If you still have not solve it yet, you might want to try this solution that uses SharedPreferences.
public class FirstFragment extends Fragment
{
// ...
private SharedPreferences prefs;
private SharedPreferences.Editor prefsEditor;
// ...
#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);
// ...
prefs = new SharedPreferences(getActivity().getApplicationContext());
prefsEditor = prefs.edit();
current_temp.setText(prefs.getString("current_temp", "not yet initialised"));
current_output.setText(prefs.getString("current_output", "not yet initialised"));
// ...
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;
final String currentTemp = response.body().getMain().getTemp() + " ℃";
editor.putString("current_temp", currentTemp);
editor.commit();
current_temp.setText(currentTemp);
current_temp.setVisibility(View.VISIBLE);
final String currentOutput = response.body().getWeather().get(0).getDescription();
editor.putString("current_output", currentOutput);
editor.commit();
current_output.setText(currentOutput);
current_output.setVisibility(View.VISIBLE);
// ...
} catch (Exception e) {
Log.e("TAG", "No City found");
current_temp.setVisibility(View.GONE);
current_output.setVisibility(View.GONE);
// ...
Toast.makeText(getActivity(), "No City found", Toast.LENGTH_SHORT).show();
}
}
// ...
});
}
}
I have a Fragment inside the mainActivity, the fragment contains fragmentcontainerView which can be replaced by multiple child fragments with spinner onselectedListener. I want to able to pass those values from the child fragment via eg: Do something with: fragmentevent.TogetFName(); with a button in Mainactivity. In the parent fragment , I get the value from the child fragment(fragment_Birthday) with fragment_fr_event_birthday = (fragment_fr_event_Birthday) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment); and other value from other childfragment with frag_fr_event_wed = (fragment_fr_event_wedding) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment);, I know that they cannot be assigned with the different fragment class at once, but is there a clever way to do this or is there any other way I can pass value from child -> parent fragment->mainActivity
MainActivity:
public void onClick(View view){
case "Event":
Fragment_fr_Event fragment_fr_event = (Fragment_fr_Event) getSupportFragmentManager().findFragmentById(R.id.fragment_generated_mainView);
if(fragment_fr_event.TogetWedChildFcoupleName() !=null && fragment_fr_event.TogetEventType().equals("Wedding")){
testThis.setText(fragment_fr_event.TogetWedChildFcoupleName());
}if( fragment_fr_event.TogetEventType().equals("Birthday") && fragment_fr_event.TogetBirthdayFName() !=null){
testTat.setText(fragment_fr_event.TogetBirthdayFName());
}
}
ChildFragment(BirthdayFragment):
public String TogetEventBirthdayFName (){
EditText FBirthdayName = rootView.findViewById(R.id.Edittext_birthDay_FirstName);
return FBirthdayName.getText().toString();
}
ChildFragment(Wedding fragment):
public String toGetFcoupleName(){
EditText FCoupleName = rootView.findViewById(R.id.textView_wedding_Name);
return FCoupleName.getText().toString();
}
ParentFragment(EventFragment):
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Spinner TypeEventSpinner = rootview.findViewById(R.id.type_event);
TypeEventSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String tag_items = parent.getItemAtPosition(position).toString();
switch (tag_items){
case "Wedding":
frag_fr_event_wed = new fragment_fr_event_wedding();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_event_child_fragment, frag_fr_event_wed).disallowAddToBackStack().commit();
break;
case "Birthday":
fragment_fr_event_birthday = new fragment_fr_event_Birthday();
transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_event_child_fragment , fragment_fr_event_birthday).disallowAddToBackStack().commit();
break;
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
public String TogetWedChildFcoupleName(){
if(frag_fr_event_wed !=null){
frag_fr_event_wed = (fragment_fr_event_wedding) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment);
return frag_fr_event_wed.toGetFcoupleName();
}return "Empty";
}
public String TogetBirthdayFName(){
if(fragment_fr_event_birthday != null){
fragment_fr_event_birthday = (fragment_fr_event_Birthday) getChildFragmentManager().findFragmentById(R.id.fragment_event_child_fragment);
return fragment_fr_event_birthday.TogetEventBirthdayFName();
}
return "Empty";
}
To be honest , I couldn't understand what you did there , but i got what you want , you want to communicate with parent's parent class , the way you are doing it made it so complicated even it's not readable , BUT of course there are always a good way to do something , in your case there are Android Navigation Component , which give you the simplicity and power to do make it much more easy to handle , You can put all your fragment in one graph and from within the destinations "fragment are called destinations here" you can communicate with other fragment and the parent using actions and global actions "going from one fragment to another is called action here" parameters, but there are no need to a parent's parent here , all destinations and its parent can share one ViewModel which will allow you to share data all around your app .
You can read more if it sound good to you here
I have encountered a weird bug in my fragment class: imageViews "disappear" if I rotate/reload the view. From my understanding, rotating/reloading destroys the View and re-creates it, so local variables and local view elements may not preserve. However, I have made a manual button that should manually render the images again after I click it, yet the ImageViews stay gone even if I manually reset their imageResource or imageBackground resource. Note that these imageviews are animated-drawables. Below is most of my code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
mView = rootView; //mView is global
return rootView;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d("gnereed id", "id is "+R.id.generate_button);
final Button generate_button = getView().findViewById(R.id.generate_button);
if ( bottleMap == null ) bottleMap = new Hashtable();
// the code sets all images to invisible onCreate
// their visibility will be changed when a bottle is "created"
ImageView[] bottles = new ImageView[7];
for (int i = 0; i < bottleAry.length; i++){
bottles[i] = getView().findViewById(bottleAry[i]);
bottles[i].setVisibility(View.GONE);
}
// this is a button that generates a new bottle and manually refreshes all previous bottle
// If fragment has not been reloaded/rotated then everything works here
// after fragment reloads, new bottles can be generated but old bottles do not re-render.
generate_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Bottle bottle = new Bottle("123", bottleList.size());
bottle.setVisible();
bottleList.add(bottle);
Log.e(" mView : ", mView.toString());
// for all bottles created, re-render them
for (int i = 0; i < bottleList.size(); i ++) {
bottleList.get(i).reRender();
}
}
});
}
public class Bottle{
String message;
ImageView bottleLocation;
int imageSrc;
int avail_index;
int bottle_index;
int locationID;
AnimationDrawable bottleAnimation;
public Bottle(String msg, int bottle_index){
message = msg;
this.bottle_index = bottle_index;
locationID = getRandomBottleLocation();
bottleLocation = getView().findViewById(locationID);
Log.e(" old View : ", getView().toString());
// sets the image source and sets visible, lastly start animation
imageSrc = getRandomBottleImg();
bottleLocation.setBackgroundResource(imageSrc);
bottleMap.put(Integer.toString(locationID), imageSrc);
bottleAnimation = (AnimationDrawable) bottleLocation.getBackground();
bottleAnimation.start();
bottleLocation.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), ViewBottleActivity.class));
availableLocation[avail_index] = false;
bottleMap.remove(Integer.toString(locationID));
bottleAnimation.stop();
bottleLocation.setVisibility(View.GONE);
bottleList.remove(bottle_index);
}
});
}
public int getRandomBottleImg(){
int bottle;
Random rand = new Random();
bottle = imgAry[rand.nextInt(imgAry.length)];
return bottle;
}
public int getRandomBottleLocation(){
int location;
Random rand = new Random();
avail_index = rand.nextInt(bottleAry.length);
while (availableLocation[avail_index]){
avail_index = rand.nextInt(bottleAry.length);
}
location = bottleAry[avail_index];
availableLocation[avail_index] = true;
return location;
}
public void reRender(){
Log.e("location ID is:" , Integer.toString(this.locationID));
bottleLocation = mView.findViewById(this.locationID);
Log.e("ImageView is:" , bottleLocation.toString());
imageSrc = getRandomBottleImg();
bottleLocation.setBackgroundResource(imageSrc);
bottleAnimation = (AnimationDrawable) bottleLocation.getBackground();
bottleAnimation.stop();
bottleAnimation.start();
this.setVisible();
}
public void setVisible(){
bottleLocation.setVisibility(View.VISIBLE);
}
I fixed the problem. What I learned is that you cannot use
View.findViewByID(viewID)
outside of onViewCreated(). Notice how I implemented reRender() function inside the onViewCreated() but it didn't work? The result is that mView.findViewByID(viewID) is actually executed OUTSIDE OF onViewCreated() although I call the function from WITHIN.
Yes, the line will be executed, but upon inspection, mView.findViewByID(viewID) will return TWO DIFFERENT objects when called from inside onViewCreated() and when called from a function that is called from onViewCreated().
This is very counterintuitive, especially for us who's taught to deploy the DRY(do not repeat urself) principle. The fix is just to simply not write outside functions for handling View.
I'm working with an application, whereas the purpose of it is to keep track of a list of words a person might be interested in learning. The app is split up into 3 activities, in which the first activity (ListActivity) shows a list of words with a few infos (image, word, pronounciation and rating) about each word. Image, word and pronounciation datas are already known and are hardcoded (I know that it would be much better if I just read data from a csv-file but let it be for now), and rating can be set from the EditActivity.
In that context I've implemented code that handles clicks on a specific word that will navigate me to 2nd activity (DetailsActivity) with all relevant infos from the particular word, aswell as retrieving rating data and display at the correct index/item. The code works and everything seems to be fine, but there's a bug, which I really can't solve.
The bug occurs when I do the following:
I click on a particular word in the recyclerview
I edit the word and give it a rating of 8 and press OK. Data will now be sent to ListActivity and will be shown at the correct index/word.
I press on the same word and switch to landscape mode and choose to edit the word. Let's say that I now give it a rating of 5 and press OK.
The new rating data will then be set/shown at index 0 and not the particular word.
What might be the issue here? It seems like it loses position somewhere in the application and between intents, but I can't figure out why.
ListActivity:
public class ListActivity extends AppCompatActivity implements WordAdapter.OnItemListener {
ArrayList<WordItemParcelable> mWords = new ArrayList<WordItemParcelable>();
private WordAdapter mAdapter;
private static final int REQUEST_CODE_DETAILS_ACTIVITY = 1;
private Button exitBtn;
private String rating, note;
private int wordClickedIndex = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
if (savedInstanceState != null) {
mWords = savedInstanceState.getParcelableArrayList(getString(R.string.key_orientationchange));
} else {
mWords = new ArrayList<>();
insertWords();
}
setUpExitBtn();
setUpRecyclerView();
}
public void insertWords() {
mWords.add(new WordItemParcelable(R.drawable.lion, "Lion", "ˈlīən", "A large tawny-coloured cat that lives in prides, found in Africa and NW India. The male has a flowing shaggy mane and takes little part in hunting, which is done cooperatively by the females.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.leopard, "Leopard", "ˈlepərd", "A large solitary cat that has a fawn or brown coat with black spots, native to the forests of Africa and southern Asia.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.cheetah, "Cheetah", "ˈCHēdə", "A large slender spotted cat found in Africa and parts of Asia. It is the fastest animal on land.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.elephant, "Elephant", "ˈeləfənt", "A very large plant-eating mammal with a prehensile trunk, long curved ivory tusks, and large ears, native to Africa and southern Asia. It is the largest living land animal.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.giraffe, "Giraffe", "jəˈraf", "A large African mammal with a very long neck and forelegs, having a coat patterned with brown patches separated by lighter lines. It is the tallest living animal.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.kudo, "Kudu", "ˈko͞odo͞o", "An African antelope that has a greyish or brownish coat with white vertical stripes, and a short bushy tail. The male has long spirally curved horns.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.gnu, "Gnu", "n(y)o͞o", "A large dark antelope with a long head, a beard and mane, and a sloping back.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.oryx, "Oryx", "null", "A large antelope living in arid regions of Africa and Arabia, having dark markings on the face and long horns.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.camel, "Camel", "ˈkaməl", "A large, long-necked ungulate mammal of arid country, with long slender legs, broad cushioned feet, and either one or two humps on the back. Camels can survive for long periods without food or drink, chiefly by using up the fat reserves in their humps.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.shark, "Shark", "SHärk", "A long-bodied chiefly marine fish with a cartilaginous skeleton, a prominent dorsal fin, and tooth-like scales. Most sharks are predatory, though the largest kinds feed on plankton, and some can grow to a large size.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.crocodile, "Crocodile", "ˈkräkəˌdīl", "A large predatory semiaquatic reptile with long jaws, long tail, short legs, and a horny textured skin.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.snake, "Snake", "snāk", "A long limbless reptile which has no eyelids, a short tail, and jaws that are capable of considerable extension. Some snakes have a venomous bite.", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.buffalo, "Buffalo", "ˈbəf(ə)ˌlō", "A heavily built wild ox with backward-curving horns, found mainly in the Old World tropics:", "" + 0.0));
mWords.add(new WordItemParcelable(R.drawable.ostrich, "Ostrich", "ˈästriCH", "A flightless swift-running African bird with a long neck, long legs, and two toes on each foot. It is the largest living bird, with males reaching a height of up to 2.75 m.", "" + 0.0));
}
public void setUpRecyclerView() {
RecyclerView mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
mAdapter = new WordAdapter(mWords, this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onItemClick(int position) {
Intent intent = new Intent(this, DetailsActivity.class);
WordItemParcelable clickedWord = mWords.get(position);
wordClickedIndex = position;
intent.putExtra(getString(R.string.key_picture), clickedWord.getImageResource());
intent.putExtra(getString(R.string.key_name), clickedWord.getWord());
intent.putExtra(getString(R.string.key_pronouncing), clickedWord.getPronouncing());
intent.putExtra(getString(R.string.key_description), clickedWord.getDescription());
intent.putExtra(getString(R.string.key_rating), clickedWord.getRating());
intent.putExtra(getString(R.string.key_notes), clickedWord.getNotes());
startActivityForResult(intent, REQUEST_CODE_DETAILS_ACTIVITY);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_DETAILS_ACTIVITY) {
if (resultCode == RESULT_OK) {
if (data != null) {
rating = data.getStringExtra(getString(R.string.key_rating));
note = data.getStringExtra(getString(R.string.key_notes));
WordItemParcelable i = mWords.get(wordClickedIndex);
i.setRating(rating);
mWords.set(wordClickedIndex, i);
mAdapter.updateData(mWords);
mAdapter.notifyDataSetChanged();
}
}
}
}
public void setUpExitBtn() {
exitBtn = (Button) findViewById(R.id.exitBtn);
exitBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setResult(RESULT_CANCELED);
finish();
}
});
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
outState.putParcelableArrayList(getString(R.string.key_orientationchange), mWords);
super.onSaveInstanceState(outState);
}
}
Adapter:
public class WordAdapter extends RecyclerView.Adapter<WordAdapter.WordViewHolder> {
private ArrayList<WordItemParcelable> mWordList;
private OnItemListener mOnItemListener;
private int position;
public static class WordViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView mPicture;
TextView txtName, txtPronouncing, txtRating;
OnItemListener onItemListener;
private int position;
public WordViewHolder(#NonNull View itemView, OnItemListener onItemListener) {
super(itemView);
mPicture = itemView.findViewById(R.id.picture);
txtName = itemView.findViewById(R.id.txtNameOfTheWord);
txtPronouncing = itemView.findViewById(R.id.txtPronoucing);
txtRating = itemView.findViewById(R.id.txtListRating);
this.onItemListener = onItemListener;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onItemListener.onItemClick(getAdapterPosition());
}
}
public WordAdapter(ArrayList<WordItemParcelable> wordList, OnItemListener onItemListener) {
this.mWordList = wordList;
this.mOnItemListener = onItemListener;
}
#NonNull
#Override
public WordViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.word_item, parent, false);
WordViewHolder wvh = new WordViewHolder(v, mOnItemListener);
return wvh;
}
#Override
public void onBindViewHolder(#NonNull WordViewHolder holder, int position) {
WordItemParcelable word = mWordList.get(position);
int image = word.getImageResource();
String name = word.getWord();
String pronounce = word.getPronouncing();
String rating = word.getRating();
holder.mPicture.setImageResource(image);
holder.txtName.setText(name);
holder.txtPronouncing.setText(pronounce);
holder.txtRating.setText(rating);
}
#Override
public int getItemCount() {
return mWordList.size();
}
public interface OnItemListener {
void onItemClick(int position);
}
public void updateData(ArrayList<WordItemParcelable> newList) {
mWordList = newList;
}
}
When changing the orientation of the screen, activity is recreated to save information, use onsaveinstancestate: https://developer.android.com/guide/components/activities/activity-lifecycle
if (savedInstanceState != null) {
position = savedInstanceState.getInteger(KEY);
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(KEY, position);
super.onSaveInstanceState(outState);
}
(This is pseudo code, there may be errors)
What I want to do is to show the same selected items on a recycler view even after the activity has been closed and only change items color when I again click on it. For now I have achieved changing the color on click but the state doesn't get saved?
This is my adapter:
public class LightsRecyclerViewAdapter extends
RecyclerView.Adapter<LightsRecyclerViewAdapter.ViewHolder> {
// private List<Integer> mViewColors;
private List<String> mAnimals;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
LightsRecyclerViewAdapter(Context context, List<String>
animals) {
this.mInflater = LayoutInflater.from(context);
this.mAnimals = animals;
}
// inflates the row layout from xml when needed
#Override
#NonNull
public ViewHolder onCreateViewHolder(#NonNull ViewGroup
parent, int viewType) {
View view = mInflater.inflate(R.layout.item, parent,
false);
return new ViewHolder(view);
}
// binds the data to the view and textview in each row
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int
position) {
// int color = mViewColors.get(position);
String animal = mAnimals.get(position);
// holder.myView.setBackgroundColor(color);
holder.myTextView.setText(animal);
}
// total number of rows
#Override
public int getItemCount() {
return mAnimals.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
View myView;
TextView myTextView;
ViewHolder(View itemView) {
super(itemView);
// myView = itemView.findViewById(R.id.colorView);
myTextView =
itemView.findViewById(R.id.tvAnimalName);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null)
mClickListener.onItemClick(view, getAdapterPosition());
}
}
// convenience method for getting data at click position
public String getItem(int id) {
return mAnimals.get(id);
}
// allows clicks events to be caught
public void setClickListener(ItemClickListener
itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
}
And this is my activity:
public class DevicesList extends AppCompatActivity implements
LightsRecyclerViewAdapter.ItemClickListener{
private LightsRecyclerViewAdapter adapter,adapter1;
TextView title;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_devices_list);
title = (TextView)findViewById(R.id.textGrid);
// data to populate the RecyclerView with
ArrayList<Integer> viewColors = new ArrayList<>();
viewColors.add(Color.BLUE);
viewColors.add(Color.YELLOW);
viewColors.add(Color.MAGENTA);
viewColors.add(Color.RED);
viewColors.add(Color.BLACK);
ArrayList<String> Lab1LightsList = new ArrayList<>();
Lab1LightsList.add("Light 1");
Lab1LightsList.add("Light 2");
Lab1LightsList.add("Light 3");
Lab1LightsList.add("Light 4");
Lab1LightsList.add("Light 5");
ArrayList<String> Lab1ACList = new ArrayList<>();
Lab1ACList.add("AC 1");
Lab1ACList.add("AC 2");
Lab1ACList.add("AC 3");
Lab1ACList.add("AC 4");
Lab1ACList.add("AC 5");
ArrayList<String> Lab2LightsList = new ArrayList<>();
Lab2LightsList.add("Light 1");
Lab2LightsList.add("Light 2");
Lab2LightsList.add("Light 3");
Lab2LightsList.add("Light 4");
Lab2LightsList.add("Light 5");
Lab2LightsList.add("Light 6");
ArrayList<String> Lab2ACList = new ArrayList<>();
Lab2ACList.add("AC 1");
Lab2ACList.add("AC 2");
Lab2ACList.add("AC 3");
Lab2ACList.add("AC 4");
// set up the RecyclerView
RecyclerView recyclerView = findViewById(R.id.list1);
RecyclerView recyclerView1 =findViewById(R.id.list2);
LinearLayoutManager horizontalLayoutManagaer
= new LinearLayoutManager(DevicesList.this, LinearLayoutManager.HORIZONTAL, false);
LinearLayoutManager horizontalLayoutManager
= new LinearLayoutManager(DevicesList.this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(horizontalLayoutManagaer);
recyclerView1.setLayoutManager(horizontalLayoutManager);
Intent mIntent = getIntent();
int intValue = mIntent.getIntExtra("labno", 0);
if(intValue==0) {
adapter = new LightsRecyclerViewAdapter(this, Lab1LightsList);
adapter1 = new LightsRecyclerViewAdapter(this, Lab1ACList);
adapter.setClickListener(this);
adapter1.setClickListener(this);
recyclerView.setAdapter(adapter);
recyclerView1.setAdapter(adapter1);
}
if(intValue==1) {
adapter = new LightsRecyclerViewAdapter(this, Lab2LightsList);
adapter1 = new LightsRecyclerViewAdapter(this, Lab2ACList);
adapter.setClickListener(this);
adapter1.setClickListener(this);
recyclerView.setAdapter(adapter);
recyclerView1.setAdapter(adapter1);
}
}
#Override
public void onItemClick(View view, int position) {
Toast.makeText(this, "You clicked " +
adapter.getItem(position) + " on item position " + position,
Toast.LENGTH_SHORT).show();
view.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
}
}
Please help on this.
Create one selected item position list and store it in prefs when an app goes to background or closed. Load that list when launching an app and compare that list in an adapter's onBindViewHolder's position parameter and marks it selected/unselected based on a comparison.
As per my understandings about your question, you want to save the state of the selected items even after the app is closed, and then you want to reload it whenever the app is launched again. You need to refer to this link Android Save Data
For the above solution, there can be various ways to save state, I am mentioning a few below:
Use SQLite Database to save the selected items. Then, whenever the app is loaded, fetch all the selected data from the DB and then mark them selected with whatever colour you want on the list.
You can also use Shared Preferences, to store the selection. And, same as above, you can reload the data when the app is launched.
You can also store the data in a specific format, maybe CSV, JSON, XML etc., in a file and save it either in Internal Storage or External Storage of the device. And when the app is launched, fetch all the selected values from the file and process accordingly.
You can also use a web server, Firebase Storage, or other cloud storage services to save the data and then fetch the data on new app launch.
Do note: All these techniques require you to save the state before the app is closed. So it is better to store the states, either on click of the item, or onPause method of the activity.
If you face any problems with these solutions, you can post another comment and I will give it a look.
Save these clicked item position in a hashmap in Shareprefence. suppose u close the activity after u coming back the activity just pass the saved list with ur data in adapter and compare the shareprefence list with ur data list if position or data match than make the itemview layout colored.
// save clicked item is a list and save it sharePreference.
List<Integer> clikedList = new ArrayList<>();
if (clicked item){
ClikedList.add(position)
}
String value = gson.toJson(list);
SharedPreferences prefs = context.getSharedPreferences("mylist",
Context.MODE_PRIVATE);
Editor e = prefs.edit();
e.putString("list", value);
e.commit();
// for getting cliked position list from SharePreference
SharedPreferences prefs = context.getSharedPreferences("mylist",
Context.MODE_PRIVATE);
String value = prefs.getString("list", null);
GsonBuilder gsonb = new GsonBuilder();
Gson gson = gsonb.create();
MyObject[] list = gson.fromJson(value, MyObject[].class);
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// suppose clicked position 4 u get from shaved cliked list
in here u neddd to retreive cliked list position and clored those item
int select = 4;
if (select == position) {
holder.itemView.setBackgroundColor(Color.BLUE);
Toast.makeText(context, "" + position, Toast.LENGTH_SHORT).show();
} else {
holder.itemView.setBackgroundColor(Color.parseColor("#214F4B"));
Toast.makeText(context, "" + position, Toast.LENGTH_SHORT).show();
}
holder.tv_title.setText(data.get(position));
}