Is it possible to inflate Textview from #android:id/text1 ?
I don't want to create own layout, I would like just get a little modified text.
Here is my code:
First, I created variable to store data
private List<HashMap<String, String>> dataCities = new ArrayList<HashMap<String, String>>();
//Hashmap with keys and values
//id - 0
//name - default
Second, I created custom adapter in onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
Spinner citiesSpinner = (Spinner) findViewById(R.id.city_sp);
citiesAdapter = new CustomArrayAdapter(this, R.layout.sherlock_spinner_item, dataCities);
citiesSpinner.setAdapter(citiesAdapter);
}
Third, I created my listener. It works but after calling notifyDataSetChanged nothing happens. Why?
#Override
public void onRequestJsonResponded(RequestType type, JSONArray array) {
//my enum type
switch (type) {
case cities:
//returns hashmap in arraylist, ArrayList<HashMap<String,String>> ...
dataCities = parseJSonArray(array);
Log.d(TAG, "End of parsing");
citiesAdapter.notifyDataSetChanged();
break;
case mark:
//...
break;
case model:
break;
}
}
Here is my custom arrayadapter
private class CustomArrayAdapter extends ArrayAdapter<HashMap<String, String>> {
public CustomArrayAdapter(Context context, int textViewResourceId, List<HashMap<String, String>> objects) {
super(context, textViewResourceId, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
View v = layoutInflater.inflate(R.layout.sherlock_spinner_item, null);
TextView tv = (TextView)v.findViewById(android.R.id.text1);
tv.setText(getItem(position).get("name"));
return v;
}
}
Could someone tell me why do I get blank spinner data? (Spinner is empty). And how I get modified text without creating new layout? I would like just to use sherlock spinner item layout. Please help.
Simple, the list in your adapter is not the same as the list that you retrieved:
//In here you update your activity's list to the returned values
dataCities = parseJSonArray(array);
Log.d(TAG, "End of parsing");
//But the adapter is using the original value of dataCities (new ArrayList<HashMap<String, String>>() )
citiesAdapter.notifyDataSetChanged();
Since your adapter depends on ArrayAdapter, the easiest solution might be to just create a new adapter when the data is received.
Related
I need to set only first name field value in the listview.
Am querying data and getting all values and showing them in the listview. need to show only first name
The code,
import c...l.Database.ItemCRUDOperations;
import c...l.Model.Item;
List<Item> items;
ListView listView = (ListView) findViewById(R.id.listView);
itemCrud = new ItemCRUDOperations(this);
itemCrud.open();
items = itemCrud.getAllItems(); // returns id, first name, last name ...
// i need to only show first name in list view, currently it is showing all fields in listview
//I tried items.getFirstName() as I have getters and setters to it but not getting the value.
itemCrud.close();
ArrayAdapter<Item> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, items);
listView.setAdapter(adapter);
Try this code:
...
List<String> firstNames = new ArrayList<>();
for (Item item : items) {
firstNames.add(item.getFirstName);
}
ArrayAdapter<Item> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, firstNames);
listView.setAdapter(adapter);
Or you can simple override toString() method in your Item class:
#Override
public String toString() {
return getFirstName();
}
Use this to get a list with only firstName:
List<String> firstNamelist = new ArrayList<>();
for(Item item : items){
firstnameList.add(item.getFirstName());
}
then pass firstNameList to adapter:
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, firstNameList);
Hope it helps.
The easiest option already suggested in another answer is to create list of Strings and pass it to the adapter instead of list of Items.
Another, more general, way is to implement own list adapter, overriding method getView in it, like this:
class ItemListAdapter extends ArrayAdapter<Item> {
private static class ViewHolder {
TextView firstName;
TextView lastName;
// More fields ...
}
ItemListAdapter(Context context, List<Item> items) {
super(context, 0, items);
}
#Override
#NonNull
public View getView(final int position, View view, #NonNull ViewGroup parent) {
final Item item = getItem(position);
final ViewHolder viewHolder;
if (view == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(R.layout.item_layout, parent, false);
viewHolder.firstName = (TextView) view.findViewById(R.id.first_name_text_view);
view.setTag(viewHolder);
} else {
viewHolder = (ItemListAdapter.ViewHolder) view.getTag();
}
viewHolder.firstName.setText(item.getFirstName());
return view;
}
}
With this approach you will be able to add more information to the list item in future if you need.
I populate a ListView with a SimpleAdapter. Immediately after doing so, I have a function that tries to loop over the child views to set their background color programmatically. The problem is that the ListView may not have any children immediately after listView.setAdapter() is called. I don't know which callback to stick my function on.
// A HashMap to store the values for the ListView rows
List<HashMap<String, String>> aList = new ArrayList<HashMap<String, String>>();
for (int i = 0; i < steps.length; i++) {
HashMap<String, String> hm = new HashMap<String, String>();
hm.put("txt", steps[i]);
hm.put("icon", Integer.toString(step_images[i]));
aList.add(hm);
}
// Keys used in Hashmap
String[] from = {"icon", "txt"};
// Ids of views in layout
int[] to = {R.id.icon, R.id.txt};
// Instantiating an adapter to store each items
// R.layout.listview_steps defines the layout of each item
SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), aList, R.layout.listview_steps, from, to);
// Setting the adapter to the listView
listView.setAdapter(adapter);
//fixme after the listView adapter is set, the listView may not have any children immediately.
//I'm not sure which callback to use to properly call refreshStepsListView().. So I'm hacking it and I just wait 250ms
(new Handler())
.postDelayed(
new Runnable() {
public void run() {
refreshStepsListView();
}
}, 250);
Don't need a callback. Do it live
public class ColoredAdapter extends SimpleAdapter {
ColoredAdapter(...) {
super(context, list, reslayout, from, to);
}
getView(...) {
// set colors here
}
Or you could create a list of a Step class rather than a Hashmap, plus use an ArrayAdapter<Step>
From what I understand. You just want to change the background of each item inside the listview.
First, define you class item for Adapter.
public class Item {
private String mTitle;
private String mBackgroundColor;
public void setTittle(String title){
this.mTitle = title;
}
public void setBackgroundColor(int color){
this.mBackgroundColor= color;
}
public void getTitle (){
return this.mTitle;
}
public void getBackgroundColor(){
return this.mBackgroundColor;
}
}
Then use ArrayAdapter instead of SimpleAdapter
See this sample Here
Make sure inside getView() of your ArrayAdapter, you have set item background color from item.getBackgroundColor()
itemView.setBackgroundResource(item.getBackgroundColor());
When you want to change the background color, you can just set new color to which item item.setBackgroundColor(newColor) and call adapter.notifyDataSetChanged() to refresh the adapter.
Do your background coloring work inside your adapter. You can simply extend SimpleAdapter and get access to the views as they are created. In the adapter is where you are expected to do any view manipulation logic.
Iterating over the children is not advised and will result in bugs, unmaintainable code, and bad performance.
public class ColorAdapter extends SimpleAdapter {
public ColorAdapter(Context context,
List<? extends Map<String, ?>> data,
int resource,
String[] from,
int[] to) {
super(context, data, resource, from, to);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
view.setBackgroundColor(Color.RED);
return view;
}
}
I am aware that there are plenty of similar questions, but they all have in common, that their solutions dont work with my list :(
I am trying to get my userList refreshing itself via the custom ArrayAdapter, when the database-contents are changed. In my case when i reset();
here my snippets (partial code):
MainActivity.java
public class MainActivity extends ListActivity {
private MyUserListAdapter myUserlistAdapter;
public ArrayList<User> myUserList = new ArrayList<User>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//shortened the list-filling. but it works properly!
User user = db.readUser(int);
myUserList.add(user);
myUserlistAdapter = new MyUserListAdapter(this, R.layout.row_main, myUserList);
setListAdapter(myUserlistAdapter);
//now when reset-button is hit, the listview should refresh itself
bReset.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//this is what is posted in most questions, but it does nothing for me
//myUserlistAdapter.notifyDataSetChanged();
//getListView().invalidateViews();
} });
and here myUserListAdapter.java:
public class MyUserListAdapter extends ArrayAdapter<User>{
private Context context;
private ArrayList<User> userList;
public MyUserListAdapter(Context context,
int textViewResourceId, ArrayList<User> userList) {
super(context, textViewResourceId, userList);
this.context = context;
this.userList = userList;
}
public View getView(int position, View v, ViewGroup parent) {
if (v == null) {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.row_main, null);
}
User user = getItem(position);
TextView tvUser = (TextView) v.findViewById(R.id.tvUser);
ImageView ivVoted = (ImageView) v.findViewById(R.id.ivVoted);
tvUser.setText(user.getName());
//abfrage ob hasVoted() = true muss noch eingebaut werden.
if (user.getVoted().equals("1"))
ivVoted.setImageResource(R.drawable.redcheck);
else
ivVoted.setImageResource(R.drawable.greencheck);
return v;
}
}
User.java is just a simple object-class. think its not the troublemaker here!
any help is appreciated!!! thx :-)
I am trying to get my userList refreshing itself via the custom
ArrayAdapter, when the database-contents are changed.
Since you are using ArrayAdapter and not CursorAdapter when you update data in database your adapter won't refresh itself. Whenever you want to update ListView you need to provide new datasource for Adapter.
One possible solution is to create setter in adapter subclass that will change datasource of adapter.
Pseudo code:
/* setter in adapter subclass */
public void changeDataSource(ArrayList<User> newUserList) {
this.userList = newUserList;
}
Then call adapter.notifyDataSetChanged(); for ListView update.
Try this:
arrayAdapter.clear()
for(Object o : objects)
arrayAdapter.add(o)
clear() and add() call to notifyDataSetChanged() itself.
I'm new to android, i've spent the last 2 days trying previous examples and online solutions but I just can't seem to get my head around it :(
I'm able to display a list view, parse some json from online and store a book title, book description and book ID and display this data in the listview. I want to be able to put a 'download' button in each row for the ListView, each button will correspond to its book ID on Click() and the action listener will download the book by appending that ID to a url.
e.g www.books.com/download_book1 or /download_book2....
Here is my code. Catalogue.java class
public class Catalogue extends ListActivity {
private JSONObject json;
private ListView lv;
private ArrayList<Integer> alKey = new ArrayList<Integer>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //icicle
setContentView(R.layout.shelflist);
ArrayList<HashMap<String, String>> mylist = new ArrayList<HashMap<String, String>>();
....
try{
JSONArray entries = json.getJSONArray("entries");
for(int i=0;i<entries.length();i++){
HashMap<String, String> map = new HashMap<String, String>();
JSONObject e = entries.getJSONObject(i);
alKey.add(e.getInt("key"));
map.put("id", String.valueOf(i));
map.put("title", "Title:" + e.getString("title"));
map.put("description", "Description: " + e.getString("description"));
mylist.add(map);
}
}catch(JSONException e) {
Log.e("log_tag", "Error parsing data "+e.toString());
}
ListAdapter adapter = new SimpleAdapter(this, mylist , R.layout.shelfrow,
new String[] { "title", "description" },
new int[] { R.id.item_title, R.id.item_subtitle });
setListAdapter(adapter);
lv = getListView();
lv.setTextFilterEnabled(true);
.....
This is as far as I get. I don't know how to add 1 button per row in the List and assign an action listener to each button.
I also have a shelfrow.xml file (textView, textView for item_title and item_subtitle) and a shelflist.xml file (ListView).
I have a shelf.xml file with
Basically you need to learn the concept of ListAdapter.
Here's the short story: picture an object that holds the data to be displayed inside a list, along with the way to display each line individually. That's your ListAdapter. Now take each individual line: it's a book with a title and an OnClickListener. It's rendered inside a View with a TextView (for the title) and a Button (for the OnClickListener). All you need to do is give one View to the adapter that will be used for each line, and a List of the books you want to be inside the list.
Here's some sample code. I hope it clears things up a bit
private class MyItemModel{ //that's our book
String title; // the book's title
String description;
long id;
OnClickListener listener = new OnClickListener(){ // the book's action
#Override
public void onClick(View v) {
// the default action for all lines
doSomethingWithTheBookTitleOrUniqueId(this);
}
};
}
private class MyListAdapter extends BaseAdapter{
View renderer;
List<MyItemModel> items;
// call this one and pass it layoutInflater.inflate(R.layout.my_list_item)
public MyListAdapter(View renderer) {
this.renderer = renderer;
}
// whenever you need to set the list of items just use this method.
// call it when you have the data ready and want to display it
public void setModel(List<MyItemModel> items){
this.items = items;
notifyDataSetChanged();
}
#Override
public int getCount() {
return items!=null?items.size():0;
}
#Override
public Object getItem(int position) {
return items!=null?items.get(position):null;
}
#Override
public long getItemId(int position) {
return items!=null?items.get(position).id:-1;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
convertView = renderer;
}
MyItemModel item = items.get(position);
// replace those R.ids by the ones inside your custom list_item layout.
TextView label = (TextView)convertView.findViewById(R.id.item_title);
label.setText(item.label);
Button button = (Button)convertView.findViewById(R.id.item_button);
button.setOnClickListener(item.listener);
return convertView;
}
}
In order to pass the List, instead of putting the data inside your list of hashmaps you can do this for instance (be careful, I also updated the MyItemModel and MyListAdapter to your need, added the id and description properties):
List<MyItemModel> myListModel = new ArrayList<MyItemModel>();
try{
JSONArray entries = json.getJSONArray("entries");
for(int i=0;i<entries.length();i++){
MyItemModel item = new MyItemModel();
JSONObject e = entries.getJSONObject(i);
alKey.add(e.getInt("key"));
item.id = i;
item.title = e.getString("title");
item.description = e.getString("description");
// you can change the button action at this point:
// item.onClickListener = new OnClickListener(){...};
myListModel.add(item);
}
}catch(JSONException e) {
Log.e("log_tag", "Error parsing data "+e.toString());
}
ListAdapter adapter = new MyListAdapter(getLayoutInflater().inflate(R.layout.shelfrow, this));
adapter.setModel(myListModel);
setListAdapter(adapter);
lv = getListView();
lv.setTextFilterEnabled(true);
You can create your own class extending ArrayAdapter that will hold your list and set onClickListener to the Button in each row.
But in getView method of your ArrayAdapter you have to create a new view every time.
for example - row layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="110dp"
android:background="#FFF"
android:layout_width="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:background="#FFF"
android:orientation="vertical"
android:padding="2dp"
android:layout_height="110dp">
<TextView android:id="#+id/list_item_title"
android:background="#FFF"
android:layout_width="fill_parent"
android:layout_height="40dp"/>
<Button android:id="#+id/download_button"
android:gravity="center"
android:text="Download"
android:layout_height="35dp"/>
</LinearLayout>
</RelativeLayout>
and getView method in ArrayAdapter
private List<Map<String, String>> jsonMapList;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.list_item, null);
// here you set textview values (title and description)
// TextView title = (TextView) v.findViewById(R.id.list_item_title);
// title.setText('bla');
// and set OnClickListener
Button button = (Button) v.findViewById(R.id.download_button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
downloadFile(getUrl(position));
}
});
return v;
}
// method that downloads file
private void downloadFile(String url) {}
// get url from your list by index
private String getUrl(int index) {
return jsonMapList.get(index).get("url");
}
Usage of Map is unnecessary, you could use any object you prefer.
In activity class
CustomAdapter listAdapter = new CustomAdapter(this, android.R.layout.simple_list_item_single_choice, jsonMapList);
setListAdapter(listAdapter);
I'd like to map an Array of "complex" data to a ListView. In a very simplified form my data model would look like something like this:
class ListPlacesValues {
String idObject;
String name;
String city;
String country;
ArrayList<String> classification;
double distance_quantity;
DistanceUnit distance_unit;
[...more stuff ...]
}
I know that I can convert my complex data into a HashList and then just use a SimpleAdapter:
SimpleAdapter mAdapter = new SimpleAdapter(
this,
hashList,
R.layout.places_listitem,
new String[] { "name", "city", "country"},
new int[] { R.id.name, R.id.city, R.id.country}
);
However, I would rather use my data model directly, but I've no idea where and how to start, so that in the end I can do something like this:
ArrayList<ListPlacesValues> values = getData();
MyAdapter mAdapter = new MyAdapter(
this,
values,
R.layout.places_listitem,
ListPlacesValues { values.name, values.city, values.country},
new int[] { R.id.name, R.id.city, R.id.country}
);
Solution: I found this Android API sample (List14), which was really helpful.
You can extend ArrayAdapter. Here's code example for you. In this example - SearchItem is some custom POJO. Basically you need to override getView() method to build your row by inflating row layout and then populating values based on List of items and current position
class SearchItemsAdapter extends ArrayAdapter<SearchItem> {
Activity context;
List<SearchItem> items;
SearchHeader header;
#SuppressWarnings("unchecked")
public SearchItemsAdapter(final Activity context,
final Map<SearchHeader, List<SearchItem>> result) {
super(context, R.layout.item, (List) ((Object[]) result.values()
.toArray())[0]);
this.context = context;
this.header = result.keySet().iterator().next();
this.items = result.get(this.header);
}
#Override
public View getView(final int position, final View convertView,
final ViewGroup parent) {
final View view = this.context.getLayoutInflater().inflate(
R.layout.item, null);
final SearchItem item = this.items.get(position);
((TextView) view.findViewById(R.id.jt)).setText(item.jt);
((TextView) view.findViewById(R.id.dp)).setText(item.dp);
((TextView) view.findViewById(R.id.cn)).setText(item.cn);
((TextView) view.findViewById(R.id.loc)).setText(item.loc.name);
final TextView body = ((TextView) view.findViewById(R.id.e));
body.setText(item.e);
body.setTag(item.src[0]);
((TextView) view.findViewById(R.id.src)).setText(item.src[1]);
return view;
}
}
There is one pitfall with the convertView in the sample you linked
if(convertView != null){ //reuse
convertView.setAnimation(null);
convertView.setAnyCustomFieldsIdontWantFilledWithData(null);
}
you want to set all animations or unused fields to null otherwise your items might have data in them or animations pending you dont want.