Adding ListView Sub Item Text in Android - java

I have created an RSS reader that lists items in a listview. I also want a date below each item, but I have no idea how to do that. I need someone's help to make the Sub Item text display the pubDate that was retrieved from the RSS feed.
This is the code I have for my class:
public class RSSReader extends Activity implements OnItemClickListener
{
public final String RSSFEEDOFCHOICE = "http://app.calvaryccm.com/mobile/android/v1/devos";
public final String tag = "RSSReader";
private RSSFeed feed = null;
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
// go get our feed!
feed = getFeed(RSSFEEDOFCHOICE);
// display UI
UpdateDisplay();
}
private RSSFeed getFeed(String urlToRssFeed)
{
try
{
// setup the url
URL url = new URL(urlToRssFeed);
// create the factory
SAXParserFactory factory = SAXParserFactory.newInstance();
// create a parser
SAXParser parser = factory.newSAXParser();
// create the reader (scanner)
XMLReader xmlreader = parser.getXMLReader();
// instantiate our handler
RSSHandler theRssHandler = new RSSHandler();
// assign our handler
xmlreader.setContentHandler(theRssHandler);
// get our data via the url class
InputSource is = new InputSource(url.openStream());
// perform the synchronous parse
xmlreader.parse(is);
// get the results - should be a fully populated RSSFeed instance, or null on error
return theRssHandler.getFeed();
}
catch (Exception ee)
{
// if we have a problem, simply return null
System.out.println(ee.getMessage());
System.out.println(ee.getStackTrace());
System.out.println(ee.getCause());
return null;
}
}
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
menu.add(Menu.NONE, 0, 0, "Refresh");
Log.i(tag,"onCreateOptionsMenu");
return true;
}
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()) {
case 0:
Log.i(tag,"Set RSS Feed");
return true;
case 1:
Log.i(tag,"Refreshing RSS Feed");
return true;
}
return false;
}
private void UpdateDisplay()
{
TextView feedtitle = (TextView) findViewById(R.id.feedtitle);
TextView feedpubdate = (TextView) findViewById(R.id.feedpubdate);
ListView itemlist = (ListView) findViewById(R.id.itemlist);
if (feed == null)
{
feedtitle.setText("No RSS Feed Available");
return;
}
if(feedtitle != null)
feedtitle.setText(feed.getTitle());
if(feedpubdate != null)
feedpubdate.setText(feed.getPubDate());
ArrayAdapter<RSSItem> adapter = new ArrayAdapter<RSSItem>(this,android.R.layout.simple_list_item_1,feed.getAllItems());
itemlist.setAdapter(adapter);
itemlist.setOnItemClickListener(this);
itemlist.setSelection(0);
}
#Override
public void onItemClick(AdapterView parent, View v, int position, long id)
{
//Log.i(tag,"item clicked! [" + feed.getItem(position).getTitle() + "]");
Intent itemintent = new Intent(this,ShowDescription.class);
Bundle b = new Bundle();
b.putString("title", feed.getItem(position).getTitle());
b.putString("description", feed.getItem(position).getDescription());
b.putString("link", feed.getItem(position).getLink());
b.putString("pubdate", feed.getItem(position).getPubDate());
itemintent.putExtra("android.intent.extra.INTENT", b);
startActivity(itemintent);
}
}
This is my XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Android RSSReader"
android:id="#+id/feedtitle"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
android:id="#+id/feedpubdate"/>
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/itemlist"
android:fastScrollEnabled="true"/>
</LinearLayout>
This is what it looks like now in Eclipse:
This is what it looks like running:
How to make the Sub Item text display the pubDate that was retrieved from the RSS feed?

The simplest solution is probably to substitute the ArrayAdapter and the android.R.layout.simple_list_item_1 that you are using with a SimpleAdapter and the android.R.layout.simple_list_item_2 predefined layout. This layout is composed by two TextViews, with an id of android.R.id.text1 (the "item") and android.R.id.text2 (the "sub item") respectively, which you will need as a reference for the SimpleAdapter to work.
By looking at the constructor for SimpleAdapter you will notice that, apart from a Context instance and the id of a layout resource, it takes three parameters that may be new to you:
a List<? extends Map<String, ?>> instance where you put the elements you want the ListView to show. Elements are in the form of a Map, i.e. something akin to a struct composed by properties in form of name/value pairs. For example, you may use "title" and "date" as keys for the title and date of each RSS item, respectively.
an array of strings, where to put the names of the keys in each map that you want to show on the ListView.
an array of integers where you need to put the ids of the parts in the list item view where you want the single elements referenced by the keys in the preceding array of strings to be shown. For example, if you want to show the title and date of a RSS item in the "item" and "sub item" views respectively, you use new String[] { "title", "date" } as the array of strings argument, and new int[] { android.R.id.text1, android.R.id.text2 } as this argument.
A rough code example, just to give you the idea:
List<Map<String, String>> data = new ArrayList<Map<String, String>>();
for (RSSItem item : feed.getAllItems()) {
Map<String, String> datum = new HashMap<String, String>(2);
datum.put("title", item.getTitle());
datum.put("date", item.getDate().toString());
data.add(datum);
}
SimpleAdapter adapter = new SimpleAdapter(this, data,
android.R.layout.simple_list_item_2,
new String[] {"title", "date"},
new int[] {android.R.id.text1,
android.R.id.text2});
itemList.setAdapter(adapter);
The documentation states that "the maps contain the data for each row, and should include all the entries specified in the from parameter", so both title and date should always be present.
Please note that this is all off the top of my head. I haven't actually tested all the code, so you may very well encounter some quirk or bug that you need to adjust or fix on your way up.

You should have an item_list.xml file like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="6dp" >
<TextView
android:id="#+id/page_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#D8000000"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="#+id/page_date"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/page_title"
android:ellipsize="marquee"
android:lines="3"
android:marqueeRepeatLimit="marquee_forever"
android:textColor="#D8000000"
android:textSize="12sp" />
</RelativeLayout>
and in your listadapter in the method getview :
View row = convertView;
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
row = inflater.inflate(R.layout.item_list, parent, false);
TextView listTitle = (TextView) row.findViewById(R.id.page_title);
listTitle.setText(title);
TextView date = (TextView) row.findViewById(R.id.page_date);
date.setText( <here put your date from RSS feed>);
return row;
this should do it!

Since you want to map multiple data items to multiple views, you can't use an ArrayAdapter. You'll have to use a SimpleAdapter instead.
Also, I noticed you're getting the RSS feed on the main UI thread. This is going to cause your application to be highly non-responsive. If you don't know what I'm talking about, take a gander at the Painless Threading article (I consider it a must read for any android developer). What you should do instead is use a Loader. They were designed for exactly you type of situation. Although they weren't introduced until API-10 you can still use them on lesser API levels via the Support Package.

create a custom listView http://saigeethamn.blogspot.com/2010/04/custom-listview-android-developer.html

Related

How do I use Android data binding to populate a list?

I can get POJOs out of my ORM (Cupboard), but I can't figure out how to write the xml to bind to such a list. All the examples are singular except for a very brief mention of using them inside a ListView or RecyclerView. So I've tried having a singular binding in an xml file that I "inflate" again for each item. That code follows:
private void listThings() {
LinearLayout gList = (LinearLayout) findViewById(R.id.thingList);
gList.removeAllViews();
SQLiteDatabase db = new CGDatabaseHandler(gList.getContext()).getReadableDatabase();
DatabaseCompartment dbc = cupboard().withDatabase(db);
QueryResultIterable<Thing> itr = null;
try {
itr = dbc.query(Thing.class).query();
for (Thing thing : itr) {
// use data binding to create a UI widget for each
LayoutInflater inflater = (LayoutInflater) getBaseContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ThingDocumentLabelBinding binding = DataBindingUtil.inflate(inflater,
R.layout.thing_document_label, gList, false);
binding.setThing(thing);
//
gList.addView(binding.getRoot());
}
} finally {
// close the cursor
if (itr != null)
itr.close();
}
}
I am aware that this uses a LinearLayout not a ListView or RecyclerView. What's the difference? I couldn't get anything at all to show up in a ListView. ListView also doesn't seem to support clearing the list with removeAllViews.
And here is the XML for the file thing_document_label.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="thing"
type="com.example.Thing"/>
</data>
<TextView android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="20dp"
android:paddingTop="20dp"
android:text="#{#string/thing_document_label(thing.gName, thing.dstName, thing.property3)}"
>
</TextView>
</layout>
What I get is a single TextView showing in my LinearLayout. I should be seeing six of them. I had them showing up before I started trying to do data-binding. I had just subclassed TextView and instantiated them in the same loop shown here. That worked fine, but I'm in the beginning stages of this app and I want to standardize on data-binding if possible.
For me, I solve the updating items of RecyclerView using #BindingAdapter:
Build base adapter:
abstract class BaseAdapter<Item, VH : ViewHolder> : Adapter<VH>() {
abstract fun setItems(items: List<Item>)
}
#BindingAdapter("items")
fun <Item> updateItems(view: RecyclerView, items: List<Item>) {
(view.adapter as? BaseAdapter<Item, in ViewHolder>)?.setItems(items)
}
Call on binding:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="#{layoutManager}"
android:adapter="#{adapter}"
items="#{viewModel.items}"/>

Android ListView - add HTML links when activity starts

I have an activity that displays a listview of html links, each stored in a TextView, that the user can add and remove links from by specifying a name and web address. Each link is stored in the listview in HTML, as below, and the user would click the word 'google' to start the web browser.
<a href='http://www.google.com'>google</a>
I have tried several ways to make the links do this, including android:autolink="web" in the XML file, with the method below being the only way that works. However, I cannot call it as the activity is initializing (from onCreate() or onStart()) as the getChildAt method returns null.
TextView wantedView = (TextView) listView.getChildAt(i);
wantedView.setText(Html.fromHtml(s));
wantedView.setClickable(true);
wantedView.setMovementMethod(LinkMovementMethod.getInstance());
However, if I set a clickable button that calls this code, then it works, although adding a new link to the list reverts the formatting.
Does anybody know why I can't access the TextView objects while initializing, and if there is another way to do this? I've posted my layout file and listview start up code below.
simplerow.xml <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rowTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:textSize="15sp"
android:visibility="visible"
android:autoLink=""
android:gravity="center">
listview.xml <ListView
android:id="#+id/listView1"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test2);
listView1 = (ListView) findViewById(R.id.listView1);
items = new String[]{ "<a href='http://www.facebook.com'>facebook</a>",
"<a href='http://www.google.com'>google</a>",
"<a href='http://www.twitter.com'>twitter</a>" };
list = new ArrayList<>();
for(int i=0; i<items.length; i++) {
list.add(items[i]);
}
adapter = new ArrayAdapter<String>(Test.this, R.layout.simplerow, list);
listView1.setAdapter(adapter);
makeLinksVisible();
}
private void makeLinksVisible() {
int i = 0;
TextView wantedView = (TextView) listView.getChildAt(i);
while (wantedView != null) {
String s = wantedView.getText().toString();
wantedView.setText(Html.fromHtml(s));
wantedView.setClickable(true);
wantedView.setMovementMethod(LinkMovementMethod.getInstance());
i++;
wantedView = (TextView) listView.getChildAt(i);
}
}
Let you create the class extending ArrayAdapter<String>, and then override the getView(...) method. There you will have to have a LayoutInflatter and a View that refer to a single row (simply named row). This is the best place to execute row.setOnClickListener(new View.OnClickListener() {/*...*/}. Do not forget to create a holder if you will have a lot of displayed at once data.
More about custom ArrayAdapters e.g. here
The solution is to get the TextView from your ArrayAdapter, not your ListView. So if you are using a ListActivity, you would do something like:
ListView listView = getListView();
ArrayAdapter arrayAdapter = listView.getAdapter();
FrameLayout frameLayout = (FrameLayout) arrayAdapter.getView(0, null, null);
TextView wantedView = (TextView) frameLayout.findViewById(R.id.your_text_view);
wantedView.setText(Html.fromHtml(s));
wantedView.setClickable(true);
wantedView.setMovementMethod(LinkMovementMethod.getInstance());
This code of course assumes the use of a FrameLayout, but you would just replace that with whatever parent container holds your TextView that you are trying to access. Then in the getView() method, you would pass in the position of your TextView (I used zero just for an example). But if you try and use this code in your onCreate() it should work.
For any future viewers, here is a solution to the problem, and a brief description of how I found it.
I tried drschultz solution and noticed no change. He commented that using a textview with a string resource works. I tried this and realised the listview needed to be passed Spanned objects.
I found a solution for creating a Spanned Listview - here - and then added an onItemClickListener, which called setMovementMethod on the Textview object provided.
I realised that the previous solution wasn't working because the adapter's 'getview' method wasn't providing a reference to the listview object, instead it was providing a copy with the same data (I think). Overriding the onItemClick method is a quick solution to get a reference to the list object, which allows you to manipulate it directly and do whatever you like to it.
I've added the code I used to test this solution below. It creates a list of two html references. The first click of a textview calls the onItemClick method, then any more clicks will take you to the web page specified.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
String s = new String("facebook");
Spanned sp = Html.fromHtml(s);
String s1 = new String("google");
Spanned sp1 = Html.fromHtml(s1);
listValues = new ArrayList<>();
listValues.add(sp);
listValues.add(sp1);
setListAdapter(new SpannedAdapter(this, listValues));
ListView l = getListView();
l.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
try {
LinearLayout linearLayout = (LinearLayout) view;
TextView wantedView = (TextView) linearLayout.findViewById(R.id.rowTextView);
wantedView.setClickable(true);
wantedView.setMovementMethod(LinkMovementMethod.getInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

Finding out the parent of clicked element in android ListView

I'm building my first app based on material from http://javatechig.com/video/json-feed-reader-in-android.
Everything goes ok so far, but I found one bug with ListView elements, which I can not manage to resolve by myself :(
I have extended list_row_layout.xml by 2 fields:
<Button
android:layout_width="wrap_content"
android:layout_height="20dp"
android:text="komcie"
android:textSize="11sp"
android:id="#+id/loadComments"
android:layout_gravity="center|bottom"
android:background="#bbb"
android:layout_marginLeft="5dp"
android:enabled="true"
android:clickable="true"
android:onClick="clickedLoadComments"
android:elegantTextHeight="true"
android:layout_toRightOf="#id/thumbImage"
android:layout_below="#+id/content"
android:padding="1px" />
<ListView
android:id="#+id/comment_list"
android:layout_toRightOf="#id/thumbImage"
android:layout_below="#+id/content"
android:paddingTop="5dp"
android:layout_marginTop="0dp"
android:paddingLeft="5dp"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:cacheColorHint="#00000000"
android:dividerHeight="1dp"
android:focusable="false"
android:listSelector="#drawable/list_selector_flatcolor"
android:visibility="invisible" />
Button.android:onClick="clickedLoadComments" function load Json with elements into ListView/comment_list. It works quite fine. But if there are more elements than could be displayed on screen (~8 elements) there is a bug. Comments from clicked element are loaded into every 8th element in a ListView.
Some code:
public void clickedLoadComments(View v)
{
try {
View parent = (View)v.getParent();
ViewHolder t = (ViewHolder) parent.getTag();
if( parent != null ) {
this.loadCommentsForLeaf(parent);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
protected void loadCommentsForLeaf( View view )
{
String tmpUrl = "http://some.url.com/Ajax/LoadComments?lid=" + this.currentLeafInUse;
JSONObject commentsJson = this.getJSONFromUrl(tmpUrl);
this.parseJsonComments(commentsJson);
if( commentsJson != null )
this.updateCommentList(view);
}
public void updateCommentList( View view) {
commentListView = (ListView) view.findViewById(R.id.comment_list);
commentListView.setVisibility(View.VISIBLE);
CommentListAdapter cla = new CommentListAdapter(this, this.commentList.get(this.currentLeafInUse));
commentListView.setAdapter(cla);
// Set list height.
ViewGroup.LayoutParams params = commentListView.getLayoutParams();
params.height = setListViewHeightBasedOnItems(commentListView) + 20;
commentListView.setLayoutParams(params);
commentListView.requestLayout();
}
CustomListAdapter.java code is mostly the same as the one in tutorial.
I would really appreciate help as I have spent many hours figuring it out with not success :(
This is just a guess. You might post your Adapter code and your parseJsonComments also if this does not work.
The Cause:
The problem you are describing might be caused due to the recycling and the reusage of Views. Take a look at this image from http://android.amberfog.com
As you can see the 1. items is reused and becomes the 8. item after scrolling.
Let's assume that Item 1 has an OnClickListener which updates a Text of the item.
For example we set the text to "clicked" after the OnClickListener is triggered.
Because item 1 is reused to create item 8, item 8 will also display the text "clicked".
The Solution:
The usual way is to save all states/content in a List(or whatever) and update everything in the getView call. So if you want to update text:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
...
holder.textView.setText(jsonTexts[position]);
...
return convertView;
}
And if you want to update an item just update the List in your Adapter which holds the content/JsonObjects(etc.) and call notifyDataSetChanged.
public void updateCommentList(JSONObject commentsJson, int position) {
// does not exist you might create something
//like that in your Adapter class
commentListAdapter.updateItem(commentsJson,position);
commentListAdapter.notifyDataSetChanged();
}
After i populate the listview i call this method:
private void registerClickCallback() {
ListView list = (ListView) findViewById(R.id.lv);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View viewClicked,
int position, long id) {
String xx = position+ ":" + id;
//then you can do what ever you want
}
});
}

After selecting items in the Spinner, it is not getting reflected to me (not visible to naked eye)

I am working on a project in which I need to dynamically add TextView and Spinner as well. I was able to add these two things dynamically from my program successfully.
Now when I was trying to select some items in the Spinner, that items is not getting shown in my emulator but the items that I selected gets shown in the Toast.
Does I need to do anything to make that item selected in Spinner?
for (Map.Entry<String, String> entry : mapColumns.entrySet()) {
spinnerArray = new ArrayList<String>();
final TextView rowTextView = new TextView(cont);
final Spinner spinner = new Spinner(cont);
rowTextView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
spinner.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
for(String s: entry.getValue().split(",")) {
System.out.println(s);
s = s.replaceAll("[^a-zA-Z0-9]+","");
spinnerArray.add(s);
}
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(cont, android.R.layout.simple_spinner_dropdown_item, spinnerArray);
rowTextView.setText(entry.getKey());
rowTextView.setTypeface(null, Typeface.BOLD);
spinner.setAdapter(spinnerArrayAdapter);
// add the listener
spinner.setOnItemSelectedListener(new CustomOnItemSelectedListener());
layout.addView(rowTextView);
layout.addView(spinner);
}
class CustomOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent, View view, int pos,
long id) {
Toast.makeText(
parent.getContext(),
"OnItemSelectedListener : "
+ parent.getItemAtPosition(pos).toString(),
Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
}
Below is my XML Layout-
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/llayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button1"
android:layout_width="100px"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal|center"
android:gravity="center_vertical|center_horizontal"
android:text="Save" />
</LinearLayout>
</ScrollView>
Here mapColumns will hev Key-Value pair. So in the Spinner all the items are getting shown from the Value of that map.
Problem Statement:-
Now I need to make sure if anybody is selecting any items in the Spinner, it should get selected and be visible to other person.
Below is the image in which I have selected items in the Spinner but it is not getting shown and also TextView is also very light in color-
The below code which you are using to populate the spinnerArray looks suspicious because it will remove all the characters from the string.
for(String s: entry.getValue().split(",")) {
///System.out.println(s); move this print statement to below line and see what it prints in your logs
s = s.replaceAll("[^a-zA-Z0-9]+","");
System.out.println(s);
spinnerArray.add(s);
}
So if the spinnerArray is provided with empty string it will come up with empty spinner. I would suggest comment out the whole block and then try your app and see if the problem persist.
If you want the spinner to comeup with a selected item then add the following line:
spinner.setSelection (0);

Seeking some insight understanding how a simpleCursorAdapter works

Hey guys I was just hoping someone could shed some light on how this code is working and more specifically the simpleCursorAdapter. The full program is an app that is a to-do list, it's a very simple tutorial the user can input data or "notes" and save to a sqlite data base using cursors and loaders.
So my problem is that there is a specific method that I'm having trouble grasping how it works and as a result I cannot manipulate the way the data is displayed. I think the problem lies in the fact that I just don't understand how the adapter is taking in a different layout than what is displayed and showing it all in a list view.
private void fillData() {
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY };
// Fields on the UI to which we map
int[] to = new int[] { R.id.label }; //I don't quite understand but I know it's just a value for the adapter
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from,
to, 0); /*This line specifically I don't understand how it is working.
R.layout.todo_row is a near blank xml, used when there are no "todos"
with no listviews. R.layout.todo_list has the listview's but when
assigned in the adapter it doesn't work.
setListAdapter(adapter);
}
Overall I'm trying to make 3 listviews side by side to read data from the DB and just play around. If anyone could help me out it would be very much so appreciated, thank you.
R.layout.todo_row
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/icon"
android:layout_width="30dp"
android:layout_height="24dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:src="#drawable/reminder" >
</ImageView>
<TextView
android:id="#+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:lines="1"
android:text="#+id/TextView01"
android:textSize="24dp"
>
</TextView>
</LinearLayout>
and R.layout.todo_list
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#android:id/list"
android:layout_width="110dp"
android:layout_height="200dp" >
</ListView>
<ListView
android:id="#+id/listMiddle"
android:layout_width="110dp"
android:layout_height="200dp"
android:layout_toRightOf="#android:id/list" >
</ListView>
<ListView
android:id="#+id/listRight"
android:layout_width="110dp"
android:layout_height="200dp"
android:layout_toRightOf="#id/listMiddle" >
</ListView>
<TextView
android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/no_todos" />
</RelativeLayout>
The entire class is below
package de.vogella.android.todos;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import de.vogella.android.todos.contentprovider.MyTodoContentProvider;
import de.vogella.android.todos.database.TodoTable;
/*
* TodosOverviewActivity displays the existing todo items
* in a list
*
* You can create new ones via the ActionBar entry "Insert"
* You can delete existing ones via a long press on the item
*/
public class TodosOverviewActivity extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_EDIT = 1;
private static final int DELETE_ID = Menu.FIRST + 1;
// private Cursor cursor;
private SimpleCursorAdapter adapter;
private SimpleCursorAdapter middleAdapter;
private SimpleCursorAdapter rightAdapter;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.todo_list);
this.getListView().setDividerHeight(2);
fillData();
registerForContextMenu(getListView());
}
// Create the menu based on the XML defintion
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.listmenu, menu);
return true;
}
// Reaction to the menu selection
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.insert:
createTodo();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DELETE_ID:
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(MyTodoContentProvider.CONTENT_URI + "/"
+ info.id);
getContentResolver().delete(uri, null, null);
fillData();
return true;
}
return super.onContextItemSelected(item);
}
private void createTodo() {
Intent i = new Intent(this, TodoDetailActivity.class);
startActivity(i);
}
// Opens the second activity if an entry is clicked
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, TodoDetailActivity.class);
Uri todoUri = Uri.parse(MyTodoContentProvider.CONTENT_URI + "/" + id);
i.putExtra(MyTodoContentProvider.CONTENT_ITEM_TYPE, todoUri);
startActivity(i);
}
private void fillData() {
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY };
String[] middleId = new String[] { TodoTable.COLUMN_ID };
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
int[] two = new int[] { R.id.label };
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from,
to, 0);
middleAdapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, middleId,
two, 0);
setListAdapter(adapter);
// setListAdapter(middleAdapter);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}
// Creates a new loader after the initLoader () call
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { TodoTable.COLUMN_ID, TodoTable.COLUMN_SUMMARY };
CursorLoader cursorLoader = new CursorLoader(this,
MyTodoContentProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// data is not available anymore, delete reference
adapter.swapCursor(null);
}
}
So my problem is that there is a specific method that I'm having trouble grasping how it works and as a result I cannot manipulate the way the data is displayed.
The method:
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from, to, 0);
Well, let's break this constructor down by each parameter:
this, a Context. The Adapter needs a Context to inflate each row's layout.
R.layout.todo_row, the row's layout. Every record in your Cursor will be displayed in this layout. (Exactly how the Cursor is displayed depends on from and to.)
null, a Cursor. This holds all of the data that will be shown in your ListView.
from, an array of the essential Views in the rows layout.
to, an array of the essential columns from your Cursor.
0, flags for when and why the data should be refreshed.
The trick behind every thing is this: the ids in the fourth (from) must each match a View in the second parameter (R.layout.todo_row). The Strings in fifth parameter must each match a column name in your Cursor. The fourth (from) and fifth parameters (to) must match one-to-one, because each column is displayed in one View. That's it really.
As you may have realized by now, this note:
R.layout.todo_row is a near blank xml, used when there are no "todos" with no listviews.
is wrong, sorry. If you want to display a note when the Cursor is empty add:
<TextView android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="No data"/>
to todo_list.xml as described in ListActivity's documenation. By using this "magic id" in your TextView, the note should automatically be shown or hidden when appropriate.
All of this only interacts view the first ListView (with the id: `android:id="#android:id/list"), you need to create new Cursors and Adapters to use the other ListViews. Hope that helps!
I haven't looked at the source code for SimpleCursorAdapter. However, it appears that it is mostly doing two things:
The query for your data, based on the params you provide in fillData.
Looping through the results and populating the list using your template.
In my debuggin, I did notice that it's pretty efficient about filling the list - it only allocates as many rows as are needed to display. As you scroll, it recycles them rather than free reallocate them.
It looks like your fillData code is good. You don't say what isn't working so perhaps it's elsewhere. I've never used onCreateLoader (but probably should), so can't comment on that.
I saw one minor problem: in your R.layout.todo_row, you forgot the orientation attribute.
I'm assuming that the code compiles and runs fine and you just want to know what's going on. Well, there are a few things you need to be aware of. The first one is that the ListView doesn't take a layout parameter, your activity does in setContentView. Your R.layout.todo_list is only used by the TodosOverviewActivity to create the "screen" or "look" of the activity, that is, 3 ListView views side by side. Since the activity is a ListActivity it will automatically look for an entry of type ListView with an id of #android:id/list to automatically hook up the list listeners (just saves you a bit of typing), so your other lists will pretty much just sit there until you hook them up yourself (don't use the same id for items on the same layout). If you need to access these other lists you'll need to use the findViewById method in your activity and search for the id of the list you want. For example, we can access the middle list using this:
ListView middleList = (ListView)this.findById(R.id.listMiddle);
Now that we have the list, we need something to show. The lists are completely empty and you need to bring in data from somewhere. In your case, the data comes from a Cursor object you get from a ContentProvider. The cursor contains only one column that matters to us, the TodoTable.COLUMN_SUMMARY column that has the text we want to display in the list. The problem is that a list doesn't have a clue of what to do with a Cursor since the only thing it does is put a view on the screen and scroll it up and down. The Cursor, on the other hand, has all the data you want to show but doesn't have a clue of what a View is, much less how to put all the data it contains inside one for the list to show. Now you have the SimpleCursorAdapter which is, like the name says, an adapter. It is used to make incompatible things work together. On one side you have a list that needs a view, on the other side you have a cursor with the data you want to show, so now you need an adapter that will map each piece of data to part of a view. The SimpleCursorAdapter will ask you for 4 things in particular. The first is the layout of the view to show on the list, that is, what should a list item look like. This is the R.layout.todo_row that tells the adapter what views should be created. In this case we only have an icon and a TextView with the id R.id.label. Second, it will ask you for the cursor that contains the data, which is being set inside the onLoadFinished method (it is null when the adapter is created). Third, it wants to know what columns on the cursor matter. This is the String[] from array that says it should look for the data in the TodoTable.COLUMN_SUMMARY. Last, it needs to know where in the view to put this data, and this is the int[] to that contains the id of the TextView you'll be using to display the text, R.id.label.
In summary, the adapter is like a map between the data from the cursor and the layout for the view. Now, when the list needs a view to show on screen it will ask the adapter to give it one. The adapter then either recycles or creates a view form the layout you provided, fetches the data for each piece of the layout from the cursor and gives it all ready to go to the list to put it on the screen.

Categories

Resources