Android my own listener - java

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.
}
}

Related

Dialog on close: Keep the view changes

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.

Android: How to change view in Fragment that receive data from MainActivity

I'm developing application that receive data from bluetooth device to fragment in viewpager.
my application's process it
MainActivity receive data from bluetooth device
MainActivity's static String value changed as a data from bluetooth device
fragment's progressbar in viewpager in MainActivity refresh data as a MainActivity's data(changed integer)
fragment's progressbar refresh data and view whenever MainActivity's data is Changed
public class BalanceFragment extends Fragment{
public static String data = "0";
private String old = data;
private ProgressBar progressBar;
private TextView textView;
private Handler handler;
private MyThread myThread;
public BalanceFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_balance, container, false);
}
#Override
public void onStop() {
try{
myThread.stop();
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
textView = (TextView) view.findViewById(R.id.balanceProgressText);
myThread = new MyThread();
myThread.run();
}
#Override
public void onResume() {
myThread.run();
}
class MyThread extends Thread{
#Override
public void run() {
while(true) {
try {
this.sleep(5000);
MainActivity.println("test", "test");
} catch (Exception e){
e.printStackTrace();
}
if (old != data){
old = data;
progressBar.setProgress(Integer.valueOf(data));
textView.setText(data);
progressBar.invalidate();
textView.invalidate();
}
}
}
}
}
however.... when I launch this application, view is frozen......
I don't know reason why my app is frozen
You can create a interface to communicate with fragment, for example:
public class TestActivity extends Activity {
interface DataCallback {
void onData(String data);
}
DataCallback callback;
#Override
protected void onResume() {
super.onResume(savedInstanceState);
SampleFragment fragment = SampleFragment.newInstance();
callback = fragment.getCallback();
getFragmentManager().beginTransaction().replace(R.id.container, fragment, "tag").commit()
}
public static class SampleFragment extends Fragment implements DataCallback {
public static SampleFragment newInstance() {
Bundle args = new Bundle();
SampleFragment fragment = new SampleFragment();
fragment.setArguments(args);
return fragment;
}
public DataCallback getCallback() {
return this;
}
#Override
public void onData(String data) {
//Update UI with data
}
}
}
every time your activity receives a new data you should call:
callback.onData(variableValue)
and pass the new data.

communicating between fragments when only one of them is visible

I have a problem with comuunicating between fragments that are chnged dynamicaly, i mannaged to use the interface system to cimmunicate if the fragments are all viewble but when i am viewing them sepretly i can't use a their actions since they dont have a tag yet, any thoughts?
main activty
public class MainActivity extends FragmentActivity implements OnClickListener, Communicator {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (isSingleFragment()) {
Button btn_color = (Button) findViewById(R.id.button_color);
Button btn_text = (Button) findViewById(R.id.button_text);
Button btn_list = (Button) findViewById(R.id.button_list);
btn_color.setOnClickListener(this);
btn_list.setOnClickListener(this);
btn_text.setOnClickListener(this);
singleFragActions();
} else {
doubleFragActions();
}
// some more stuff happens
#Override
public void apply(String data) {
Log.d("data", data+"");
FragmentManager fm = getSupportFragmentManager();
Frag_list frag_list = (Frag_list) fm.findFragmentByTag("list");
//i cannot use id because the fragment in
//MainActivity are in a layout and are enterd dynamicaly
frag_list.addItem(data);
}
three fragments here, when on small screen, on is shown and three button on the top change them arrounf, if on tablet, all three are displyed, this is the interface
public interface Communicator {
public void apply(String data);
}
frag_text (first fragment)
public class Frag_text extends Fragment implements OnClickListener{
Communicator comm;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// stuff
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
comm = (Communicator) getActivity();
}
#Override
public void onClick(View v) {
EditText input = (EditText) getView().findViewById(R.id.frag_text_edit);
TextView output = (TextView) getView().findViewById(R.id.frag_text_show);
String text_get = input.getText().toString();
output.setText("hello "+text_get+" how are you today?");
comm.apply(text_get);
}
}
frag_list (other fragment)
public class Frag_list extends Fragment{
ArrayList<String> list;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frag_list, container, false);
list = new ArrayList<String>();
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, list);
ListView lv = (ListView) view.findViewById(R.id.list_frag);
lv.setAdapter(adapter);
return view;
}
public void addItem(String data){
list.add(data);
}
}
how to i access the other fragment if it doesnt have a tag yet?
If you happen to know what fragment you will be calling then FragmentActivity could act as a mediator in this case, as it is a mere "host". You already have this Communicator interface. Have it communicate to a given fragment:
public interface Communicator {
public void apply(String data, String tag);
}
And the override:
#Override
public void apply(String data, String tag) {
FragmentManager fm = getSupportFragmentManager();
Frag_list frag_list = (Frag_list) fm.findFragmentByTag(tag);
//Fallback plan in case the fragment has not been created.
if(frag_list == null) {
frag_list = new Frag_list();
//Include the fragment for future reference.
//This fragment has no view
fm.beginTransaction().add(frag_list, tag);
}
frag_list.addItem(data);
}
And modify the fragment to always have the list available:
public class Frag_list extends Fragment{
ArrayList<String> list = new ArrayList<String>();
//...
public void addItem(String data){
//Add check to avoid problems
if(list != null) {
list.add(data);
}
}
}

How to load data into a FragmentActivity to be used by nested fragments?

I have a FragmentActivity with two Fragments. The two Fragments depend on a shared object. The object is loaded by an AsyncTask. So the problem here is that when the activity creates and shows up the fragments, the object is not loaded yet. Is there any method that pauses the Fragment creation or something like this?
The scenario is like this:
FragmentActivity
public class MainActivity extends FragmentActivity {
private Object sharedObject;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new FooAsyncTask(...).execute();
}
...
public void onAsyncTaskCompleted(result) {
sharedObject = result;
}
// Pager adapter -------------------------------------------------------
private class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return FooFragment.newInstance(sharedObject);
case 1:
return BarFragment.newInstance(sharedObject);
default:
throw new IllegalStateException();
}
}
#Override
public String getPageTitle(int position) {
switch(position) {
case 0:
return "Foo";
case 1:
return "Bar";
default:
throw new IllegalStateException();
}
}
#Override
public int getCount() {
return 2;
}
}
}
FooFragment
public class FooFragment extends Fragment {
public FooFragment newInstance(Object sharedObject) {
FooFragment f = new FooFragment();
f.setObject(sharedObject);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
// Here I use the object to show data
}
}
BarFragment
public class BarFragment extends Fragment {
public BarFragment newInstance(Object sharedObject) {
BarFragment f = new BarFragment();
f.setObject(sharedObject);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
// Here I use the object to show data
}
}
Final thoughts
So, as you can see, I need the same data for the two fragments, and this data needs to be loaded in an AsyncTask. The ideal behavior for me would be:
Load MainActivity
Show ProgressDialog and load the data in the background
Dismiss ProgressDialog
Show Fragments and use the data
You do this wrong. You should NOT pause anything. Instead, when parent activity load what is needed it should tell the fragments about that. In fact I'd make it differently - by using listeners - so my fragment would need to register on creation and the object that loads my data would then broadcast message back once loading task it done.

Strange fragment lifecycle behavior: onCreate called twice

I've created simple fragment.
FRAGMENT CLASS
public class MyFragment extends Fragment {
static int count = 0;
static TextView tv;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("TEST", "oncreate");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frag, container, false);
tv = (TextView) view.findViewById(R.id.tv);
return view;
}
public static TextView setMyText(String text) {
tv.setText(text);
return tv;
}
}
But the thing is that onCreate fragment is called twice (when activity is first launched). Anyone has an idea why? In fact every lifecycle method is called twice ( onAttach, onResume..). I'm not rotating screen or anything, if someone might wonder. Is this common case or I'm doing something wrong here?
ACTIVITY CLASS
public class MainActivity extends FragmentActivity {
private static int COUNT = 0;
private static int COUNT2 = 5;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_pager);
ViewPager vp = (ViewPager) findViewById(R.id.vp);
vp.setAdapter(new MyAdapter(getSupportFragmentManager()));
vp.setOnPageChangeListener(new CustomPageListener());
}
private static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
MyFragment mf = new MyFragment();
return mf;
}
#Override
public int getCount() {
return 5;
}
}
private class CustomPageListener extends ViewPager.SimpleOnPageChangeListener {
#Override
public void onPageSelected(int position) {
MyFragment.setMyText("This is page "+position);
}
}
}
By default FragmentStatePagerAdapter will instantiate two pages for ViewPager. The onCreate it is called twice, once for each fragment returned by public Fragment getItem(int position)

Categories