Changing a icon in ActionBar depending on a condition - java

I am updating a record in a SQLite database when the user presses an icon on the ActionBar. The information being updated is a flag that adds a record to a Favourites page.
PROBLEM
When the user adds or removes the record to favourites, I would like the icon in the ActionBar to change. I have a full star icon and a empty star icon.
The setIcon method displays the the full star icon if the record is a favourite, and a empty star icon if the record is not a favourite.
In the code below you will see I am using a boolean isInFavourite, which is true when String fav = "y".
When entering the Activity, the icon displayed is correct.
When the user clicks on the icon to invoke the onMenuItemClick() method, the record is successfully updated but the icon does not change.
I am unable to change the boolean isInFavourite when the record has been updated because Eclipse wants all the variables to be set as final
Can anyone help me change the icon to once the record has been updated.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
db = new DBHelper(this);
db.createDataBase();
db.openDataBase();
Bundle bundle = getIntent().getExtras();
final String rowid = bundle.getString("id");
final String fav = bundle.getString("fav");
//Boolean to check if record is a favourite
final boolean isInFavourite = fav.contentEquals("y");
menu.add("Add to Favourites")
.setOnMenuItemClickListener(new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
String toggle;
String message;
//Logic to add or remove row from recording.
if (isInFavourite) {
toggle = "n";
message = "Recipe removed from Favourites";
} else {
toggle = "y";
message = "Recipe added to Favourites";
}
//Update favourite record in database
db.updateFavourite(rowid, toggle);
db.close();
Toast.makeText(getApplicationContext(), message,
Toast.LENGTH_LONG).show();
return true;
}
})
//Set icon depending on whether record is a favourite or not.
.setIcon(isInFavourite ? R.drawable.fav_true : R.drawable.fav_false)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return true;
}
Thanks to #dmon for the solution
SOLUTION
private DBHelper db = null;
public String fav = null;
public String rowid = null;
public boolean isFav;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Bundle bundle = getIntent().getExtras();
rowid = bundle.getString("id");
fav = bundle.getString("fav");
if (fav.contentEquals("y")) {
isFav = true;
} else {
isFav = false;
}
try {
db = new DBHelper(this);
db.createDataBase();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.menu_settings, menu);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem fave = menu.findItem(R.id.add);
MenuItem unfave = menu.findItem(R.id.remove);
fave.setVisible(isFav);
unfave.setVisible(!isFav);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
case R.id.add:
fav = "n";
isFav = false;
updateFav();
supportInvalidateOptionsMenu();
Toast("Removed from Favourites");
return true;
case R.id.remove:
fav = "y";
isFav = true;
updateFav();
supportInvalidateOptionsMenu();
Toast("Added to Favourites");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void updateFav (){
db.openDataBase();
db.updateFavourite(rowid, fav);
db.close();
}
XML File: res/menu/menu_settings.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/add"
android:icon="#drawable/good"
android:showAsAction="always"
/>
<item
android:id="#+id/remove"
android:icon="#drawable/bad"
android:showAsAction="always"/>

The easiest way is to just provide two different buttons and hide/show them accordingly:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inf = new MenuInflater(this);
inf.inflate(R.menu.menu_xml, menu);
return true;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem fave = menu.findItem(R.id.favorite);
MenuItem unfave = menu.findItem(R.id.unfavorite);
fave.setVisible(!isFave);
unfave.setVisible(isFave);
return true;
}
Then you invalidate the options menu when the state has changed. Note that you have to have a global variable that has the current state of the item (where isFave is coming from)
invalidateOptionsMenu();

Make the isInFavourite variable a global field (declare it outside of the method, just in the class). Any local variables must be final for a scope below them to use them. However, if you make it global, and declare it above, it doesn't need to be final.

Related

RecyclerView cannot be sorted after using SearchView filter

I'm trying to implement a simple searching button using a SearchView to find items in a RecyclerView. However, this searching button breaks my sorting functions as the RecyclerView can no longer be sorted after typing in the SearchView.
Here is my Activity
private void sortAscending() {
Collections.sort(stationList, (station1, station2) -> {
if (station1.getData().getCurrent().getPollution().getAqius() > station2.getData().getCurrent().getPollution().getAqius()) {
return 1;
} else if (station1.getData().getCurrent().getPollution().getAqius() < station2.getData().getCurrent().getPollution().getAqius()) {
return -1;
} else {
return 0;
}
});
}
private void sortDescending() {
Collections.sort(stationList, (station1, station2) -> {
if (station1.getData().getCurrent().getPollution().getAqius() > station2.getData().getCurrent().getPollution().getAqius()) {
return -1;
} else if (station1.getData().getCurrent().getPollution().getAqius() < station2.getData().getCurrent().getPollution().getAqius()) {
return 1;
} else {
return 0;
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu here
getMenuInflater().inflate(R.menu.list_menu, menu);
MenuItem searchItem = menu.findItem(R.id.item_search);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setOnQueryTextListener(this);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//A String for the message to be displayed in a Toast
String msg = "";
//Switch and case on the MenuItem object's id
switch (item.getItemId()) {
case R.id.sort_ascending:
msg = "sorting by ascending.";
sortAscending();
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
break;
case R.id.sort_descending:
msg = "sorting by descending.";
sortDescending();
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
break;
}
adapter.notifyDataSetChanged();
return super.onOptionsItemSelected(item);
}
#Override
public boolean onQueryTextChange(String newText) {
filter(newText.toLowerCase());
return true;
}
private void filter(String text) {
ArrayList<Station> filteredList = new ArrayList<>();
for (Station item : stationList) {
if (item.getData().getCity().toLowerCase().contains(text.toLowerCase())) {
filteredList.add(item);
}
}
adapter.filterList(filteredList);
adapter.notifyDataSetChanged();
}
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
Now I suspect it has something to do with the onOptionsItemSelected method because I don't have anything in there that does something when the user clicks on the search button on the menu.
Here's the Adapter class for the RecyclerView, removed other functions for simplicity.
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
//...set text and information from ArrayList
//Launch an activity and user clicks on an item in RecyclerView
holder.itemView.setOnClickListener(view -> {
Intent intent = new Intent(context, CityActivity.class);
context.startActivity(intent);
});
}
//Function to set the ArrayList to the filtered list
//filteredList ArrayList is passed in from filter() in Activity
public void filterList(ArrayList<Station> filteredList) {
stationList = filteredList;
}
And here is the XML for the menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/item_sort"
android:icon="#drawable/ic_sort_black_24dp"
android:title="Sort"
app:showAsAction="ifRoom">
<menu>
<item android:id="#+id/sort_ascending"
android:title="Sort by ascending"/>
<item android:id="#+id/sort_descending"
android:title="Sort by descending"/>
</menu>
</item>
<item android:id="#+id/item_search"
android:icon="#drawable/ic_search_black_24dp"
android:title="Search"
app:showAsAction="always"
app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>
I'm having trouble figuring out why after entering information into the SearchView and removing it so the RecyclerView returns to its normal state, I cannot use the sort functions (ascending and descending) to sort the RecyclerView.
I have fixed my problem by checking if the text length is zero, which means no text is entered and user is not using the search. Then I just call the filterList function to set the current stationList as the one to display.
private void filter(String text) {
if (text.length() == 0) {
adapter.filterList(stationList);
} else {
ArrayList<Station> filteredList = new ArrayList<>();
for (Station item : stationList) {
if (item.getData().getCity().toLowerCase().contains(text.toLowerCase())) {
filteredList.add(item);
}
}
adapter.filterList(filteredList);
}
}
I also moved the adapter.notifyDataSetChanged(); into the filterList function for clarity.

How to make calls and visit website from context menu on long clicking listview items?

I am trying to learn the Android onLongclick context menu actions. When clicking an item on list it displays two action image one for calling and another for website url. But I am having null pointer exception. When toasting using the toast method it can display number and url. But this is not what i want to to. Its just for test. I want to dial the number when phone action is clicked and visit website when url action is clicked. I have commented toast for doing so. And tried to create perform call and performUrl() methods. I took idea from the example with toast so i tried to modify it but its simply not working so i commented the performCall() and performUrl() methods for now. Could anyone suggest how to make these things happen?
I have a department class which is a pure java class.
public class DepartmentActivity extends Activity {
//Department dept = new Department();
private ListView listView;
ArrayAdapter<Department> adapter;
//String list_item;
Object mActionMode;
private Department[] myDepartment = {
new Department("CS", "cs#yahoo.edu", "405.111.2222"),
new Department("Biology", "bio#yahoo.edu", "405.222.3333"),
new Department("Business", "business#yahoo.com", "405.333.4444"),
new Department("Music", "music#yahoo.com", "405.444.5555"),
new Department("Engineering", "engg#ucoll.com", "405.555.6666"),
new Department("Nursing", "nursing#yahoo.com", "213.555.6666")
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_department);
listView =(ListView) findViewById(R.id.list);
//adapter = new ArrayAdapter<>(getApplicationContext(), R.layout.deptlist, deptList);
adapter = new ArrayAdapter<>(getApplicationContext(), R.layout.deptlist, myDepartment);
listView.setAdapter(adapter);
//listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
//listener = new listView.OnItemLongClickListener(this);
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
//list_item = myDepartment.toString();
if (mActionMode != null){
return false;
}
mActionMode = DepartmentActivity.this.startActionMode(mActionModeCallback);
return true;
}
});
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
//mode.setTitle(list_item);
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
return true;
//return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Respond to clicks on the actions in the CAB
switch (item.getItemId()) {
case R.id.action_phone:
performCall();
mode.finish(); // Action picked, so close the CAB
return true;
case R.id.action_www:
//performUrl();
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
/*
private void performCall() {
SparseBooleanArray selected = listView.getCheckedItemPositions();
String selectedNames ="";
for (int i = 0; i < selected.size(); i++) {
if (selected.valueAt(i)) {
int pos = selected.keyAt(i);
selectedNames += " " + myDepartment[pos].getPhone();
}
}
Intent callIntent = new Intent(Intent.ACTION_DIAL);
callIntent.setData(Uri.parse("tel:"+selectedNames));
startActivity(callIntent);
//Toast.makeText(DepartmentActivity.this, "Call: " + selectedNames,
// Toast.LENGTH_SHORT).show();
}
*/
/*
private void performUrl() {
SparseBooleanArray selected = listView.getCheckedItemPositions();
String selectedNames = "";
for (int i = 0; i < selected.size(); i++) {
if (selected.valueAt(i)) {
int pos = selected.keyAt(i);
selectedNames += " " + myDepartment[pos].getUrl();
}
}
//Toast.makeText(DepartmentActivity.this, "Url: " + selectedNames,
//Toast.LENGTH_SHORT).show();
}
*/
Stack trace
....PID: 3250
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.util.SparseBooleanArray.size()' on a null object reference
at esu.uco.rawal.p4rabina.DepartmentActivity.performCall(DepartmentActivity.java:108)
at esu.uco.rawal.p4rabina.DepartmentActivity.access$100(DepartmentActivity.java:18)
at esu.uco.rawal.p4rabina.DepartmentActivity$2.onActionItemClicked(DepartmentActivity.java:85)
at com.android.internal.policy.PhoneWindow$DecorView$ActionModeCallback2Wrapper.onActionItemClicked(PhoneWindow.java:3540)
at com.android.internal.app.WindowDecorActionBar$ActionModeImpl.onMenuItemSelected(WindowDecorActionBar.java:1093)
at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:761)
at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:904)
at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:894)
at android.widget.ActionMenuView.invokeItem(ActionMenuView.java:616)
at com.android.internal.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:141)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Thank You

Change android:icon dynamically

How to change the the action bar icon dynamically in the JAVA code?
See the image, the icon number 2.
(source: android.com)
What I want to do is a flip between two icons I got. For example when the user click on the "search icon[2]" it will be change to the world icon.
So there's the code I got.
menu.xml
<item android:id="#+id/actionMenu"
android:icon="#drawable/icon1"
android:showAsAction="ifRoom" />
Then we inizializate the menu at JAVA code with this:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
Then, here we go to handle this.
First, we make a switch to know if it exist an click or not.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.actionMenu:
changeIcon(); // Here we call that magic function
return true;
default:
return super.onOptionsItemSelected(item);
}
}
So, then we call the changeIcon(); this function needs the magic
private void changeIcon(){
try {
if(this.theSwitcher){
// What code need this function?
// I just need to change icon1 to icon2
this.theSwitcher = false;
} else {
// What code need this function?
// I just need to change icon2 to icon1
this.quince = true;
}
} catch (Exception e) {
Log.e("MyBad", "Error: " + e);
}
}
Try the following
private void changeIcon(){
MenuItem mi = mMenu.findItem(R.id.actionMenu);
try {
if(this.theSwitcher){
// What code need this function?
// I just need to change icon1 to icon2
mi.setIcon(R.drawable.icon2);
this.theSwitcher = false;
} else {
// What code need this function?
// I just need to change icon2 to icon1
mi.setIcon(R.drawable.icon1);
this.quince = true;
}
} catch (Exception e) {
Log.e("MyBad", "Error: " + e);
}
}
And in
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
mMenu = menu;
}

Login/Logout with Action Bar/Menu Item

I have a single menu item that shows up in my action bar and I would like it to display "Log In" or "Log Out" depending on whether the user is logged in or logged out. However, I cannot get it to change text because in order to do so I have to call invalidateOptionsMenu() from inside my onOptionsSelected() method. I currently have a method that updates the text that should show, and this works fine, but in order to display that text I have to recreate the options menu.
Here is some of my code:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
this.menu = menu;
updateMenuTitles();
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.login:
if (!loggedIn) {
Authentication();
} else {
loggedIn = false;
authentication = false;
updateMenuTitles();
Toast.makeText(getApplicationContext(),"Log Out Successful",Toast.LENGTH_SHORT).show();
}
break;
}
return super.onOptionsItemSelected(item);
}
private void updateMenuTitles() {
MenuItem bedMenuItem = menu.findItem(R.id.login);
if (loggedIn) {
bedMenuItem.setTitle("Log Out");
}else {
bedMenuItem.setTitle("Login");
}
}
As I'm using firebase, I'm checking if user is null or not. Based on that I'm adding which menu option I need.
Something like this:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
if (firebaseUser!=null){
inflater.inflate(R.menu.landing_logout,menu);
}else {
inflater.inflate(R.menu.landing_login, menu);
}
return true;
}
I'd suggest to have two different buttons in your menu and switch their visibility according to the needs.
Something like this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.login:
loggedIn = true;
Authentication();
updateMenuTitles();
break;
case R.id.logout:
loggedIn = false;
authentication = false;
updateMenuTitles();
Toast.makeText(getApplicationContext(),"Log Out Successful",Toast.LENGTH_SHORT).show();
break;
}
return super.onOptionsItemSelected(item);
}
private void updateMenuTitles() {
MenuItem loginMenuItem = menu.findItem(R.id.login);
MenuItem logoutMenuItem = menu.findItem(R.id.logout);
if (loggedIn) {
loginMenuItem.setVisibility(View.VISIBLE);
logoutMenuItem.setVisibility(View.GONE);
}else {
logoutMenuItem.setVisibility(View.VISIBLE);
loginMenuItem.setVisibility(View.GONE);
}
}

How to add Line Break on ViewText?

I'm a newbie. I'm editing Searchable Dictionary given to sample on SDK.
Dictionary follows a database on \res\raw\definitions.txt
when searching word shows result (defination) like below-
line1 line2 line3
But I want to add line break on result and show (defination) like below-
line1
line2
line3
code of WordActivity.java is here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.word);
Uri uri = getIntent().getData();
Cursor cursor = managedQuery(uri, null, null, null, null);
if (cursor == null) {
finish();
} else {
cursor.moveToFirst();
TextView word = (TextView) findViewById(R.id.word);
TextView definition = (TextView) findViewById(R.id.definition);
int wIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_WORD);
int dIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_DEFINITION);
word.setText(cursor.getString(wIndex));
definition.setText(cursor.getString(dIndex));
}}
public void onBackPressed() {
onSearchRequested();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// TODO Auto-generated method stub
case R.id.search:
onSearchRequested();
return true;
case R.id.about:
setContentView(R.layout.abouttanzil);
return true;
case R.id.exit:
moveTaskToBack(true);
return true;
default:
return false;
}
}
}
Please let me know what should i add and where to make line break??
Thanks in advance.
Display it as HTML and use \n.
yourTextView.setText(Html.fromHtml(someText + "\n" + someOtherText));
Obviously there are smarter ways. Just showing you the Html.fromHtml() method.

Categories