OK, so I'm mainly a C++ developer but I've been looking into making apps for Android. I haven't used Java before, or done much UI development, so it's a lot to take in at the moment.
There is a question at the end of this, skip to it if you feel like it. The following is mostly background details...
Anyway, for my app, I require the ability to make a vertical scrolling list of elements. Each element should have the same layout, but I'd like to be able to have a number of elements laid out in each list element. So, something like this:
List:
ListItem:
Column:
TickBox
Column:
Row: TitleText
Row: DetailText
Column:
Image
ListItem:
Column:
TickBox
Column:
Row: TitleText
Row: DetailText
Column:
Image
... etc.
(I hope that's vaguely understandable). I don't know the number of items in the list at the time of writing the app (the user will create new items to go in the list).
OK, so after looking about a bit, I have the following scheme. A main activity (activity_main.xml) that looks like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<ListView
android:id="@+id/android:list"
android:layout_width="fill_parent"
s android:layout_height="fill_parent"
/>
<TextView
android:id="@+id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@string/click_to_add_task"
/>
</LinearLayout>
This has a main ListView
, which the items will go in, and a TextView
, which has some text in it. I found this example that seemed to do what I was looking for (or at least had a sensible-looking approach). So, each item has it's own XML that describes how an item in the list should be layed out:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip" >
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:src="@drawable/ic_launcher"
android:contentDescription="@string/null_string"
/>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:layout_weight="1" >
<TextView
android:id="@+id/toptext"
android:orientation="vertical"
android:layout_width="0dip"
android:layout_height="fill_parent"
/>
<TextView
android:id="@+id/bottomtext"
android:orientation="vertical"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:singleLine="true"
android:ellipsize="marquee"
/>
</LinearLayout>
</LinearLayout>
In order to use this layout XML for the items in the list, there is a custom TaskListView
class (extends listView
) and a custom TaskAdapter
class (extends ArrayAdapter< Task >
, Task
is my class that holds details of each item in the list) that populates the list.
Finally, my ActivityMain.java looks like this:
package com.test.test1
import java.util.ArrayList;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends ListActivity {
private ProgressDialog m_progDialog = null;
private ArrayList< Task > m_tasks = null;
private TaskAdapter m_adapter;
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main);
m_tasks = new ArrayList< Task >();
m_progDialog = ProgressDialog.show( MainActivity.this, "Please wait...", "Retrieving data...", true );
getTasks();
m_adapter = new TaskAdapter( this, R.layout.row, m_tasks );
setListAdapter( m_adapter );
m_adapter.notifyDataSetChanged();
Log.d("ARRAY", " " + m_tasks.size() );
for ( int i = 0; i < m_tasks.size(); i++ )
m_adapter.add( m_tasks.get( i ) );
m_progDialog.dismiss();
m_adapter.notifyDataSetChanged();
}
public void getTasks() {
try {
m_tasks = new ArrayList< Task >();
Task t1 = new Task();
t1.setName("SF Services");
t1.setStatus("Pending");
Task t2 = new Task();
t2.setName("SF Ads");
t2.setStatus("Completed");
m_tasks.add(t1);
m_tasks.add(t2);
}
catch ( Exception e )
{
Log.e("BACKGROUD_PROC", e.getMessage() );
}
}
private class TaskAdapter extends ArrayAdapter< Object >
{
private ArrayList< Task > m_items = new ArrayList< Task >();
private Context m_context;
public TaskAdapter( Context context, int textViewResourceId, ArrayList< Task > items ) {
super( context, textViewResourceId );
m_context = context;
m_items = items;
}
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
View v = convertView;
if ( v == null )
{
LayoutInflater vi = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
v = vi.inflate( R.layout.row, null);
}
if ( m_items.size() > position )
{
Task task = m_items.get( position );
if ( task != null ) {
TextView tt = (TextView) v.findViewById( R.id.toptext);
TextView bt = (TextView) v.findViewById( R.id.bottomtext);
if (tt != null)
tt.setText( "Name: " + task.getName());
if(bt != null)
bt.setText( "Status: " + task.getStatus() );
// (DEBUGGING) make an alert box that displays the values of the values in the text views
AlertDialog.Builder altDialog= new AlertDialog.Builder(m_context);
altDialog.setMessage( tt.getText().toString() + ": " + bt.getText().toString() ); // here add your message
altDialog.setNeutralButton( "OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "Ok button Clicked ", Toast.LENGTH_LONG).show();
}
});
altDialog.show();
}
}
return v;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
So, the onCreate
method of MainActivity
does some "generic" bits first and then (on line 35) calls getTasks()
. This populates the ArrayList
of tasks with a couple of tasks. Then line 37 initialises the TaskAdapter
, which doesn't do anything particularly suspicious. It then appears that the tasks get added to the adapter on line 43. At some point the method TaskAdapter.getView
is called for each item in the list of tasks. I can see this happening because I made an alert box appear each time the method is called. The box (line 106) displays the text that should be in the finished view. I can indeed see the correct text in the alert. However, the text does not appear in the items of the list. My question (finally) is why not? My understanding is that the View
returned by View.findViewById
is a reference to a view (if my new understanding of Java is correct), so adding text to these TextView
objects should affect the ones in the oringinal view that was passed into the function?
Sorry about the extremely long post! (I'm not sure which bits are crittical and which are superfluous)