I'm writing an IM client and I need to download (from filesystem or network) and show new elements at the top of ListView (it is history of messages -- older messages are at the top, newer -- at the bottom). I implemented my own Adapter for ListView but I can't add new elements at the beginning of the list and redraw it. (notifyDataSetChanged() isn't good for me, because indexes of messages in ListView changes and android can't redraw it normally).
How do other apps do something similar?
I don't create special code for it, I am simply creating new Adapter for my ListView:
messagesListView.setAdapter(new MessagesListAdapter(this));
And redefine getView() and getCount() method in MessagesListAdapter (extends ArrayAdapter now).
My XML for ListView is
<ListView
android:id="#+id/dialog_messages_list"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:layout_marginTop="#dimen/title_height">
</ListView>
And my XML for one element (one message) is
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/dialogMessageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:background="#drawable/dialog_message_in"
/>
<TextView
android:id="#+id/dialogMessageDatetime"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""/>
</LinearLayout>
May be you need other code?
EDIT: I tried
messagesListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrayList));
(new Thread() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
arrayList.add(0, "qwer");
}
}).start();
But it also not seems good. I tried to call ((ArrayAdapter<String>)messagesListView.getAdapter()).notifyDataSetChanged(); in thread, but it makes exception.
I suggest reversing the order of the List to display the newest result first.
Run this example:
public class Example extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String[] array = {"oldest", "older", "old", "new", "newer"};
List<String> list = new ArrayList<String>();
Collections.addAll(list, array);
Collections.reverse(list);
// When you want to add new Strings, put them at the beginning of list
list.add(0, "newest");
ListView listView = (ListView) findViewById(R.id.list);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);
}
}
You don't have to override anything in the ArrayAdapter or ListView this way.
you can programmatically add some views in the class associated with your list view. For example:
To add stuff to a layout and make new elements:
TextView tv = new TextView(getApplicationContext());
EditText edit = new EditText(getApplicationContext());
relative.addView(tv);
relative.addView(edit);
This is to manipulate an existing element in the xml layout:
final TextView tv = (TextView) v.findViewById(R.id.listItem);
tv.setText("This an item I am changing");
If you look at some of the related questions, they will give your more information on this. But you can also checkout other people's custom listviews and adapters online. This one is really nice: http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
Related
I want to implement a listView with 2 different lists which is using 2 different Items and want to display them using a same ListView.
Both lists are separated with a sectional divider
The above listView is having a custom selector imageButton in the item
below listView has dateDividers for their items
You should use
https://github.com/commonsguy/cwac-merge
in order to create and merge different adapters with the data you need. You should also insert a header view.
Here follows an example:
#BindView(R.id.lv_validation_errors)
ListView lvValidationErrors;
private List<Notification> notificationList = new ArrayList<>();
public MergeAdapter mergeAdapter = new MergeAdapter();
public ArrayAdapter errorAdapter;
private ArrayList<String> errorList = new ArrayList<>();
public ArrayAdapter alertAdapter;
private ArrayList<String> alertList = new ArrayList<>();
First I set the ListView (using ButterKnife, which does the findViewById stuff) then
errorAdapter = new ArrayAdapter(this, R.layout.adapter_error, R.id.text1, errorList);
mergeAdapter.addView(header("Erros"), false);
mergeAdapter.addAdapter(errorAdapter);
I created the adapters I want to mmerge and added the view for the section header (dont forget to set a value to your list to prevent it from being null) then
alertAdapter = new ArrayAdapter(this, R.layout.adapter_warning, R.id.text1, alertList);
mergeAdapter.addView(header("AdvertĂȘncias"), false);
mergeAdapter.addAdapter(alertAdapter);
same as the previous step and finally:
lvValidationErrors.setAdapter(mergeAdapter);
to set the merged adapter to the ListView.
for that you have to take one list with type Object
List<Object> listItems = new ArrayList<>();
your both different list values add in above listItems .
then after bind your adapter with listItems and when you bind your item in your BindView() write like below.
if(listItems instanceof firstListItemModel)
// bind item from your first list
else if(listItems instanceof secondListItemModel)
// bind item from your second list
As i can understand, you want a vertically half divided screen with containing ListView in each of them.
And you are saying that bottom ListView is completely based on upper ListView.
bottom ListView should load according to Upper ListView selection.
if Yes, then you can use weight to show bothListViews(upper and bottom) on layout like this :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ll_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<ListView
android:id="#+id/list_view1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<ListView
android:id="#+id/list_view2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Then you can take set Adapter on list_view2 on list_view1 item click :
ListView upperListView=(ListView) findViewById(R.id.list_view1);
ListView bottomListView=(ListView) findViewById(R.id.list_view2);
upperListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
// You can put your logic here to fetch data according to upper ListView item clicked position. I am taking temp. data for now.
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, arrayList//your desired list here);
((ListView) bottomListView.setAdapter(adapter);
}
});
That's it!!
I tried a few different layouts to get deeper in possibilities and variances.
I started with an array to display the items in an listview that worked fine.
Now I wanted to display items that I got out of a database via JSON.
I get the following error: You must supply a resource ID for a TextView
I used the same XML-file, that worked before, here my all_products.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="#+id/mobile_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#color/colorPrimaryDark"
android:dividerHeight="1px">
</ListView>
</LinearLayout>
In my java class I used the code that I used before for the array adapter, I changed only the parameter which should be displayed. Here part of my AllProductsActivity.java:
private void processValue(ArrayList<String> result) {
{
ArrayAdapter adapter = new ArrayAdapter<String>(AllProductsActivity.this, R.layout.all_products, result);
ListView listView = (ListView) findViewById(R.id.list);
listView.setAdapter(adapter);
}
}
result comes from my asynctask. here a snippet as well:
JSONArray jsonArray = new JSONArray(result.toString())
ArrayList<String> listdata = new ArrayList<String>();
for(int n = 0; n < jsonArray.length(); n++)
{
JSONObject object = jsonArray.getJSONObject(n);
listdata.add(object.optString("nr"));
}
return listdata;
protected void onPostExecute( ArrayList<String> result) {
pdLoading.dismiss();
processValue(result);
}
Why I get the error? And perhaps what about using only a Textview. As I was searching for the toppic, I found different threats where people using Textviews instead of Listview.
EDIT: So it works I added another xml file, mytextview.xml
<TextView android:id="#+id/mytext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#color/colorPrimaryDark"
android:dividerHeight="1px"
xmlns:android="http://schemas.android.com/apk/res/android">
and changed the adapterstatement to the following:
ArrayAdapter adapter = new ArrayAdapter<String>(AllProductsActivity.this,
R.layout.mytextview,R.id.mytext , result);
ListView listView = (ListView) findViewById(R.id.mobile_list);
listView.setAdapter(adapter);
when you use custom layout R.layout.all_products then Adapter don't know about the view to set the data from data list
so simply you need to tell the adapter , the ID of your text view to set data on .
ArrayAdapter adapter = new ArrayAdapter<String>
(AllProductsActivity.this,
R.layout.all_products,R.id.your_text_view_id, result);
// ^^^^^^^^^ pass the text view ID
ArrayAdapter (Context context,
int resource,
int textViewResourceId,
T[] objects)
or
If you just want to display your data without any custom layout then can use in build android resource layout as
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,result);
in Pavneet's first solution take care that:
resource: is the resource containing a textView not your xml file that contain the list itself (not R.layout.all_products)
textViewResourceId: the ID of the textView contained in the resource
When instantiating your ArrayAdapter, the id you provide must be from a layout containing only a TextView. That is what the error You must supply a resource ID for a TextView means
I have an activity that uses setContentView to initiate a listview, and then I have an If-Else condition. In the If part of the statement I use a simpleAdapter to place a new layout in the listview and other data from a cursor. In the Else part, I just want to put a sentence in a label to provide some information. How can I do that? I tried parameters and textview but they didn't work. I tried to put another setcontentView but it can't work either.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
doMySearch() is a cursor that extracts data from database based on a keyword input by user.
public void onPostExecute() {
SimpleCursorAdapter adapter;
Int count = doMysearch().getCount;
If (count >= 1){
adapter=new SimpleCursorAdapter(this, R.layout.activity_results,
doMySearch(), new String[] {
DB.COL_NAME,
DB.COL_CITY },
new int[] { R.id.lblName, R.id.lblCity },
0);
setListAdapter(adapter);
}
Else {
}
I just want a sentence that says "No data found" for the Else part. Thanks.
It is not clear for me what you are asking but here we go.
There is empty view setting in ListView for empty lists, you have to add another empty view in your current layout. Then you will set the empty view layout in code like below:
//add to your layout
<LinearLayout
android:id="#+id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="There is no item" >
</TextView>
</LinearLayout>
//set empty view
ListView listView = (ListView) container.findViewById(R.id.list);
LinearLayout emptyView = (LinearLayout) container.findViewById(R.id.empty);
listView.setEmptyView(emptyView);
As far as I understand the label doesn't show up because of the listview, right?`
EDIT:
Okay I think I get it now.
To keep it simple you could hide your ListView with myList.setVisibility(View.GONE);
Then make a premade Label visible with myLabel.setVisibility(View.VISIBLE) that already holds your error-string.
The harder way you could make a StringArray, like this in (for example) the strings.xml
<string-array name="error">
<item>No results found</item>
</string-array>
Then, in your else-block you can do somethink like that:
ListView lv = (ListView) findViewById(R.id.myList);
String[] content = getResources().getStringArray(R.array.error);
ArrayAdapter<String> typeList = new ArrayAdapter<String>(this, R.layout.simple_list_item_custom, content);
lv.setAdapter(typeList);
I am fairly new to Android programming and trying to set items in a listview upon loading the information from internal storage.
I have two global arrays that I am using: first one is a String array that has the names of the items in the list, and the second is a boolean array that keeps track of which items are crossed out. I am using a TextView in the listview.
main_activity.xml:
<ListView
android:id="#+id/listViewMyList"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
rowlayout.xml:
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:text="#+id/label" />
I have created an onClickListener() which successfully updates the state of each list item:
public class MainActivity extends Activity
{
// Initialize the list (global list values)
String[] values = new String[0]; // array of items for the list
boolean[] checkedVals = new boolean[0]; // keep track of which items are crossed-off
String localFileName = "myListData.csv";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// read the data from file if present
readListFromFile();
// find the ListView
ListView lst = (ListView) findViewById(R.id.listViewMyList);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.rowlayout, R.id.label, values);
lst.setAdapter(adapter);
// define what happens on click
lst.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view,int position, long id)
{
// read crossed status and set text flags for strikethrough
if (checkedVals[position])
{
TextView text1 = (TextView)view.findViewById(R.id.label);
text1.setPaintFlags(text1.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
text1.setTextColor(0xff000000);
checkedVals[position] = false;
}
else
{
TextView text1 = (TextView)view.findViewById(R.id.label);
text1.setPaintFlags(text1.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
text1.setTextColor(0xff888888);
checkedVals[position] = true;
}
// save the data in a file
saveListToFile();
}
});
}
So this code works fine for crossing out and un-crossing out the items. I don't know how can I cross-out some of the items (determined by the checkedVals boolean array) without clicking or any activity when I load the list.
Thanks in advance.
You need to create a custom Adapter by extending ArrayAdapter and overriding getView().
The getView() method loads every row's layout, this is where you should check if the row is in your checkedVals array and draw with the appropriate flags. This Google Talk by an Android lead programmer, Romain Guy, provide a wealth of information about best practices on how to do this.
I am playing around with ListView in android and I have a simple class that extends ListActivity.
Whenever I change the name of the ListView in xml file from android:list to something else my program crashes.
Here is my ListActivity code:
private NewExpenseScreenModel mDbAdabter;
String[] items = { "Category", "Month", "Year"};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.statistics);
Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter adapter = new ArrayAdapter(
this,
android.R.layout.simple_spinner_item,
items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
mDbAdabter = new NewExpenseScreenModel(this);
populateFields();
}
private void populateFields()
{
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.spending_row, mDbAdabter.getAllCategories());
setListAdapter(adapter);
}
and here is my xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="10dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Spinner
android:id="#+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:prompt="#string/spinner_title"
/>
<ListView android:id="#+id/android:list" android:layout_height="wrap_content" android:layout_width="match_parent"></ListView>
</LinearLayout>
any idea what is going on..
I want to implement a onItemClickedListener later on so I want to be able to find the ListView by id.
I dont really understand why i cannot just change the id
Additional Info:
I also have another class that uses a different xml file with a list id="android:list" but I never need to find that listView by id so its not a problem.
The other class uses the same xml file to describe layout of each row.
Update:
I managed to access the id by using findViewById(android.R.id.list); instead of findViewById(R.id.list); but i still dont understand why it wont let me rename the list
The list view id should be #android:id/list.
You use + in id when you define a new id. Since you are using ListView id of the listview should have list and list id is already defined internally, so no need to add +.
Edited
The above is mandatory when you extend the class from ListActivity. See #Mejonzhan answer why so.
Now, if you want your own id or when you want to have many List Views, then do not extend ListActivity instead extend Activity and handle the ListViews as below:
Change id as follows:
<ListView android:id="#+id/myListView"
And in the code, access the list view as follows:
ListView myListView = (ListView) findViewById(R.id.myListView)
Try this ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item_1,
items);, in place of `ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item,
items); `
ListActivity has a default layout that consists of a single, full-screen list in the center of the screen. However, if you desire, you can customize the screen layout by setting your own view layout with setContentView() in onCreate(). To do this, your own view MUST contain a ListView object with the id "#android:id/list" (or list if it's in code)
ListActivity extend Activity, you can search the source code ,find the ListActivity, and find the function:
public void onContentChanged() {
239 super.onContentChanged();
240 View emptyView = findViewById(com.android.internal.R.id.empty);
241 mList = (ListView)findViewById(com.android.internal.R.id.list);
242 if (mList == null) {
243 throw new RuntimeException(
244 "Your content must have a ListView whose id attribute is " +
245 "'android.R.id.list'");
246 }
247 if (emptyView != null) {
248 mList.setEmptyView(emptyView);
249 }
250 mList.setOnItemClickListener(mOnClickListener);
251 if (mFinishedStart) {
252 setListAdapter(mAdapter);
253 }
254 mHandler.post(mRequestFocus);
255 mFinishedStart = true;
256 }
in line 241 mList = (ListView)findViewById(com.android.internal.R.id.list);
so you can not change the list name