I am trying to implement some code on every activity and don't want to copy and page the code into each activity.
Originally I just had a parent activity with the code then extended all of the others but I couldn't do this on ListActivities or ExpandableListActivities.
I think this will be done by using an interface class then having each activity implement this. However when I try to do this Eclipse gives me an error and says to remove the method body.
Here is what I have so far
import android.content.Intent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
public interface MenuOptions {
/**
* Method called when the hardware menu button is called. Uses optionmenu.xml for layout
*/
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.optionmenu, menu);
return true;
}
/**
* Event listener for the options menu. If home is pressed user is sent to home screen. If settings is pressed user is sent to setting screen
* User is passed as an extra
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent nextIntent = null;
switch (item.getItemId()) {
case R.id.home:
Toast.makeText(this, "You pressed the icon!", Toast.LENGTH_LONG).show();
nextIntent = new Intent(this, Home.class);
break;
case R.id.settings:
Toast.makeText(this, "You pressed the text!", Toast.LENGTH_LONG).show();
nextIntent = new Intent(this, Settings.class);
break;
}
nextIntent.putExtra("user", user);
startActivity(nextIntent);
return true;
}
}
Interface classes in Java should only contain method signatures and no implementation. Therefore you have to create a base class:
public class MenuOptions extends Activity {
/**
* Method called when the hardware menu button is called. Uses optionmenu.xml for layout
*/
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.optionmenu, menu);
return true;
}
/**
* Event listener for the options menu. If home is pressed user is sent to home screen. If settings is pressed user is sent to setting screen
* User is passed as an extra
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent nextIntent = null;
switch (item.getItemId()) {
case R.id.home:
Toast.makeText(this, "You pressed the icon!", Toast.LENGTH_LONG).show();
nextIntent = new Intent(this, Home.class);
break;
case R.id.settings:
Toast.makeText(this, "You pressed the text!", Toast.LENGTH_LONG).show();
nextIntent = new Intent(this, Settings.class);
break;
}
nextIntent.putExtra("user", user);
startActivity(nextIntent);
return true;
}
}
And your activity:
public class YourActivity extends MenuOptions {
/*...*/
}
Interfaces are not allowed to have method bodies. This is because it describes the interface of an object, not the method itself.
The problem with the same menu options shared by all activities is a common one. The easiest way of handling this is to define 2 or 3 superclasses (for ListActivities and MapActivities).
As far as I know an interface only describes what methods must be supported by a class implementing the interface.
This is the difference between implementing an interface and extending a class.
- so you can't write an interface thats used by all your classes, only define a set of methods which must be defined by any class implementing that interface
I would say your best bet is to create a new class which can be used by all your activities to do the same job. Then you'll need an instance of that class which is created and initialised by each activity onCreate (or somewhere else)
Alternately you may find you can make your new class static so you don't need to create an instance, just call a static method at the right times
One other way of doing it is to create a subclass of Activity which includes the standard behaviour and create all your activities as a subclass of this. Where you have ListActivities (for instance) you'll have to do some recoding, making them subclassed activities containing lists
Related
I would like to call notifyDataSetChanged() but my code structure is a mess and I cannot get my way around it. I have a fragment, a custom dialog class, and MainActivity.
MainActivity.java
Fragment is created here along with 3 other fragments. Dialog is also created here
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = new FragmentHome();
loadFragment(fragment);
loadCustomActionBar("Home", false);
return true;
case R.id.navigation_foods:
fragment = new FragmentFood();
loadFragment(fragment);
loadCustomActionBar("Foods", true);
return true;
case R.id.navigation_drinks:
fragment = new FragmentDrink();
loadFragment(fragment);
loadCustomActionBar("Beverages", true);
return true;
case R.id.navigation_cart:
fragment = new FragmentCart();
loadFragment(fragment);
loadCustomActionBar("Cart", false);
return true;
}
return false;
}
};
private void loadCustomActionBar(String titleTxt, boolean bool) {
CustomDialog dialog = new CustomDialog()
.....
}
CustomDialog
CustomDialog is responsible for updating database.
I want to call notifyDataSetChanged() here after updating database to update view as well.
Button addItem = .....
addItem.setOnClickListener(new OnClickListener() {
public void onClick() {
dialog.show();
}
});
Fragment.java
ListAdapter is located here
I have tried using interfaces, but either I did it wrong or it doesn't work. Kindly help please. What are other alternatives for interfaces?
EDIT :
It seems you do not get my problem. I JUST WANT TO ACCESS THE LISTADAPTER CONTAINED IN THE FRAGMENT FROM MY CUSTOM DIALOG, PEOPLE OF THIS SITE.
As I see in your code you have a variable fragment. For your case you can define some BaseFragment class in which you will have abstract method datasetChanged(). All your separate fragments will be derivatives of BaseFragment and your variable fragment will be of type BaseFragment. Derivative fragments will implement function datasetChanged() in which they will call notifyDatasetChanged for their own adapters.
Then you can have a callback in your CustomDialog which will call datasetChanged of your variable fragment.
class BaseFragment extends Fragment {
...
abstract void datasetChanged();
...
}
class CustomDialog extends Dialog {
...
var notifyCallback: Function;
public setCallback(Function callback) {
notifyCallback = callback;
}
}
Then when you will create CustomDialog just set the callback to your fragments method datasetChanged():
customDialog.setCallback(fragment::datasetChanged());
After that, when your customDialog will need to notify for updates it will call for callback function.
notifyCallback.apply()
There are multiple approaches to achieve this
Send the instance of the fragment to the CustomDialog like CustomDialog dialog = new CustomDialog(fragment). Now that the CustomDialog has the reference to the fragment you can call a method in the fragment that would fetch the new data from the db and update it's views
Use EventBus library. You would register the Fragment as a listener to EventBus and implement onEventMainThread(YourEventName event).And the CustomDialog would post the YourEventName event to the EventBus after it updates the db. The EventBus will deliver this event to the fragment since it is registered to listen to YourEventName
Try to refresh the views in the onResume of the fragment. This would work in the case where the dialog and fragment are not accessible simultaneously
I am using the same tabBar across multiple Activities. And since there are extensive logic involved for onOptionsItemSelected, I want to write the relevant methods once and then reuse them. Hence I am deciding to created a super class called CustomActionBarActivity and then have the children activities extend it. One particular problem I need help with is how can I tell which child has caused the onOptionsItemSelected to be called? To elucidate, I will present the general code and then a failed attempt at a solution.
Here is the general code
public class CustomActionBarActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.tab_dog:
startActivity(new Intent(this, DogActivity.class));
return true;
case R.id.tab_cat:
startActivity(new Intent(this, CatActivity.class));
return true;
case R.id.tab_mouse:
startActivity(new Intent(this, MouseActivity.class));
return true;
case R.id.tab_goose:
startActivity(new Intent(this, GooseActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Failed attempt
If I try, for instance,
case R.id.tab_dog:
if(!(this instanceof DogActivity))
startActivity(new Intent(this, DogActivity.class));
then I get a compile error such that CustomActionBarActivity is not compatible with DogActivity. Thanks for any help you can provide.
Instead of having your parent class inspect the children using reflection (which is pretty fragile since it doesn't scale with the number of children subclasses you create), maybe you could take advantage of dynamic dispatch instead.
For example, maybe you could declare an abstract method in your parent activity like:
protected abstract void onTabItemSelected(MenuItem item);
Then your children activities can override this method depending on the desired behavior. For example, DogActivity might implement it like this:
protected boolean onTabItemSelected(MenuItem item) {
if (item.getItemId() != R.id.dog_tab) {
startActivity(new Intent(this, DogActivity.class));
return true;
}
return false;
}
The onOptionsItemSelected method would then be implemented like this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (onTabItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
Let me know if I misunderstood the question. Either way, you might be able to modify this approach to suit your use case.
Well lets say that I have an app that have like 50-60 buttons all around and I want to handle all click methods inside other package. How could I handle click for package app.test; class one in package app.test.clicks class clicks?
Create a class which implements OnClickListener,
public class ClickHandler implements OnClickListener
{
public void onClick(View v) {
//This method will be automatically implemented once OnClickListener is implemented.
}
}
Now set the onClickistener to your button like this,.
button.setOnClickListener(new ClickHandler());
And now inside the onClick() just do this,
public void onClick(View v) {
if(v.getId()==R.id.button)
{
//your stuff here.
}
}
If you need context object then,try v.getContext();. "v" is the parameter form the onClick().
make sure you import the package name of your ClickHandler class into your Activity.
But it would be much better if you had this as an inner class for each Activity.
public class HeaderActivity extends Activity implements OnClickListener{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((Button)findViewById(R.id.home)).setOnClickListener(this);
((Button)findViewById(R.id.search)).setOnClickListener(this);
((Button)findViewById(R.id.list)).setOnClickListener(this);
((Button)findViewById(R.id.filter)).setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.home:
Intent home=new Intent(this,HomeScreen.class);
startActivity(home);
finish();
break;
case R.id.search:
Intent search=new Intent(this,SearchScreen.class);
startActivity(search);
finish();
break;
case R.id.list:
Intent list=new Intent(this,ListScreen.class);
startActivity(list);
finish();
break;
case R.id.filter:
Intent filter=new Intent(this,FilterScreen.class);
startActivity(filter);
finish();
break;
default : break;
}
}
Well I don't think it will be a great idea to implement. However you need to pass the Context to clicks class. From context object you'll be able to get to the control which has been clicked and you can write your logic accordingly.
I'm 100% sure this is going to be one of those newbie questions, but here it goes...
Is there a way I can write a method in one activity and be able to access it from the others?
Example:
I have six activites in my app, each with it's own menu.xml because the options available for each need to be different, and I have these menus & menuitems set up as shown:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.calculator_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle item selection
switch (item.getItemId()) {
case R.id.menuItem_calculator_Help:
helpDialogGo();
return true;
case R.id.menuItem_calculator_Settings:
//settingsActivityGo();
return true;
case R.id.menuItem_calculator_Share:
shareGo();
return true;
case android.R.id.home:
// app icon in Action Bar clicked; go home
Intent uptohome = new Intent(this, Main.class);
uptohome.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(uptohome);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
The an example of one of these methods is:
private void helpDialogGo() {
Toast.makeText(this, "help", Toast.LENGTH_LONG).show();
AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
alt_bld.setMessage("Sorry, no help has been written since this application is still in development. This is a prerelease version.")
.setCancelable(false)
.setPositiveButton("Cool", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Action for 'Yes' Button
dialog.cancel();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Action for 'NO' Button
dialog.cancel();
}
});
AlertDialog alert = alt_bld.create();
// Title for AlertDialog
alert.setTitle("Pixel Help");
// Icon for AlertDialog
alert.setIcon(R.drawable.question);
alert.show();
}
So is there a way to have this custom method shared among all the activities and run it when the button is pressed in each of them, as to avoid having large amounts of code replicated across my app?
And if so, are there any potholes that I may hit? (Some of the menu items are going to bring up dialogs, others will take the user to a new activity)
Do you have similar menuitems in every activity? i.e. same number of items but different behaviour? If yes...
How about creating a BaseActivity which overrides onCreateOptionsMenu and onOptionsItemSelected() methods.. (As you have given in the above example). All your activities should inherit from this BaseActivity and then override the menu handling methods. eg. helpDialogGo() will go to the new class.
so the BaseActivity will have onCreateOptionsMenu and onOptionsItemSelected() methods. Plus all the menuItem actions (i.e. helpDialogGo() etc) as empty methods. The inherited classes will overide menuItem Actions.
If the menuitems are not similar in each activity, you are better off creating menu for each activity.
EDIT:
Not sure what you expect more. I thought I made it clear. Let me try again.
Class BaseActivity extends Activity.
BaseActivity extends Activity {
// Copy your onCreateOptionsMenu() and onOptionsItemSelected() methods here
protected void helpDialogGo() { }
// ... other methods
}
Class MyActivity1 extends BaseActivity.
MyActivity1 extends BaseActivity {
// Copy your helpDialogGo() code in full here and then make
// any specific changes to menu behaviour based on activity.
}
Class MyActivity2 extends BaseActivity
MyActivity2 extends BaseActivity {
// Copy your helpDialogGo() code in full here and then make
// any specific changes to menu behaviour based on activity.
}
One way, of course, is to created some custom classes that encapsulate your desired functionality - and use those within your activities. It's a better abstraction than placing the implementation directly in the Activity(s) itself (all things being equal, and based on what you described so far).
Any time you find yourself duplicating an implmentation that's a flag reminding you this is a good place to roll that code into its own class - usually.
I have an Activity in a Library that has a menu in it. The menu has all the standard attributes as well as the #Override on onCreateOptionsMenu and onOptionsItemSelected.
In my actual project which imports above library I have another activity that extends teh above activity. For this specific implementation of this program I want to have a different menu on this activity so inside this activities code base add add onCreateOptionsMenu and onOptionsItemSelected with the proper #Override, but i get the menu from the Library, not the override in the library.
What could I be doing wrong?
Base Class:
public class ListItems extends ListActivity {
public static final int LOGOUT = 0;
public static final int HISTORY = 1;
public static final int REFRESH = 2;
#Override
public boolean onCreateOptionsMenu(Menu menu){
menu.add(0, LOGOUT ,0,"Log Out");
menu.add(0,HISTORY,0,"Order History");
menu.add(0,REFRESH,0,"SMS");
return true;
}
#Override
public boolean onOptionsItemSelected (MenuItem item){
TextView textView = (TextView)findViewById(com.imobileminutes.library.R.id.text);
switch (item.getItemId()){
case LOGOUT:
Intent logOut = new Intent(ListItems.this,MainScreen.class);
startActivity(logOut);
finish();
return true;
case HISTORY:
Intent orderHistory = new Intent(this,OrderItems.class);
startActivity(orderHistory);
return true;
case REFRESH:
Intent orderHistory = new Intent(this,OrderItems.class);
startActivity(orderHistory);
return true;
}
return false;
}
}
Override Class
public class ListItems extends com.imobileminutes.library.ListItems {
static final int SMS = 2;
#Override
public boolean onCreateOptionsMenu(Menu menu){
Log.d("onCreateOptionsMenu", "My onCreateOptionsMenu ran");
Toast.makeText(getApplicationContext(), "HEllo Menu Created", Toast.LENGTH_LONG).show();
super.onCreateOptionsMenu(menu);
return true;
}
#Override
public boolean onOptionsItemSelected (MenuItem item){
Log.d("onCreateOptionsMenu", "My onOptionsItemSelected ran");
Toast.makeText(getApplicationContext(), "HElloonOptionsItemSelected", Toast.LENGTH_LONG).show();
super.onOptionsItemSelected(item);
return false;
}
}
CONCLUSION
It ended up being that my library was calling ListItems.this and CreateAccount.this which was referencing the activities in the library not in the extended classes.
I added an intent for every extended activity type and nwo call that, thus bringing up the extended activity rather than the base activity.
Thanks for the help guys.
Either you didn't copy and paste the code correctly, or part of the problem is in your base class, which provides the same definition for onCreateOptionsMenu as your derived class:
#Override
public boolean onCreateOptionsMenu(Menu menu){
menu.add(0, LOGOUT ,0,"Log Out");
menu.add(0,HISTORY,0,"Order History");
menu.add(0,REFRESH,0,"SMS");
return true;
}
That last line can't be right -- I'm pretty sure the last line of the base class implementation should be: menu.add(0,REFRESH,0,"REFRESH");.
But that last line of onCreateOptionsMenu also needs to be changed in the derived class:
#Override
public boolean onCreateOptionsMenu(Menu menu){
menu.add(0, LOGOUT ,0,"Log Out");
menu.add(0, HISTORY,0,"Order History");
menu.add(0, SMS,0,"SMS"); // Use the correct constant (SMS instead of REFRESH)
return true;
}
EDIT: Also, you can simplify the code a bit, by re-using the superclass implementation for those choices where you're currently just copying the superclass code (LOGOUT and HISTORY):
#Override
public boolean onOptionsItemSelected (MenuItem item){
TextView textView = (TextView)findViewById(com.imobileminutes.library.R.id.text);
// Left this as a 'switch'; you might want to use an 'if' instead.
switch (item.getItemId()){
case SMS:
AppClass.sendSMS("8135551212", "Hello Jason");
return true;
}
return super.onOptionsItemSelected(item);
}
Use the fully qualified name on the import my suspicion is you are referencing the wrong class.
Turns out it was the way I was calling the Activitys. I was calling the Activity directly in the Library rather than calling the overridden one. Since the overriden ones will be dynamic depending on the Apps naming, I ended up using Intents that will search for the callers packagename and append the specific intent onto it.
As soon as I set all that up, my SMS menu item showed.