I'm trying to pass some views to ClassA from abstract ClassB, so that ClassA can use: ClassA extends ClassB
Instead of using something like this for each view I'd like to be inherited/passed on:
protected View getView1() {
return view1;
}
I want a bulk way to let ClassX (in this case, ClassA) know which views it should be using. But keep some views private to ClassB to use for itself.
The code below does work, but is it okay to do it this way? or is there a better way?
public abstract class ClassB extends Activity {
abstract void useTheseViews(View view1, View view2);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View view1 = findViewById(R.id.view1);
View view2 = findViewById(R.id.view2);
useTheseViews(view1, view2);
View view3 = findViewById(R.id.view3);
view3.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// do something independent here
}
});
}
}
public class ClassA extends ClassB {
private View view1, view2;
#Override
void useTheseViews(View view1, View view2) {
this.view1 = view1;
this.view2 = view2;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view1.setOnClickListener(..);
view2.setOnClickListener(..);
}
}
Reason? ClassA and ClassX use the same base layout, but do different functions on the same views.
My ClassB holds a base layout and initializes the views (buttons, switches..), and uses some of the views for itself regardless of what class extends it.
I would do it this way
public abstract class ClassB extends Activity {
private View view1, view2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view1 = findViewById(R.id.view1);
view2 = findViewById(R.id.view2);
}
protected View getView1(){
return view1;
}
protected View getView2(){
return view2;
}
}
public class ClassA extends ClassB {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getView1().setOnClickListener(..);
getView2().setOnClickListener(..);
}
}
and if you don't want to create protected getter for each view, use a holder class
public abstract class ClassB extends Activity {
protected class ViewHolder {
public final View view1, view2;
public ViewHolder(){
view1 = findViewById(R.id.view1);
view2 = findViewById(R.id.view2);
}
}
private ViewHolder viewHolder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewHolder = new ViewHolder();
}
protected ViewHolder getViewHolder(){
return viewHolder;
}
}
public class ClassA extends ClassB {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getViewHolder().view1.setOnClickListener(..);
getViewHolder().view2.setOnClickListener(..);
}
}
the way I'm suggesting seems coherent with OOP, also, if you say ClassA and ClassB I would assume that ClassA extends ClassB. What happens if you want to create a third class that extends ClassA?
Related
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.
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.
}
}
I have an android application that has an ExpandableList, in its Adapters getChildView(int,int,boolean,View,ViewGroup) I want to redraw the ExpandableList if some conditions were true, what should I do?
Here is the answer but it is NOT recommended:
This is your Acitivity:
public class MyActivity extends Activity implements MyAdapterListener{
private ExpandableListView expandableListView1;
private MyAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
expandableListView1 = (ExpandableListView) findViewById(R.id.expandableListView1);
adapter = new MyAdapter(this);
expandableListView1.setAdapter(adapter);
}
#Override
public void doRefresh() {
adapter.notifyDataSetChanged();
}
}
This is your Adapter:
public class MyAdapter extends BaseExpandableListAdapter{
public interface MyAdapterListener{
public void doRefresh();
}
private MyAdapterListener listener;
public MyAdapter(MyAdapterListener listener){
this.listener = listener;
}
#Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
if(some condition is true and you want to refresh the list view){
listener.doRefresh();
}
return null;
}
}
Hope this helps.
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.
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)