Manipulating with array adapter or with the underlaying data? - android

In the book The Busy Coders Guide to Android Development from CommonsWare, there is a chapter explaining how to work with the context menus.
In one example in that chapter, the context menu offers option for removing item from a list view that is generated from an ArrayList<String> object named words.
In the example the onContextItemSelected method is implemented like this:
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
ArrayAdapter<String> adapter=(ArrayAdapter<String>)getListAdapter();
switch (item.getItemId()) {
case R.id.remove:
adapter.remove(words.get(info.position));
return true;
default:
...
}
The line where adapter.remove(...) is called seems strange to me because of following fact:
Let's say the words object contains following items (in that order)
alfa
beta
gama
alfa
Now, when user loads the context menu upon the 2nd alfa and selects the option for removing it, the mentioned line actually removes the 1st alfa. And that seems wrong to me.
Instead, I would do something like this:
...
words.remove(info.position);
adapter.notifyDataSetChanged();
...
I'm not that good in Java and Android programming, so I would like to hear your opinion to this, because I want to be sure I understand well how adapters should be used.

Your idea sounds good.
The example is defunct if it behaves like you have described and you should tell Mark about it so he can check it as well (he'll probably do it anyway since he is very active on this site).

Related

Android Room - Select All then write to text file

I followed the Android Room with a View tutorial and I was successfully able to recreate the tutorial app. All of my code can be found in that tutorial. The only difference with my app now is my MainActivity is a webView and then when the user clicks on a button in the toolbar, they're taken to the Room with a View app functionality exactly how it is in the tutorial. So essentially I just added another activity beforehand.
I've tried using the methods getAllWords() and getAlphabetizedWords() associated with LiveData and dumped the results into the Logcat, but they always come out in a hash-like string (Ex: Word#6f2f356). I'm trying to get the exact value of the word I'm pulling from the Room database.
I have 2 issues currently:
I'd like to select all of the words in the Room database table from the MainActivity.
After selecting all terms from the table, I'd like to write them all to a text file or CSV.
In my MainActivity I'm using the function below to send the user to the start of the Room with a View tutorial functionality (WordList). My WordList class is the equivalent to the tutorial's MainActivity. Within this WordList class, I've tried using getAllWords() and it returns the terms in a hash-like state.
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.nix_menu_back:
onBackPressed();
break;
case R.id.nix_menu_forward:
onForwardPressed();
break;
case R.id.nix_menu_refresh:
nixWebView.reload();
break;
case R.id.nix_string_list:
nixWebView.reload();
Intent intent = new Intent(this, WordList.class);
startActivity(intent);
break;
}
return super.onOptionsItemSelected(item);
}
Can someone please explain to me how to select all terms in the Room database and output them in a readable format to Logcat and an internal text file?
Any help is appreciated, and my sincerest apologies if this is a repeat! I've checked around SO and the only question I found related to my situation didn't cover this situation.
I'm happy to provide more code if asked. Thank you everyone!
Hashy looking strings
If you want your Word object to be printed in a readable object you should override the method public String toString() for that class since the default implementation returns that hashy looking String.
Some light on this could be:
public class Word {
// Other stuff you may have
#Override
public String toString() {
return this.wordID+" - "this.wordContent;
}
}
And this could print: "12 - Cat"
To list all words.
The methods provided in that codelab are enough to list all the words contained in the SQLite DB.
To save them in an external file as plain text.
Check this other stack overflow question
Don't forget to add
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
In your AndroidManifest.xml file!

Change fragments from spinner drop down items?

My activity has a spinner and an empty container where fragments should be added when selecting drop down items from spinner.
My plan was to try to make switch construction inside into override method "public void onItemSelected()", where each case represents one drop down item from spinner, and sets correct fragment into container, like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
switch (itemSelectedFromSpinner) {
case "first item": // First and second item put same fragment into container, but do other methods when used
case "second item": // my code
}
My other taught was to put it in if construction like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
if (itemSelectedFromSpinner.equals("first item") || itemSelectedFromSpinner.equals("second item")){
// my code }
Since I've never done something like this, and I believe you can understand from my question what needs to be done, tell me what is the best practice to do that.
Am I doing it right by putting a String itemSelectedFromSpinner into switch construction? Also if user selects one item and first fragment is loaded, when selecting other item will the first fragment disappear and put second fragment into container automatically? (sorry if this sound little silly to you, I have lack of experience with fragments)
Don't use string like "first item" in code directly - move them to string resources.
For components like spinner use Adapter. The concept of Adapters use widely in Android so it's a good idea to be familiar with it. And also it allow you to compare your data by some integer asigned id's, and not by strings (which is unefficient, slow and ugly - correcting string representation everywhere is hard).
To replace or add Fragments dynamically use FragmentManager. See the simple replace() / add() / commit() code

Actionbar navigation

My android application targets the latest platform. I am new to the platform, and read bit conflicting information on actionbar. The way I was using it for navigation was.
menu.xml
<menu>
<item android:id="#+id/action_sort_size"
android:icon="#android:drawable/ic_menu_sort_by_size"
android:title="#string/action_barabc"
android:onClick="abc" />
<item android:id="#+id/action_sort_alpha"
....
In my activity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void abc(MenuItem item) {
//...
}
this works, but the back/up navigation is not working correctly. could be unrelated, still like to confirm.
But, I also see implementation like here
where it switches on item.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menuitem1:
Toast.makeText(this, "Menu Item 1 selected", Toast.LENGTH_SHORT)
.show();
break;
case R.id.menuitem2:
....
}
Which is the better approach?
The better approach, in my opinion, is the switch approach. There aren't many reasons why, but I'll list them:
The code is centralized. You don't have x amount of methods that basically do the same thing. It keeps your code more readable; it is "cleaner". You also get a default statement using the switch, this can help if you mess up and forget to make a case specifically for an element in the layout.
If you really wanted to have a centralized method using xml, you would have onClick reference the same method and check the ids of the View parameter. Which is essentially the same asonOptionsItemSelected.
It is a part of the API. The Android engineers would not have made it a part of the API if they didn't want the developer to use it. Yes the XML is techinally API, but XML should be used more for layouts and visuals, not for logic.
Everyone uses it. All the tutorials I have seen and everyone's code uses this method. It is now more of a convention.
It's largely personal, but if it looks like it's a convention, and everyone uses it, I'd adhere to it. Especially if you're working as part of a team. Different coding styles for such arbritrary things should be avoided.
And concerning your back/up navigation, it shouldn't make a difference which way you do it, since you have to implement the same code to get that navigation type.

Add/change compatibility SimpleMenu/MenuItem from compatibility Fragment

I realise this is a specific problem however I feel like others who are dealing with compatibility must have dealt with this.
As the user swipes through various fragments in the ViewPager I would also like the actionBar menu items to change. I am not sure if this is easily doable for the compatibility actionbar, any direction or help would be extremely appreciated. I am changing the title simply via setTitle() since the ActionBarHelper handles this however i cant find anything for updating the menu items. I tried the following but it fails..
public void setMenuDynamically(int resId){
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(resId, menu);
}
Looking through the code it seems there should be an easy/obvious way to get a handle to the SimpleMenu and add an item and set its icon.
Thanks in advance ( I am hoping the google boys are reading this as the android developers Google+ suggests)
Ok well please let me know if I suck as describing things or if there just is not much knowledge on this topic. If the first I am really sorry guys. Regardless I seemed to get this to work but am unsure if this is the correct way.
AcitonBarHelper
public void updateMenu(MenuItem item) {
}
ActionBarHelperBase (for 2.2 - 3.0 devices )
#Override
public void updateMenu(MenuItem item){
addActionItemCompatFromMenuItem(item);
}
And create similar methods for honeycomb and ICS
finally i have a listener for page changing and in that listener i call...
public void setMenuDynamically(int resId, String title){
MenuItem item = menu.add(title);
item.setIcon(resId);
getActionBarHelper().updateMenu(item);
MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
}
I am not sure if the MenuItemCompat is necessary but I included it nonetheless. Everything seems to work great for 2.2 at least. I will most likely have to make changes in the Overrides but I can handle myself from here.

How to use Android Spinner like a drop-down list

It's taken me quite a while to get my head around the Android Spinner. After several failed implementation attempts, and after reading many questions partially similar to my own but without satisfactory answers, and some without any answers at all, e.g. here and here, I finally get that a "spinner" in Android isn't meant to be the same thing as a "drop-down list" from desktop apps, or a select in HTML. However, what my app (and I'm guessing the apps of all the other posters whose questions are similar) needs is something that works like a drop-down box, not like a spinner.
My two problems are with what I first considered to be idiosynchrasies the OnItemSelectedListener (I've seen these as separate questions on this site but not as one):
An initial selection of the first list item is triggered automatically without the user's interaction.
When the item that was already selected is selected again by the user, it is ignored.
Now I realise that, when you think about it, it makes sense for this to happen on a spinner - it has to start with a default value selected, and you spin it only to change that value, not to "re-select" a value - the documentation actually says: "This callback is invoked only when the newly selected position is different from the previously selected position". And I've seen answers suggesting that you set up a flag to ignore the first automatic selection - I guess I could live with that if there's no other way.
But since what I really want is a drop-down list which behaves as a drop-down list should (and as users can and should expect), what I need is something like a Spinner that behaves like a drop-down, like a combo-box. I don't care about any automatic pre-selection (that should happen without triggering my listener), and I want to know about every selection, even if it's the same one as previously (after all, the user selected the same item again).
So... is there something in Android that can do that, or some workaround to make a Spinner behave like a drop-down list? If there is a question like this one on this site that I haven't found, and which has a satisfactory answer, please let me know (in which case I sincerely apologise for repeating the question).
+1 to David's answer. However, here's an implementation suggestion that does not involve copy-pasting code from the source (which, by the way, looks exactly the same as David posted in 2.3 as well):
#Override
void setSelectionInt(int position, boolean animate) {
mOldSelectedPosition = INVALID_POSITION;
super.setSelectionInt(position, animate);
}
This way you'll trick the parent method into thinking it's a new position every time.
Alternatively, you could try setting the position to invalid when the spinner is clicked and setting it back in onNothingSelected. This is not as nice, because the user will not see what item is selected while the dialog is up.
Ok, I think I've come up with a solution for my own situation with the help of both David's and Felix' answer (I believe David's helped Felix', which in turn helped mine). I thought I'd post it here together with a code sample in case someone else finds this approach useful as well. It also solves both of my problems (both the unwanted automatic selection and the desired re-selection trigger).
What I've done is added a "please select" dummy item as the first item in my list (initially just to get around the automatic selection problem so that I could ignore when it was selected without user interaction), and then, when another item is selected and I've handled the selection, I simply reset the spinner to the dummy item (which gets ignored). Come to think of it, I should've thought of this long ago before deciding to post my question on this site, but things are always more obvious in hindsight... and I found that writing my question actually helped me to think about what I wanted to achieve.
Obviously, if having a dummy item doesn't fit your situation, this might not be the ideal solution for you, but since what I wanted was to trigger an action when the user selected a value (and having the value remain selected is not required in my specific case), this works just fine. I'll try to add a simplified code example (may not compile as is, I've ripped out a few bits from my working code and renamed things before pasting, but hopefully you'll get the idea) below.
First, the list activity (in my case) containing the spinner, let's call it MyListActivity:
public class MyListActivity extends ListActivity {
private Spinner mySpinner;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: other code as required...
mySpinner = (Spinner) findViewById(R.id.mySpinner);
mySpinner.setAdapter(new MySpinnerAdapter(this));
mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> aParentView,
View aView, int aPosition, long anId) {
if (aPosition == 0) {
Log.d(getClass().getName(), "Ignoring selection of dummy list item...");
} else {
Log.d(getClass().getName(), "Handling selection of actual list item...");
// TODO: insert code to handle selection
resetSelection();
}
}
#Override
public void onNothingSelected(AdapterView<?> anAdapterView) {
// do nothing
}
});
}
/**
* Reset the filter spinner selection to 0 - which is ignored in
* onItemSelected() - so that a subsequent selection of another item is
* triggered, regardless of whether it's the same item that was selected
* previously.
*/
protected void resetSelection() {
Log.d(getClass().getName(), "Resetting selection to 0 (i.e. 'please select' item).");
mySpinner.setSelection(0);
}
}
And the spinner adapter code could look something like this (could in fact be an inner class in the above list activity if you prefer):
public class MySpinnerAdapter extends BaseAdapter implements SpinnerAdapter {
private List<MyListItem> items; // replace MyListItem with your model object type
private Context context;
public MySpinnerAdapter(Context aContext) {
context = aContext;
items = new ArrayList<MyListItem>();
items.add(null); // add first dummy item - selection of this will be ignored
// TODO: add other items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int aPosition) {
return items.get(aPosition);
}
#Override
public long getItemId(int aPosition) {
return aPosition;
}
#Override
public View getView(int aPosition, View aView, ViewGroup aParent) {
TextView text = new TextView(context);
if (aPosition == 0) {
text.setText("-- Please select --"); // text for first dummy item
} else {
text.setText(items.get(aPosition).toString());
// or use whatever model attribute you'd like displayed instead of toString()
}
return text;
}
}
I guess (haven't tried this) the same effect could be achieved using setSelected(false) instead of setSelection(0), but re-setting to "please select" suits my purposes fine. And, "look, Ma, no flag!" (Although I guess ignoring 0 selections is not that dissimilar.)
Hopefully, this can help someone else out there with a similar use case. :-) For other use cases, Felix' answer may be more suitable (thanks Felix!).
Look. I don't know if this will help you, but since you seem tired of looking for an answer without much success, this idea may help you, who knows...
The Spinner class is derived from AbsSpinner. Inside this, there is this method:
void setSelectionInt(int position, boolean animate) {
if (position != mOldSelectedPosition) {
mBlockLayoutRequests = true;
int delta = position - mSelectedPosition;
setNextSelectedPositionInt(position);
layout(delta, animate);
mBlockLayoutRequests = false;
}
}
This is AFAIK taken from 1.5 source. Perhaps you could check that source, see how Spinner/AbsSpinner works, and maybe extend that class just enough to catch the proper method and not check if position != mOldSelectedPosition.
I mean... that's a huge "maybe" with a lot of "ifs" (android versioning comes to mind etc.), but since you seem frustrated (and I've been there with Android many times), maybe this can give you some "light". And I assume that there are no other obvious answers by looking at your previous research.
I wish you good luck!
Here is an alternative solution to differentiate between any (intended or unintended) programmatic and user-initiated changes:
Create your listener for the spinner as both an OnTouchListener and OnItemSelectedListener
public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
boolean userSelect = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
userSelect = true;
return false;
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (userSelect) {
// Your selection handling code here
userSelect = false;
}
}
}
Add the listener to the spinner registering for both event types
SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
This wouldn't handle the case in which the re-selection of the same item by the user doesn't trigger the onItemSelected method (which I have not observed), but I guess that could be handled by adding some code to the onTouch method.
Anyway, the problems Amos pointed out were driving me crazy before thinking of this solution, so I thought I'd share as widely as possible. There are many threads that discuss this, but I've only seen one other solution so far that is similar to this: https://stackoverflow.com/a/25070696/4556980.
Modifying the Spinner is useful if you want to have multiple selections simultaneously in the same activity.
If you only desire the user to have a hierarchical selection, for example:
What do you want to eat?
Fruit
Apples
Bananas
Oranges
Fast Food
Burgers
Fries
Hot dogs,
then the ExpandableListView might be better for you. It allows the user to navigate a hierarchy of different groups and choose a child element. This would be similar to having several Spinners for the user to choose from - if you do not desire a simultaneous selection, that is.
I worked through several of the issues mentioned in this thread before I realized that the PopupMenu widget is what I really wanted. That was easy to implement without the hacks and workarounds needed to change the functionality of a Spinner. PopupMenu was relatively new when this thread was started in 2011, but I hope this helps someone searching for similar functionality now.

Categories

Resources