I am new to Android programming and have a basic question.
I have created a ListActivity where each row in its ListView has a TextView and a RatingBar. I am able to display values in the TextView and RatingBar by reading them from a pre-populated database; I am doing this via a custom CursorAdapter and it's bindView() method, i.e.
public class MyCursorAdapter extends CursorAdapter {
...
public void bindView(View view, Context context, final Cursor cursor) {
TextView name = (TextView)view.findViewById(R.id.name);
name.setText(cursor.getString(cursor.getColumnIndex(MyDbAdapter.KEY_NAME)));
RatingBar rating = (RatingBar)view.findViewById(R.id.life_bar_rating);
rating.setRating(cursor.getFloat(cursor.getColumnIndex(MyDbAdapter.KEY_RATING_VALUE)));
}
...
}
Now, my problem is that in my ListActivity I want the user to be able to click a "Save" button so that all changes are saved to the database in one go?. How would I iterate through the adapter to get the row ID and rating values so they can be saved (or is there another better way of doing this?).
The reason why I want to do a save in one go is because I thought it would be bad practice to update the database every time the user makes a change to a single rating (even though this is easier) as this causes more wear and tear on flash memory. I may be wrong about this, but I thought that there are a limited number of reads and writes with flash memory.
Remember the Adapter's backing array? The one you used to initialize it? save that ;)
or if that doesn't work...
for(int i = 0; i < listView.getAdapter().getCount(); i++)
SaveThing(listView.getAdapter().getItem(i));
if you need SQLite examples of saving... tomorrow.
edit for same of keeping comments clean:
as far as that object thing goes - you're free to cast it to anything that's in the listview. the data type, not view type.
so for example, an arrayAdapter that works with strings still returns Objects. but you will most certainly cast them to Strings because you know that's what you put in. it's awkward but thats how it is.
Related
I have an Activity, which has a RecyclerView and save Button. Each item (football match) of the RecyclerView contains some text (name of teams) and EditText, where user enters data (actually enters a score of the match). My goal is to save those scores to the list in the Activity, when user clicked on save button.
I read the similar question (How can I return data from a RecyclerView adapter?), the difference is that I need to get data from editText, not from checkBox(which is easy to check by clickListeners in onBindViewHolder method in Adapter)
So I made it on my way and it's works. I implemented this method, which actually get particular item from LinearLayoutManager and then get data from EditText .
private List<Bet> getNewUserBets() {
View betView;
Bet betItem;
EditText userBet1;
EditText userBet2;
int numberOfMatches = rvMatchesAdapter.getItemCount();
List<Bet> bets = new ArrayList<>();
for (int i = 0; i < numberOfMatches; i++)
{
betItem = rvMatchesAdapter.getItem(i);
betView = linearLayoutManager.findViewByPosition(i);
userScore1 = (EditText) betView.findViewById(R.id.result1);
userScore2 = (EditText) betView.findViewById(R.id.result2);
//here i checked whether the editText is empty and other stuff
bets.add(betItem);
}
return bets;
}
But I was wondering if I made this on a good way. In my opinion there are two approach:
Separate logic. Implement a method in Adapter (probably in onBindViewHolder), which get data from EditText(entered by user). So logic about views should be in Adapter. And in Activity just call this method (like getAdatper().), when user presses a save button. So Activity doesn't know anything about views (what's is going on in Adapter).
Like I did. Make whole logic in Activity, which in my opinion is worse than approach described at number 1.
So in which way should I solve this task? And if 1 method is better, how should I get data from EditText directly from Adapter. Or maybe there is another approach(solution) to make this done. I would really appreciate any help.
I found it easier to use TextWatcher in ViewHolder method in Adapter, which get data from EditText, at the moment when those EditText's are changed. Add this data to ArrayList in Adapter of Bets and then use this ArrayList (which is public) in Activity
I'm new to Android programming. I can get simple ListViews to work in which I use an ArrayList of strings. I want to take a step upward in complexity and have my ListView be composed of simple Java objects like this:
class myItem {
public String name;
public Integer price;
public Integer weight;
}
The ListView only needs to display the name in the above object. It doesn't have to have multiple icons, or multiple clickable actions for each item.
But I don't know where to start. All of the examples I see on the WWW are much more complicated and require me to learn things that have nothing to do with this (like a database). Or each ListView items is displayed with multiple views like text and images and icons etc. and each is clickable for a different action. I don't need any of that, and I'm getting bogged down reading about unneeded features in order to get those examples to work.
Does anyone have an example of a simple ListView that contains simple Java objects (like the one above)?
But I don't know where to start.
Implement a public String toString() method on myItem. Then create an ArrayList<myItem> and use that instead of ArrayList<String>, populate that list with suitable myItem instances, and put the list into an ArrayAdapter<myItem> instead of an ArrayAdapter<String>. No other changes should be required -- whatever layout you are using for an ArrayAdapter<String> will work with your ArrayAdapter<myItem>.
In case you are new to Java, toString() is the standard Java method for returning a String representation of an object. The default behavior of ArrayAdapter is to call toString() on the object for the given list position and use that to fill in the row.
I have a ListView where I want each item to have an ID number attached to it (not the same as the position number). I was hoping this could be done by setting a tag to each View item in the ListView using setTag() when these Views are being created.
Right now I'm creating the ListView like this:
final ListView listview = (ListView) findViewById(R.id.listView1);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, names);
listview.setAdapter(adapter);
The names variable in the ArrayAdapter parameters above is an ArrayList, and each string value in this list also has a unique ID that I want to link to this string somehow.
Is there any way I can get access to and modify each of the Views with a tag? One idea was to create my own extended class of ArrayAdapter and override the getView() method, but I don't really understand how it works and how I would go about doing this.
Or is there a better way to link IDs with each string like this than adding tags like I'm trying to do?
Create a ViewBinder and set the tags as the ListView is being populated with whatever you need. You can check all properties of the view to determine what tag goes where, so this should be what you're looking for.
myAdapter.setViewBinder(new MyViewBinder());
public class MyViewBinder implements ViewBinder {
#Override
public boolean setViewValue(View view, Object data, String text){
//Since it iterates through all the views of the item, change accordingly
if(view instanceof TextView){
((TextView)view).setTag("whatever you want");
}
}
}
I just used this exact same answer on another question (albeit slightly different) yesterday.
about getView , it works by using a method of recycling views. i will try to explain it in a simple way.
suppose you have tons of items that can be viewed . you don't want to really create tons of views too , since that would take a lot of memory . google thought of it and provide you the means to update only the views that need to be shown at any specific time.
so , if there is an empty space on the listview , it will be filled with a new view . if the user scrolls , the view that becomes hidden is recycled and given back to you on the getView , to be updated with the data of the one that is shown instead .
for example , if you scroll down , the upper view becomes hidden for the end user , but in fact it becomes the exact same view that is on the bottom .
in order to understand how to make the listview have the best performance and see in practice how and why it works as i've talked about , watch this video:
http://www.youtube.com/watch?v=wDBM6wVEO70
as for tags , i think you want to do something else , since the data itself (usually some sort of collection, like an arrayList) already knows where to update , because you get the position via the getView . if you want a specific view to update , you might be able to do so by using a hashmap that keeps upadting , which its key is the position in the collection , and the value is the associated view . on each time you go to getView , you need to remove the entry that belong to the view (if exists) and assign the new position with the view that you got/created .
Thanks for the answers. thisMayhem's answer would probably have been easier in the end, but on my quest to learn more I ended up making my own adapter according to this tutorial. I pass down the names and the IDs into the adapter and set the names as the text of the TextViews and the IDs as the tags.
I would rather go with the solution discussed in this thread. It is always the easiest to have all related data in same place and in this case you just create a class to hold all the information you will need for every item.
What I'm trying to accomplish here is to set the text in a given row as read if the user clicks on it, now I was able to do that by using the onclick method, the problem with it is that it goes away when an intent is fired or the user exits the app. I want the Text to be set up as read permanently. here is my piece of code if anybody can help I would greatly appreciate it.
Thank you in advance:
public void onListItemClick(ListView parent, View v, int position, long id) {
LinearLayout ll = (LinearLayout) v;
TextView clickedTextView = (TextView) ll.getChildAt(1);
clickedTextView.setTextColor(Color.GREEN);
StringTokenizer st = new StringTokenizer(strings[position],"<#>");
for(int i=0;i<3;i++)
{
coupon = st.nextToken("<#>");
}
sharable=st.nextToken();
Intent i = new Intent(getApplicationContext(), CouponImage.class);
i.putExtra("The coupon", coupon);
i.putExtra("Sharable", sharable);
startActivity(i);
}
You'll have to store the read status for each text item, either in a SQLite Database, or in a flat file in Internal Storage.
This is because whenever you scroll the list, leave the app and come back, etc. you end up with the ListAdapter re-rendering your row view, and your views in a list are never 1:1 with underlying data due to view recycling. If you want your change to be "sticky" you need to think about modifying the underlying data for the ListAdapter in a way it the adapter knows how to render correctly, not just changing this particular instance of a row -- you're marking the item read, not just setting one instance of a rendered view of the item as read. You can think of it as an MVC thing if it helps.
That is, the real change here should be to your Adapter's getView method, with a change to its data source and then possibly a call to notifyDataSetChanged.
Fredley's answer about SQLite or storage may be overkill if that data doesn't need to persist beyond this one session in the activity, or if the data you're working with is also transient (e.g. network data that changes often that's temporarily loaded into an ArrayAdapter), and in any case it's a bit misleading because just dumping data to disk doesn't solve the fundamental issue with your conflation of views of data with your models.
First off I'm really new to android (< 4 days).
I'm trying to wrap my head around how database data is linked to Views and widgets.
I've done a few tutorials and I've noticed that Adapters are used to link AdapterViews which are (as I understand it) Views which contain a bunch of identical subviews (eg lists, gallery, etc). So the Adapter is responsible for creating those subviews and populating the data for each one (correct me if I'm wrong).
Now let's say I have a list view which lists Hotels for example. Each row in the list has the Hotel's name and a basic rating (eg 5 star). Now when you tap on a hotel in the list a new activity shows up showing the details of that particular hotel. All the data is in a database. I understand that you have an adapter manage the data<->view link for the list, but what's the best way to then manage data for the hotel details view (which is not a list but just a couple of text views and an image for example)?
Is it best to just pass the ID in the intent and then have the details activity fetch the data from the DB on its own (in this case do I store the query in the details activity?)? Or do you get all the fields you need and put those in the intent directly? Do you need an adapter for a view which doesn't actually generate lots of similar subviews?
I guess a summary of my question is what do you use instead of an adapter when you're not dealing with adapterviews but just simple straightforward single views.
Thanks for any help you can provide.
Is it best to just pass the ID in the intent and then have the details activity fetch the data from the DB
on its own (in this case do I store the query in the details activity?)?
That is what I would recommend.
Do you need an adapter for a view which doesn't actually generate lots of similar subviews?
No. You only use an Adapter with an AdapterView (ListView, Spinner, etc.)
I guess a summary of my question is what do you use instead of an adapter when you're not dealing with
adapterviews but just simple straightforward single views.
Just the Cursor from the database. Get the fields from the (one) row in the Cursor, put them in the EditText widgets, and when the user makes changes, update the row.
#CommonsWare is bang on. As a code example, I was able to DRY things up by creating a helper method to dynamically set the TextViews a little cleaner.
In my onCreate() I have a number of the following lines:
bindTextView(hotel, "uid"); // `hotel` is the Hotel object with attributes.
And then I define bindTextView() below as follows:
protected void bindTextView( Hotel hotel, String attribute ) {
try {
// Get field for object dynamically.
Field field = hotel.getClass().getField(attribute);
// Invoke field "getter" method to get value.
String value = field.get(hotel).toString();
// Get resource id dynamically.
int resourceId = R.id.class.getField(attribute).getInt(null);
// Get element with resource id.
TextView element = (TextView) this.findViewById( resourceId );
// Finally, set the element's text value.
element.setText( value );
} catch (IllegalAccessException e) { e.printStackTrace();
} catch (NoSuchFieldException e) { e.printStackTrace(); }
}
I actually moved this helper method to a base Activity class since it's shared amongst a number of different activities.
I hope that helps some people keep their activities clean.
JP