I want to implement a setup assistant for my app which is shown when the user starts the app for the first time. I use a PageViewer for this which seems to work fine.
The first page asks the user for the default language. I want to refresh the page when the user selects its language from the RadioGroup. So I need a way to reload the fragment in the chosen language.
public class SetupAssistantLanguageFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.setup_language_fragment, container, false);
((RadioGroup) view.findViewById(R.id.setup_language_radiogroup)).setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
String langcode = "";
if (i == R.id.setup_fragment1_languagechooser_en) {
langcode = "en";
} else {
langcode = "de";
}
((SetupAssistantActivity) getActivity()).changeLanguage(langcode);
SetupAssistantLanguageFragment.this.refreshView();
System.out.println("test1");
}
});
return view;
}
public void refreshView() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(this).attach(this).commit();
System.out.println("test2");
}
}
Changing the language works but only for the pages that follow the first page. This is because the fragment needs to be reloaded when the language changed. I try to reload in refreshView().
The problem is: As soon as I press one radio button, I get an endless loop printing "test1" and "test2". Why is this method called again and again?
How can I reload the fragment exactly one time?
I got it:
In my fragment I first save the current language.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
currentDisplayLang = getResources().getConfiguration().locale.getLanguage();
}
In onCreateView I only refresh the fragment when the language really changed.
((RadioGroup) view.findViewById(R.id.lecture_translator_setup_language_radiogroup)).setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
String chosenLangcode = i == R.id.setup_fragment1_languagechooser_en ? "en" : "de";
if (!chosenLangcode.equals(currentDisplayLang)) {
currentDisplayLang = chosenLangcode.toString();
((SetupAssistantActivity) getActivity()).changeLanguage(chosenLangcode);
SetupAssistantLanguageFragment.this.refreshView();
}
}
});
Is this the recommended way?
Related
I have an Activity that kicks off three fragments.
The first is a loading splash screen where I just want to show the name of the app for a couple seconds.
The second, in my current case, is a screen that gathers the users name and language.
The third fragment will gather which languages they would like to learn. I'm trying to use an Observable in my ViewModel to hold my data, but the info entered at the first fragment is being lost upon switching to the second fragment.
I think I'm running into two problems (or more) simultaneously:
I don't think I'm sharing my runOnceViewModel correctly across fragments
I don't think I'm using Observables quite right.
After permissions are checked from the splash screen, I fire a function (launchProfile) that loads the next fragment like this:
public void launchProfile(){
if(model.getLearnerPK() > 0){
learningScreenFragment fragment = new learningScreenFragment(model.getUser());
getParentFragmentManager()
.beginTransaction()
.addToBackStack(null)
.replace(R.id.fragment_container, fragment, null)
.commit();
} else {
runOnceNameAndNativeLangFragment fragment = new runOnceNameAndNativeLangFragment();
getParentFragmentManager()
.beginTransaction()
.addToBackStack(null)
.replace(R.id.fragment_container, fragment, null)
.commit();
}
}
Since this is a new user, the primary key will be 0 and so I load the runOnceNameAndNativeLangFragment:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
Log.d(LOG_TAG, "RUNONCE: Gather Name and Native Language");
mBinding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.runonce_name_native, container, false);
TransitionInflater TI = TransitionInflater.from(requireContext());
setEnterTransition(TI.inflateTransition(R.transition.slide_right));
return mBinding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState){
runOnceViewModelFactory factory = new runOnceViewModelFactory(requireActivity().getApplication());
model = new ViewModelProvider(this,factory).get(runOnceViewModel.class);
//super.onViewCreated(view,savedInstanceState); //remains of a road I tried to go down, but perhaps should have gone farther...
//model = new ViewModelProvider(requireActivity()).get(runOnceViewModel.class);
ArrayList<language> spinnerListLangs = model.getLangList();
nativeSLAdapter = new SpinLangAdapter(m_Context, android.R.layout.simple_spinner_dropdown_item, spinnerListLangs);
mBinding.spCreateNativeLang.setAdapter(nativeSLAdapter);
mBinding.spCreateNativeLang.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {
language lang = nativeSLAdapter.getItem(position);
tmpNativeLangNo = lang.getLangNo();
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {}
});
mBinding.btnNext.setOnClickListener(this::onClick);
}
#Override
public void onClick(View view) {
if (view.getId() == R.id.btnNext) {
String tmpName = mBinding.etCreateProfileName.getText().toString();
if(tmpNativeLangNo != null && !tmpName.equals("")){
model.getNewUser().observeOn(Schedulers.io()) //I'm doing something dumb here
.map(user -> {
user.setName(tmpName);
user.setNativeLang(tmpNativeLangNo);
return user;
});
runOnceSelectTargetLangsFragment fragment = new runOnceSelectTargetLangsFragment();
getParentFragmentManager()
.beginTransaction()
.addToBackStack(null)
.replace(R.id.fragment_container, fragment, null)
.commit();
} else {
Toast.makeText(m_Context,"Name and Native Language required.",Toast.LENGTH_SHORT).show();
}
}
}
If I put a breakpoint on "runOnceSelectTargetLangsFragment fragment = new runOnceSelectTargetLangsFragment();" then I can verify that model.getNewUser() returns an Observable with the name and native language of whatever I input on the UI. What's killing me is that once I load the next fragment (runOnceSelectTargetLangsFragment), and try to save the target languages in its click handler, the name and native language info are lost. Ala, I'm not sharing the viewmodel right (or, worse, I'm using an Observable when I shouldn't be -- side note: I chose to use an Observable because it makes handling the thread stuff quite a bit easier and I imagine I'll appreciate its flexibility down the road as the app becomes more complicated. At least, that's what I'm telling myself.)
Select Targets Fragment
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
Log.d(LOG_TAG, "RUNONCE: Gather Target Languages");
mBinding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.runonce_select_targets, container, false);
TransitionInflater TI = TransitionInflater.from(requireContext());
setEnterTransition(TI.inflateTransition(R.transition.slide_right));
return mBinding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState){
//runOnceViewModelFactory factory = new runOnceViewModelFactory(requireActivity().getApplication());
//model = new ViewModelProvider(this,factory).get(runOnceViewModel.class); //I think this was on the right track...maybe?
//super.onViewCreated(view,savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(runOnceViewModel.class);
mBinding.btnNext.setOnClickListener(this::onClick);
}
#Override
public void onClick(View view) {
if (view.getId() == R.id.btnNext) {
if(hasChecked()){
model.getNewUser().observeOn(Schedulers.io())
.subscribe(user -> {
user.setTargetLangs(getTargetLangs());
});
String restHereWearyTraveler = ""; //the inn :)
//...do more stuff
} else {
Toast.makeText(m_Context,"You must select at least one target language.",Toast.LENGTH_SHORT).show();
}
}
}
At the inn, model.getNewUser() returns an Observable with the target languages all set, but the name and native language info are gonezo.
View Model
public class runOnceViewModel extends AndroidViewModel {
private final profileRepository m_Repository;
private profileService PCS;
private ArrayList<language> m_Langs;
private Observable<User> newUser;
public runOnceViewModel(Application application, profileRepository newProfileRepo) {
super(application);
m_Repository = newProfileRepo;
m_Langs = m_Repository.getLanguages();
PCS = profileService.getInstance(m_Repository);
}
public Observable<User> getNewUser(){
if(newUser == null){
User tmpUser = new User();
newUser = Observable.just(tmpUser);
}
return newUser;
}
}
I'm making something like a social app.
I would like that if someone has already viewed something before, that the button in the view will change colors.
I have a method to check if someone has viewed this list before. It works in the clickListener, and will say "Already pressed."
I'm having a hard time figuring out how to change the color of the button, maybe on the onCreate method. I've tried passing it as an argument, but the color will change on the second time the list is pulled up...
This is how I call up my dialog and pass it the list ID.
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View r = inflater.inflate(R.layout.list_view_dialog_layout, container, false);
checklistView = (UserlistView) r.findViewById(R.id.user_list);
checklistview.getList(getArguments().getString("list_id")); // Can be modified
return r;
}
in that function getList, I make my call to my database to get the info of the list.
public void getList(final String listID) {
// TODO fetch list information from params and fill fields
Event.requestEvent(listID, "AuthToken", new List.ListReceivedListener() {
#Override
public void onListReceived(lissts... lissteses) {
List lst = lissteses[0];
setInfo(lst);
LISTID = listID;
}
});
}
public void setInfo(List lst){
listTitleView.setText(lst.listName);
viewsCount.setText(Integer.toString(lst.views));
}
I have a checker function to see if the user has already clicked the "have viewed"
public static boolean viewed(String id, final String user){
DatabaseReference rootref = FirebaseDatabase.getInstance().getReference("views").child(id);
rootref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChild(user)){
result = true;
}
else{
result = false;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return result;
}
then, I wanted to call it in the getList() as such
if(viewed(lstID, curuser){
viewButton.setColorFilter(R.color.blue);
}
this doesn't work for the first time the view is created, and so, if the user has already clicked view, logs out and logs back in, and click view again, messing up the view count.
int flag=0;
button.setOnClickLitener(new OnClickListener()){
#Override
public void onClick(View arg0) {
flag++;
}
});
if(flag>0){
button.setBackgroundColor(getResources().getColor(R.color.yourColor));
}
Another solution is this.
button.setBackgroundColor(getResources().getColor(R.color.youCustomColor));
I'm new in this world of Android and I'm learning making an app, so I got a problem.
Working on
I created a fragment and inside I have a radio group containing 3 radio buttons
Goal
Clear all the radio buttons inside the fragment when the user returns to this screen
Problem
I don't know how to achieve that
Question
How to clear all checks of the radio buttons?
Steps done
I tried the following:
Uncheck all RadioButton in a RadioButtonGroup
But it seems I can't do it
Code
This piece of code doesn't work for me (from the post above)
protected void onResume()
{
RadioGroup rg=(RadioGroup)findViewById(R.id.RG);
rg.clearCheck();
super.onResume();
}
But I have the following:
public class Operations extends Fragment
{
RadioButton surfArea, rad, diam;
RadioGroup radG;
Button openSelect;
public Operations()
{
// Required empty public constructor
}
public static Operations newInstance()
{
return new Operations();
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_operations_sphere, container, false);
surfArea = (RadioButton) rootView.findViewById(R.id.RB_surfArea);
rad = (RadioButton) rootView.findViewById(R.id.RB_Rad);
diam = (RadioButton) rootView.findViewById(R.id.RB_Diam);
openSelect = (Button) rootView.findViewById(R.id.btn_open_select);
//This piece is for testing purposes
openSelect.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if (surfArea.isChecked())
{
Intent sa = new Intent(getContext(), OperSphere.class);
startActivity(sa);
}
}
});
return rootView;
}
//Here is where I'm stuck
#Override
public void onResume()
{
super.onResume();
}
}
I'm interested in place the code inside onResume()
I know there is docs about fragments (https://developer.android.com/guide/components/fragments.html) but those can't answer my questions
Thanks in advance
1) Initialize your RadioGroup in onCreateView method as
private RadioGroup rg;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_operations_sphere, container, false);
// initialize your radiogroup here
rg = (RadioGroup) rootView.findViewById(R.id.RG);
.....
// Rest of your code
}
2) Now call the following method i.e.,clearSelection() wherever you want to UNCHECK the RadioButtons (but after above code).
private void clearSelection(){
if(rg != null) rg.clearCheck();
}
3) Example: If you want to uncheck the RadioButtons in onResume(), you can call it from there as
#Override
public void onResume(){
super.onResume();
clearSelection();
}
If you want to do it from some other method, it would work even then as
private void myMethod(){
// need to clear radio buttons
clearSelection();
// rest of my code
}
have you tried placing your call super.onResume() before what you want to achieve...
Call clearCheck() method of radioGroup in your onResume callback, something like:
#Override
protected void onResume() {
super.onResume();
rg.clearCheck();
}
For Anybody Who is facing the same problem While navigating between fragments can do this to make it work.
radioButton.setSaveEnabled(false);
Make sure you do that for all the radio buttons in the group and not the radioGroup itself
NOTE: This flag can only disable the saving of this view; any child views may still have their state saved.
I have an application with one Activity (ActivityMain) and some fragments. A NavigationDrawer controls the switch of the fragments. In some fragments the user has the opportunity to switch to another fragment without opening the NavigationDrawer (for example with a button click).
Everything works well, if I use the NavigationDrawer to switch between fragments, but if I use a control (eg. button) within a fragment to switch to another fragment, I cannot set the selectedItem property of the NavigationDraver's (actually a ListView in the ND) selectedItem property.
The NavigationDrawer's selectedItem property is stored with sharedPreferences, and restored in the onDrawerOpened method in the NavigationDrawer fragment.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
}
I've tried to put the selection index within the onClick event of the View to STATE_SELECTED_POSITION value, as follows, but it doesn't worked. I've also cannot get the value from the sharedPreferences in the other Fragment.
public void navigationRowClick(View view) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
switch(view.getId()) {
case R.id.tr_conv:
sp.edit().putInt(STATE_SELECTED_POSITION, 1);
((MainActivity)getActivity()).changeFrame(1);
((MainActivity)getActivity()).restoreActionBar();
break;
case R.id.trCalc:
sp.edit().putInt(STATE_SELECTED_POSITION, 2);
((MainActivity)getActivity()).changeFrame(2);
((MainActivity)getActivity()).restoreActionBar();
break;
case R.id.trCalo:
Integer i = sp.getInt(STATE_SELECTED_POSITION, 100); // get value test
String s = i.toString();
Toast.makeText(getActivity(), s, Toast.LENGTH_SHORT).show();
break;
}
}
My question is, how should I set the selectedItem of the NavigationDrawer from another fragment? Do You have a best practice to this task?
Thanks is advance for the suggestions.
in the onClick event for the button that switches the fragment:
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
((MainActivity) getActivity()).changePosition(1);
sp.edit().putInt(STATE_SELECTED_POSITION, 1).**commit()**;
}
});
//in MainActivity.java
private void changePosition(int position)
{
list.setItemChecked(position, true);
}
this works if you have set the android:choiceMode="singleChoice" attribute to the list.
Another way of doing things is to do it in the adapter of the listview:
.....
{
private int mSelectedItem = 0;
public View getView(int position, View convertView, ViewGroup parent)
{
if(position == mSelectedItem)
{
}
else
{
}
}
public void setSelectedItem(int position)
{
mSelectedItem = position;
}
}
//in MainActivity.java
private void changePosition(int position)
{
adapter.setSelectedItem(position);
adapter.notifyDataSetChanged();
}
Also make sure to commit the changes to the SharedPreferences:
sp.edit().putInt(STATE_SELECTED_POSITION, 1).**commit()**;
maybe you are doing it in some other place, but I don't see it in the snippets you have shown.
I have an activity and a fragment inside it.inside fragment, there is a button, and on click of button a dialog shows.
Everything works, until user do a orientation change and click button after it.
IllegalStateException(cannot perform this action after onsaveinstancestate) occurs when user clicks button after orientation change. I'm using android support framework.
Anybody have any idea regarfing this?
Activity Code
public void openMoreDialog(String shareData, String link) {
DialogFragment dialog = new MoreDialog(shareData, link);
dialog.show(getSupportFragmentManager(), "MoreDialog");
}
Fragment Code
public void onAttach(Activity activity) {
super.onAttach(activity);
mControl = (ActivityControl)activity;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
ImageButton moreButton = (ImageButton)v.findViewById(R.id.moreButton);
moreButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mControl.openMoreDialog(shareData, link);
}
});
return rootView;
}
FragmentDialog code
public class MoreDialog extends DialogFragment {
private String mShareData;
private String mLink;
public MoreDialog(String shareData, String link){
mShareData = shareData;
mLink = link;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.more_dialog, null);
Button openBtn = (Button)dialogView.findViewById(R.id.openBtn);
openBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
openLink(mLink);
}
});
Button shareBtn = (Button)dialogView.findViewById(R.id.shareBtn);
shareBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
shareNews(mShareData);
}
});
builder.setView(dialogView);
return builder.create();
}
private void openLink(String link){
}
private void shareNews(String data){
}
}
Helpful link & solution how to:
https://stackoverflow.com/a/17413324/619673 and btw, constructor in fragment must be empty! Documentation:
http://developer.android.com/reference/android/app/Fragment.html
public Fragment ()
Added in API level 11
Default constructor.
Every fragment must have an empty constructor, so
it can be instantiated when restoring its activity's state. It is
strongly recommended that subclasses do not have other constructors
with parameters, since these constructors will not be called when the
fragment is re-instantiated; instead, arguments can be supplied by the
caller with setArguments(Bundle) and later retrieved by the Fragment
with getArguments().
Applications should generally not implement a constructor. The first
place application code an run where the fragment is ready to be used
is in onAttach(Activity), the point where the fragment is actually
associated with its activity. Some applications may also want to
implement onInflate(Activity, AttributeSet, Bundle) to retrieve
attributes from a layout resource, though should take care here
because this happens for the fragment is attached to its activity.