This is my dialog:
#SuppressLint("ValidFragment")
public class Popup extends DialogFragment {
private final int _layout;
#SuppressLint("ValidFragment")
public Popup(int layout) {
_layout = layout;
}
#SuppressLint({"ClickableViewAccessibility", "ResourceType"})
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Nullable
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(_layout, container, false);
return view;
}
And this is how I invoke it:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Popup.ICustomTts, Popup.ITarget, Popup.IDialog, Popup.IControl {
private final Popup mPopupTurbine = new Popup(R.layout.fragment_turbine);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button turbineBtn = findViewById(R.id.turbine);
turbineBtn.setOnClickListener(view -> {
mPopupTurbine.show(getSupportFragmentManager(), "Speak");
});
When I click outside of that dialog, it will be closed. The problem is, I do some changes in that dialog (e.g. typed a text in TextView) and then I close that dialog. When I want to show it again, all the changes are lost. So how can I just hide the dialog so that the changes are still there when I re-display it?
I think in MainActivity I could add mPopupTurbine.getDialog().hide(); but where do I add this line of code?
You can use a ViewModel that lives throughout the life of your activity and remember your Popup fragment lives inside your activity.
Every time you make a change inside Popup save that data in a LiveData or Flow. Observe the LiveData inside Popup and update your UI.
You can post back entered data to your MainActivity:
#SuppressLint("ValidFragment")
public class Popup extends DialogFragment {
public Popup() {
// fragment constructor must be empty
}
private static final String LAYOUT_ID_KEY = "LAYOUT_ID_KEY";
private static final String PARAM1_KEY = "PARAM1_KEY";
public static Popup newInstance(int layoutId, String initialParam1) {
Popup popup = new Popup();
Bundle bundle = new Bundle();
bundle.putInt(LAYOUT_ID_KEY, layoutId);
bundle.putString(PARAM1_KEY, initialParam1);
popup.setArguments(bundle);
return popup;
}
private PopupCallback popupCallback;
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
popupCallback = (PopupCallback) context;
}
#Nullable
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
int layoutId = getArguments().getInt(LAYOUT_ID_KEY);
final View view = inflater.inflate(layoutId, container, false);
EditText editText = view.findViewById(R.id.edittext);
// set latest value of param1
editText.setText(getArguments().getString(PARAM1_KEY));
return view;
}
#Override
public void onDismiss(#NonNull DialogInterface dialog) {
EditText v = getView().findViewById(R.id.edittext);
// post back the param1
popupCallback.onDismissPopup(v.getText().toString());
super.onDismiss(dialog);
}
interface PopupCallback {
void onDismissPopup(String param1);
}
}
and receive and keep them in MainActivity:
public class MainActivity extends AppCompatActivity implements Popup.PopupCallback {
private String param1Backup;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button turbineBtn = findViewById(R.id.turbine);
turbineBtn.setOnClickListener(view -> {
Popup.newInstance(R.layout.fragment_turbine, param1Backup).show(getSupportFragmentManager(), "Speak");
});
}
#Override
public void onDismissPopup(String param1) {
param1Backup = param1;
}
}
Use viewModel to save your current values and attach the lifecycle of viewModel with your activity to save the current state.
Purpose of viewModel is to survive the configuration changes in your app.
You can use Singleton Class to save those data temporarily and moment you invoke it check if you have saved any data in it and put it back in your dialog.
public class SingletonClass{
public boolean isDialogDone;
public string dialogTitle, dialogMsg;
public static SingletonClass getInstance(){
if (instance == null) {
synchronized(SingletonClass.class) {
if (instance == null) {
instance = new SingletonClass();
}
}
}
return instance;
}
}
Then while invoking your dialogBox, you can simply check
if(!SingletonClass.getInstance().isDialogDone){
editText.setText(SingletonClass.getInstance().dialogMsg);
}
Ofc you also need to add listener to your EditText so that you can save the msg user is typing.
Related
I have a dialog that contains a button. When I press that button, I want the view of MainActivity to be changed.
I have tried it with an interface but I get this error:
java.lang.NullPointerException: Attempt to invoke interface method
'void ch.yourclick.kitt.classes.Popup$IControl.status(boolean)' on a
null object reference
Popup.java
public interface IControl {
void status(boolean status);
}
public IControl iControl = null;
#SuppressLint({"ClickableViewAccessibility", "ResourceType"})
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Nullable
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(_layout, container, false);
view.findViewById(R.id.turbineStartUp).setOnClickListener(v -> {
iControl.status(true);
});
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Popup.IControl {
private final Popup mPopup = new Popup(R.layout.fragment_dialog);
#Override
public void status(boolean status) {
System.out.println(status);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPopup.iControl = this;
I don't see why the object is null, though I set mPopup.iControl = this; in onCreate(). What is wrong with my code?
The reason is that when the instruction
private final Popup mPopup = new Popup(R.layout.fragment_dialog);
is executed, it calls the class Popup.java with
public IControl iControl = null;
and runs onCreateView and here
iControl.status(true);
refers to null objet.
To fix that, i suggest to customize the constructor of Popup class by adding an Icontrol parameter, something like that
private final Popup mPopup = new Popup(R.layout.fragment_dialog, this);
and edit the Popup.java file
I have activity that contain fragment.
This fragment have a lottie animation with lottie_loop="false",
that means, once the animation finish first loop , the animation will be and.
I want to listen for this event(animation end) in activity that contain this fragment, but some this wrong with my code, and I have white screen.
I created interface for listen to even , and this is my code:
Fragment with lottie animation:
public class EntryFragmentAnimation extends Fragment {
private View view;
private LottieAnimationView mLavTwoHearts;
private boolean isAnimationEnd = false;
private OnAnimationEndListener iOnAnimationEndListener;
public interface OnAnimationEndListener {
void onAnimationEnd(boolean isAnimationEnd);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_entry_animation, container, false);
initView(view);
initAnimation();
return view;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
iOnAnimationEndListener = (OnAnimationEndListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement OnAnimationEndListener");
}
}
private void initView(View view) {
mLavTwoHearts = view.findViewById(R.id.lavTwoHearts);
}
private void initAnimation() {
mLavTwoHearts.playAnimation();
mLavTwoHearts.addAnimatorListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isAnimationEnd = true;
iOnAnimationEndListener.onAnimationEnd(isAnimationEnd);
}
});
}
}
And an activity
public class LoginActivity extends AppCompatActivity implements EntryFragmentAnimation.OnAnimationEndListener {
private boolean isAnimationEnd = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setEntryScreen();
listenToAnimationEnd();
}
private void setEntryScreen(){
getSupportFragmentManager()
.beginTransaction()
.add(R.id.container_login_fl, new EntryFragmentAnimation())
.commit();
}
private void listenToAnimationEnd(){
while (!isAnimationEnd){
Log.d(TAG, "listenToAnimationEnd: Animation playing");
}
Log.d(TAG, "listenToAnimationEnd: animation end");
}
#Override
public void onAnimationEnd(boolean isAnimationEnd) {
this.isAnimationEnd = isAnimationEnd;
}
}
While running the app , only white screen appear and in logcat running endless log with Animation playing
Instead of a listener I would suggest you to better use a ViewModel. You only need to create ViewModel class and create its instance in fragment but using the activity scope so that it will be available for all the fragment contained within the activity including activity itself.
In your fragment create a Shared ViewModel instance like below:
activity?.let {
sharedViewModel = ViewModelProviders.of(it).get(SharedViewModel::class.java)
}
Once the animation ends update the the ViewModel
sharedViewModel?.onAnimationFinished()
Note: Inside you ViewModel class, have any live data member which is being obeserved by your Activity and then just update the variable within the function.
In the activity we just need to create instance of our ViewModel and observe the required data like this
val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)
sharedViewModel.animationEndEvent.observe(this, Observer {
it?.let {
// do some thing
}
})
I have an app with 4 tabs, all are Fragments and have an adapter because I'm using RecyclerView. On the first tab I have items. I want the second tab to show the items that are checked on the first and listen to the changes. The 3rd tab shows the items from the 2nd tab (=1st tab checked items) when I click on a button on the first segment. Now I set the listeners in onCreate and onCreateView. Sometimes it's working, sometimes not. My suspicion is that the create methods aren't executed in the same order every time. The other problem is that sometimes my Fragment has to notify the listener, sometimes the the Fragment's adapter. How do I treat it nicely?
First tab (it's adapter will notify)
public class EventFragment extends Fragment implements BettingEventAdapter.BettingItemClickListener {
private RecyclerView recyclerView;
static private BettingEventAdapter adapter;
private BettingListDatabase database;
private static Answer bettingData = null;
private static final String TAG = "EVENT";
private static BettingEventAdapter.BettingItemClickListener listener;
public static void setListener(BettingEventAdapter.BettingItemClickListener _listener) {
listener = _listener;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = BettingListDatabase.getInstance(this.getContext());
loadBettingData();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_events,
container, false);
recyclerView = view.findViewById(R.id.MainRecyclerView);
adapter = new BettingEventAdapter(this);
adapter.addBettingItemListener(listener);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity().getBaseContext()));
recyclerView.setAdapter(adapter);
loadItemsInBackground();
return view;
}
Second tab:
public class TicketFragment extends Fragment implements BettingEventAdapter.BettingItemClickListener {
private RecyclerView recyclerView;
TextView prizeTextView;
EditText stakeInput;
Button bSave;
private static BettingTicketAdapter.TicketSaveClickListener listener;
private BettingListDatabase database;
private BettingTicketAdapter adapter;
double odds=1;
int stake=0;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = BettingListDatabase.getInstance(this.getContext());
EventFragment.setListener(this);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_ticket,
container, false);
recyclerView = view.findViewById(R.id.TicketRecyclerView);
adapter = new BettingTicketAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity().getBaseContext()));
recyclerView.setAdapter(adapter);
}
Third tab:
public class TicketListFragment extends Fragment implements BettingTicketAdapter.TicketSaveClickListener {
private BettingTicketListAdapter parentAdapter;
private BettingListDatabase database;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
database = BettingListDatabase.getInstance(this.getContext());
TicketFragment.setListener(this);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_ticket_list,
container, false);
RecyclerView parentRecyclerView = view.findViewById(R.id.SavedTicketParentRecyclerView);
parentAdapter = new BettingTicketListAdapter();
//TODO db-ből feltölteni
loadItemsInBackground();
parentRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity().getBaseContext()));
parentRecyclerView.setAdapter(parentAdapter);
return view;
}
Pager activity:
public class PagerActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pager);
}
#Override
protected void onResume() {
super.onResume();
ViewPager mainViewPager = findViewById(R.id.mainViewPager);
TabPagerAdapter tabPagerAdapter = new TabPagerAdapter(getSupportFragmentManager(), this);
mainViewPager.setAdapter(tabPagerAdapter);
}
}
https://developer.android.com/training/basics/fragments/communicating
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.
Here is an example for your Fragment:
EventFragment
public class EventFragment extends Fragment {
OnEventSelectedListener callback;
public void setOnEventSelectedListener(OnEventSelectedListener callback) {
this.callback = callback;
}
// This interface can be implemented by the Activity, parent Fragment,
// or a separate test implementation.
public interface OnEventSelectedListener {
public void onEventSelected(int position);
}
// ...
}
PagerActivity
public class PagerActivity extends AppCompatActivity implements EventFragment.OnEventSelectedListener{
// ...
// In order to receive event callbacks from the fragment, the activity that
// hosts it must implement the interface defined in the fragment class.
//For example, the following activity implements the interface from the above example.
public void onEventSelected(int position) {
// ...
}
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof EventFragment) {
EventFragment eventFragment = (EventFragment) fragment;
eventFragment.setOnEventSelectedListener(this);
}
}
}
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
callback.onEventSelected(position);
}
for the sake of not duplicating the code I've decided in my program to create a BaseFragment (which extends Fragment) to hold for some boilerplate data and provide proper handling of pause/resume, as well as the newInstance(String myPram) method. Long story short: The role of the BaseFragment is to ensure the saving and restoring of the state, i.e. restoring myPramas a local variable...
What I wanted to do next was to extend that BaseFragment in order to handle two different types of layout for two different Fragments (each of the needs the handling of the MyParam filed): EditFragment and DetailFragment.
The problem arises now, because no View is inflated for the Fragment, though they are added as a transaction (so they are transparent!).
EditFragment and DetailFragment both provide the onCreateView() method, in order to build their own views, I am they are correct since I managed to create them without the "subclassing mechanism", and of course BaseFragment does not provide an implementation of the onCreateView() but no result, they draw no UI.
I tried putting some super.onCreateView() here and there, with no result as well...What am I doing wrong?
Here's the base Fragment:
public class TimetableBaseFragment extends Fragment {
public static String ID_FIELD = "id";
public static String ID_DESC = "desc";
private String id;
private String desc;
// Constructor to deliver previous position along with itself
public static Fragment newInstance(String id, String desc) {
TimetableBaseFragment f = new TimetableBaseFragment();
Bundle args = new Bundle();
args.putString(ID_FIELD, id);
args.putString(ID_DESC, desc);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
id = args.getString(ID_FIELD);
desc = args.getString(ID_DESC);
}
if (savedInstanceState != null) {
id = savedInstanceState.getString(ID_FIELD);
desc = savedInstanceState.getString(ID_DESC);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ID_FIELD, id);
outState.putString(ID_DESC, desc);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
id = savedInstanceState.getString(ID_FIELD);
desc = savedInstanceState.getString(ID_DESC);
}
}
public String getExamId() {
return id;
}
public String getExamDesc() {
return desc;
}
}
And the one I am trying to add is:
public class TimetableEditFragment extends TimetableBaseFragment {
private LinearLayout mLlEditEmpty;
private ArrayList<ScheduleUser> lessons;
private TimetableEditRecyclerViewAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lessons = new ArrayList<>();
mAdapter = new TimetableEditRecyclerViewAdapter(getActivity());
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_timetable_edit, container, false);
mLlEditEmpty = (LinearLayout) rootView.findViewById(R.id.LOGIC_empty);
RecyclerView mRvEdit = (RecyclerView) rootView.findViewById(R.id.HEADER_rv);
mRvEdit.setHasFixedSize(true);
mRvEdit.setLayoutManager(new LinearLayoutManager(getActivity()));
mRvEdit.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
mRvEdit.setAdapter(mAdapter);
Toolbar examDesc = (Toolbar) rootView.findViewById(R.id.HEADER_examDesc);
examDesc.setTitle("TOOLBAR Testing");
FloatingActionButton add_fab = (FloatingActionButton) rootView.findViewById(R.id.EDIT_fab);
add_fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
addLesson(new ScheduleUser());
}
});
return rootView;
}
public void addLesson(ScheduleUser newItem) {
lessons.add(newItem);
mAdapter.notifyItemInserted(lessons.size() - 1);
}
public class TimetableEditRecyclerViewAdapter extends RecyclerView.Adapter<TimetableEditRecyclerViewAdapter.ViewHolder> {
/* Lots of stuff */
}
}
PS: I've noticed that it's very likely that the child onCreateView() does not get called at all, since I had an error, which was due to be fund at runtime, but the system did not notify.
Please help!
I have an ActionBar and it has Tabs.
In the MainActivity there is a String variable.
In one Tab there is a TextView and a method setTv(String string) what can change the text in it.
I would like to make a Listener interface what can call the setTv(String string) method, when the str String is changes in the MainActivity.
Could anybody help me and fill in this code with the implementation?
This is the MainActivity code:
public class MainActivity extends FragmentActivity implements ActionBar.TabListener, LocationListener{
ActionBar actionbar;
ViewPager viewpager;
FragmentPageAdapter ft;
String str;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewpager = (ViewPager) findViewById(R.id.pager);
ft = new FragmentPageAdapter(getSupportFragmentManager());
actionbar = getActionBar();
viewpager.setAdapter(ft);
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionbar.addTab(actionbar.newTab().setText("Run").setTabListener(this));
actionbar.addTab(actionbar.newTab().setText("Map").setTabListener(this));
actionbar.addTab(actionbar.newTab().setText("Statistics").setTabListener(this));
viewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
});
}
This is the Fragment code:
public class RunFragment extends Fragment {
TextView tv;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.run_layout, container, false);
tv =(TextView)view.findViewById(R.id.textView1);
return view;
}
public void setTv(String string){
tv.setText(string);
}
}
Get your current fragment in your viewpager:
RunFragment fragment = (RunFragment) ft.getItem(viewPager.getCurrentItem());
Call your method where you want(after change str):
fragment.setTv("string");
For the best way i recommend to you make a class that extends Application
from the docs :
Base class for those who need to maintain global application state
So you can store your str variable in this class and notify any launched Activities of variable state change. Actually i recommend to use Observer pattern
MyApplication :
public class MyApplication extends Application {
private static MyApplication singleton;
private String str;
private ArrayList<StringObserver> observerList = new ArrayList<StringObserver>();
public MyApplication getInstance(){
return singleton;
}
#Override
public void onCreate() {
super.onCreate();
singleton = this;
}
public void setString(String str) {
this.str = str;
notifyObservers();
}
public void addObserver(StringObserver obs) {
observerList.add(obs);
}
public void removeObserver(StringObserver obs) {
observerList.remove(obs);
}
private void notifyObservers() {
for(StringObserver obs : observableList) {
obs.notifyAboutStringChanged(str);
}
}
}
StringObserver :
public interface StringObserver {
void notifyAboutStringChanged(String str);
}
And usage :
public class MainActivity extends FragmentActivity/*or Activity*/ implements StringObserver {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
MyApplication appState = ((MyApplication )this.getApplication());
appState.addObserver(this);
appState.setString("This string was changed");
}
#Override
public void notifyAboutStringChanged(String str) {
// do something
}
}
In other Activity/Fragment :
public class RunFragment extends Fragment {
TextView tv;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.run_layout, container, false);
tv =(TextView)view.findViewById(R.id.textView1);
return view;
}
public void setTv(String string){
tv.setText(string);
(MyApplication) getActivity().getApplication().setString(string); // this string notify your activity about value change.
}
}