Android ListView and Event Bubbling
I have an Android app with a ListView. I've created my own item layout that has a CheckBox, followed by two TextViews and a Button:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="f开发者_JS百科ill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBox android:id="@+id/checkBox1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="5dip"></CheckBox>
<RelativeLayout android:layout_height="match_parent" android:id="@+id/relativeLayout1" android:layout_width="match_parent">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button1" android:layout_alignParentRight="true" android:text="Button"></Button>
<TextView android:id="@+id/textView1" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="TextView" android:layout_toLeftOf="@+id/button1" android:layout_alignTop="@+id/button1" android:layout_width="match_parent"></TextView>
<TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:text="TextView" android:layout_below="@+id/textView1" android:layout_alignLeft="@+id/textView1" android:layout_alignRight="@+id/textView1"></TextView>
</RelativeLayout>
</LinearLayout>
When the user taps on the row, I want different behaviour depending on if they tapped on the CheckBox, the Button or the row itself (i.e. anything other than the CheckBox or Button). Here's what I've tried to implement so far (using setOnItemClickListener):
package com.camelconsultants.shoplist;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class HelloAndroid extends ListActivity implements OnItemClickListener
{
ArrayList<HashMap<String, String>> Items = new ArrayList<HashMap<String, String>>();
/** Called when the activity is first created. */
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate( savedInstanceState );
GetData();
setListAdapter
(
new SimpleAdapter
(
this.getBaseContext(),
Items,
R.layout.multiitem,
new String[]{ "Code", "Description" },
new int[]{ R.id.textView1, R.id.textView2 }
)
);
ListView lv = getListView();
lv.setTextFilterEnabled( true );
lv.setOnItemClickListener( this );
}
public void onItemClick( AdapterView<?> parent, View view, int position, long id )
{
Toast.makeText
(
getApplicationContext(),
parent.getItemAtPosition(position).toString(),
Toast.LENGTH_SHORT
).show();
}
void GetData()
{
HashMap<String, String> Item;
try
{
JSONObject JsonObject = new JSONObject( this.getResources().getString(R.string.Json) );
JSONArray JsonArray = JsonObject.getJSONArray( "Items" );
for ( int i = 0; i < JsonArray.length(); i++ )
{
Item = new HashMap<String, String>();
Item.put( "Code", JsonArray.getJSONObject(i).getString("Code") );
Item.put( "Description", JsonArray.getJSONObject(i).getString("Description") );
Items.add( Item );
}
}
catch( JSONException Bummer )
{
Bummer.printStackTrace();
}
}
}
When I run the application and click on one of the TextViews, nothing happens!
What's the best way to approach this?
I found out that I could set a callback for a View in the xml definition. Here's the updated xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/linearLayout1" android:clickable="true" android:onClick="onClick" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBox android:id="@+id/checkBox1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="5dip" android:onClick="onCheckBoxClick"></CheckBox>
<RelativeLayout android:layout_height="match_parent" android:id="@+id/relativeLayout1" android:layout_width="match_parent">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button1" android:layout_alignParentRight="true" android:text="Button" android:onClick="onButtonClick"></Button>
<TextView android:id="@+id/textView1" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="TextView" android:layout_toLeftOf="@+id/button1" android:layout_alignTop="@+id/button1" android:layout_width="match_parent"></TextView>
<TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:text="TextView" android:layout_below="@+id/textView1" android:layout_alignLeft="@+id/textView1" android:layout_alignRight="@+id/textView1"></TextView>
</RelativeLayout>
</LinearLayout>
I've updated the code with the appropriate callback hooks:
package com.camelconsultants.shoplist;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class HelloAndroid extends ListActivity
{
ArrayList<HashMap<String, String>> Items = new ArrayList<HashMap<String, String>>();
/** Called when the activity is first created. */
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate( savedInstanceState );
GetData();
setListAdapter
(
new SimpleAdapter
(
this.getBaseContext(),
Items,
R.layout.multiitem,
new String[]{ "Code", "Description" },
new int[]{ R.id.textView1, R.id.textView2 }
)
);
ListView lv = getListView();
lv.setTextFilterEnabled( true );
}
@SuppressWarnings("unchecked")
private HashMap<String, String> getItem( View view )
{
HashMap<String, String> Item = null;
ListView listView = getListView();
int Position = listView.getPositionForView( view );
if ( listView.getItemAtPosition(Position) instanceof HashMap )
Item = (HashMap<String, String>)listView.getItemAtPosition( Position );
return Item;
}
public void onClick( View view )
{
HashMap<String, String> Item = getItem( view );
if ( Item == null )
return;
Toast.makeText
(
getApplicationContext(),
Item.get("Code") + ", " + Item.get( "Description" ),
Toast.LENGTH_SHORT
).show();
}
public void onCheckBoxClick( View view )
{
HashMap<String, String> Item = getItem( view );
if ( Item == null )
return;
Toast.makeText
(
getApplicationContext(),
"CheckBox! - " + Item.get("Code") + ", " + Item.get( "Description" ),
Toast.LENGTH_SHORT
).show();
}
public void onButtonClick( View view )
{
HashMap<String, String> Item = getItem( view );
if ( Item == null )
return;
Toast.makeText
(
getApplicationContext(),
"Button! - " + Item.get("Code") + ", " + Item.get( "Description" ),
Toast.LENGTH_SHORT
).show();
}
void GetData()
{
HashMap<String, String> Item;
try
{
JSONObject JsonObject = new JSONObject( this.getResources().getString(R.string.Json) );
JSONArray JsonArray = JsonObject.getJSONArray( "Items" );
for ( int i = 0; i < JsonArray.length(); i++ )
{
Item = new HashMap<String, String>();
Item.put( "Code", JsonArray.getJSONObject(i).getString("Code") );
Item.put( "Description", JsonArray.getJSONObject(i).getString("Description") );
Items.add( Item );
}
}
catch( JSONException Bummer )
{
Bummer.printStackTrace();
}
}
}
The getItem
function returns the HashMap for the current item by passing up the View to getItemAtPosition
. The cool thing about that is that it doesn't matter whether the view is nested or not.
However, I'm assuming that creating my own Adapter and using the setTag method is more efficient. But for the time being, the above code suits my purposes!
精彩评论