I am using a custom BaseAdapter implementation for a Listview. Everything is fine until there are many items in the listview and the scrolling mechanism appears. (items exceed the listview height). When I now delete one item the listview refreshes and shows the right data for just milliseconds. After that it shows also the old values from time before deletion. But only for the items after the deleted entry. Every item above is ok.
When I start scrolling now, everything is fine again?! Looks somehow as there is some caching problem or whatever?!
See the following files and pictures.
CustomBaseAdapter:
public class CustomBaseAdapter extends BaseAdapter {
private Activity context;
private List<Entry> objects;
private LayoutInflater layoutInflater = null;
private int textViewResourceId;
public CustomBaseAdapter(Activity context, int textViewResourceId,
List<Entry> objects) {
this.textViewResourceId = textViewResourceId;
this.objects = objects;
this.context = context;
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void updateEntries(List<Entry> entries){
ThreadPreconditions.checkOnMainThread();
List<Entry> entryHelper = new ArrayList<>();
entryHelper.addAll(entries);
this.objects.clear();
this.objects.addAll(entryHelper);
}
#Override
public int getCount() {
return objects.size();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
EntryDataHolder holder;
//TODO Views recycling implementieren. Ist derzeit auskommentiert aufgrund des Bug #203
// If null we create row, if not we get the holder object of current row
if (convertView == null) {
// set the layout
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = li.inflate(textViewResourceId, null);
holder = new EntryDataHolder(row);
row.setTag(holder);
} else {
holder = (EntryDataHolder) row.getTag();
}
// fill components with value
Entry entry = objects.get(position);
holder.tvCol1.setText(entry.getDateAsStringWithoutYear());
holder.tvCol2.setText(entry.getDescription());
holder.tvCol3.setText(entry.getAmountAsStringWithCurrency());
// highlight text if item can be associated with a deposit
if(entry instanceof Expense){
if(((Expense)entry).getAssociatedDeposit() != null){
ComponentUtil.setDepositAssociationColoring(holder.tvCol1);
ComponentUtil.setDepositAssociationColoring(holder.tvCol2);
ComponentUtil.setDepositAssociationColoring(holder.tvCol3);
}else{
ComponentUtil.setStandardColoring(holder.tvCol1);
ComponentUtil.setStandardColoring(holder.tvCol2);
ComponentUtil.setStandardColoring(holder.tvCol3);
}
}
row.setTag(holder);
return row;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public Entry getItem(int position) {
return objects.get(position);
}
class EntryDataHolder {
public TextView tvCol1;
public TextView tvCol2;
public TextView tvCol3;
public EntryDataHolder(View base){
tvCol1 = (TextView) base.findViewById(R.id.tvCol1);
tvCol2 = (TextView) base.findViewById(R.id.tvCol2);
tvCol3 = (TextView) base.findViewById(R.id.tvCol3);
}
}
}
Activity class:
public class RevenueListActivity extends BaseActivity implements
IDialogActionListener {
// view component references
private TextView tvHeader;
private TextView tvTotal;
private ListView lvData;
private CustomBaseAdapter adapter;
private List<Entry> tmpRevenues;
private AdView adView;
private MonthControl mControl;
private AbstractDialog entryDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_entry_list);
// Add AdView
LinearLayout layout = (LinearLayout) findViewById(R.id.llAdView);
adView = AdViewManager.addAdToView(this, layout);
initializeComponents();
registerForContextMenu(lvData);
loadIntentVariables();
loadEntryList();
}
/**
* Create the options menu for this activity
*/
#Override
public boolean onCreateOptionsMenu(android.view.Menu menu) {
MenuInflater menuInflator = getMenuInflater();
menuInflator.inflate(R.menu.activity_entry_list, menu);
// Readonly configuration
MenuItem miAddEntry = menu.findItem(R.id.miAddEntry);
if (mControl.getIsSaveable()) {
miAddEntry.setVisible(true);
} else {
miAddEntry.setVisible(false);
}
return true;
}
/**
* Create the context menu for the list
*/
#Override
public void onCreateContextMenu(android.view.ContextMenu menu, View v,
android.view.ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
// Readonly configuration
if (mControl.getIsSaveable()) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.activity_entry_context_menu, menu);
}
}
/**
* Called if one of the Context Menu Item was clicked
*/
#Override
public boolean onContextItemSelected(MenuItem item) {
// get selected item from list view
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
.getMenuInfo();
Object selectedObject = lvData.getAdapter().getItem(info.position);
Entry selectedEntry = (Entry) selectedObject;
switch (item.getItemId()) {
case R.id.miDeleteEntry:
deleteSelectedEntry(selectedEntry);
break;
case R.id.miEditEntry:
editSelectedEntry(selectedEntry);
break;
default:
return super.onContextItemSelected(item);
}
return true;
}
/**
* Deletes the current selected entry from month control
*/
private void deleteSelectedEntry(Entry entryToDelete) {
//mControl.deleteRevenue(entryToDelete);
tmpRevenues.remove(entryToDelete);
adapter.updateEntries(tmpRevenues);
adapter.notifyDataSetChanged();
}
/**
* Opens the dialog for editing the selected entry
*
* #param entryToEdit
*/
private void editSelectedEntry(Entry entryToEdit) {
entryDialog.showDialog(entryToEdit);
}
/**
* Called if one of the Option Menu Item was clicked
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// check which option menu item was clicked
switch (item.getItemId()) {
case R.id.miAddEntry:
entryDialog.showDialog(null);
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
/**
* Called when dialog was committed with ok
*
* #param event
*/
public void dialog_commitHandler(DialogEvent event) {
// check which type of action was performed with the dialog
if (event.getEventType().equals(DialogEvent.EDIT_ENTRY_EVENT)) {
// Notify Month Control observers that the object has been modified.
//mControl.changeOccured();
} else if (event.getEventType().equals(DialogEvent.NEW_ENTRY_EVENT)) {
//mControl.addRevenue(event.getEntry());
tmpRevenues.add(event.getEntry());
}
adapter.updateEntries(tmpRevenues);
adapter.notifyDataSetChanged();
}
/**
* Called when the dialog was canceled
*/
public void dialog_cancelHandler() {
// Do nothing
}
/**
* inject all needed components
*/
private void initializeComponents() {
tvHeader = (TextView) findViewById(R.id.tvEntryListHeader);
tvTotal = (TextView) findViewById(R.id.tvTotal);
lvData = (ListView) findViewById(R.id.lvData);
}
/**
* injects the right dialog after intent variables were loaded
*/
private void initializeDialog() {
entryDialog = new RevenueDialog(this, R.layout.dialog_revenue);
entryDialog.addEventListener(this);
}
/**
* Loads extra values from intent and configures activity
*/
private void loadIntentVariables() {
mControl = MonthControl.getInstance(null);
// Readonly configuration
tvHeader.setText(getText(R.string.tv_revenue_header)
+ (mControl.getIsSaveable() ? "" : " ("
+ getString(R.string.tv_archived_month) + ")"));
// after intent were loaded we can init the associated dialog
initializeDialog();
}
/**
* Iterates through the entry list and adds them to a Adapter, which will be
* used for the list view
*/
private void loadEntryList() {
tmpRevenues = new ArrayList<Entry>();
tmpRevenues.addAll(mControl.getRevenues());
adapter = new CustomBaseAdapter(this,
R.layout.list_item_layout_revenue, tmpRevenues);
lvData.setAdapter(adapter);
// make calculations
calculateSum();
}
/**
* calculates the sum of all entries
*/
private void calculateSum() {
BigDecimal sum = BigDecimalFactory.createBigDecimal("0");
for (Entry entry : tmpRevenues) {
sum = sum.add(entry.getAmount());
}
tvTotal.setText(sum.toString() + " €");
}
#Override
protected void onPause() {
if (adView != null)
adView.resume();
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
if (adView != null)
adView.resume();
}
#Override
protected void onDestroy() {
if (adView != null)
adView.destroy();
super.onDestroy();
}
}
Model class code:
public void deleteRevenue(Entry entry) {
LogUtil.debug(MonthControl.class, TAG, "Deleting revenue...");
revenues.remove(entry);
notifyListener();
}
public List<Entry> getRevenues() {
if (revenues == null) {
revenues = new ArrayList<Entry>();
}
return revenues;
}
The list item Layout:
<?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="40dp"
android:minHeight="40dp"
android:orientation="horizontal"
android:weightSum="1.0" >
<TextView
android:id="#+id/tvCol1"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.3"
android:ellipsize="marquee"
android:lines="1"
android:scrollHorizontally="true"
android:singleLine="true" />
<TextView
android:id="#+id/tvCol2"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.5"
android:ellipsize="marquee"
android:lines="1"
android:scrollHorizontally="true"
android:singleLine="true" />
<TextView
android:id="#+id/tvCol3"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.2"
android:ellipsize="marquee"
android:lines="1"
android:scrollHorizontally="true"
android:singleLine="true" />
</LinearLayout>
Befor deletion:
After deletion of 5th item:
After scrolling:
UPDATE: Solution
It is the layout. I just removed one liner_layout layer the listview was contained in. Now everything ist fine.
This is the tested working layout file:
<?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="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/llAdView"
android:layout_width="fill_parent"
android:layout_height="75px"
android:orientation="vertical">
<!-- AdView inserted here at runtime -->
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="40dp"
android:background="#color/blue"
android:orientation="vertical" >
<TextView
android:id="#+id/tvEntryListHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|center_horizontal|center_vertical"
android:textSize="18dip"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="40dp"
android:layout_marginTop="10dip"
android:weightSum="1.0" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.3"
android:text="#string/tv_date_header" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.5"
android:text="#string/tv_description_header" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.2"
android:text="#string/tv_amount_header" />
</LinearLayout>
<ListView
android:id="#+id/lvData"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="8dp"
android:background="#drawable/border_ui"
android:divider="#color/dark_grey"
android:dividerHeight="1dp"
android:padding="4dp" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:weightSum="1.0" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="#string/tv_total" />
<TextView
android:id="#+id/tvTotal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.2" />
</LinearLayout>
</LinearLayout>
Your problem seems to be from
adapter.updateEntries(mControl.getRevenues());
It seems like mControl.getRevenues() returns the previous data.
Try commenting this line out and see.
BTW, you don't need to call adapter.updateEntries because notifyDataSetChange will do the adapter update.
You are missing which is a must,
row.setTag(holder);// this is missing.
return row;
in your getView Method
and also, this is somewhat wrong according to your approach, So change to this and see,
public void updateEntries(List<Entry> entries){
ThreadPreconditions.checkOnMainThread();
this.objects.clear();
this.objects.addAll(entries);
}
and also make sure that,
mControl.getRevenues() only returns values that are not deleted, if this method tend to return the same data as old data, then this is also wrong.
I think I got where the problem is,
remove the following method from your adapter.
public void updateEntries(List<Entry> entries){
ThreadPreconditions.checkOnMainThread();
List<Entry> entryHelper = new ArrayList<>();
entryHelper.addAll(entries);
this.objects.clear();
this.objects.addAll(entryHelper);
}
This is what causing the problem.
and in this method,,
/**
* Deletes the current selected entry from month control
*/
private void deleteSelectedEntry(Entry entryToDelete) {
//mControl.deleteRevenue(entryToDelete);
tmpRevenues.remove(entryToDelete);
adapter.notifyDataSetChanged();
}
adapter.updateEntries(tmpRevenues); you will not call this method because when you remove the Entry earler, you are actually removing the items inside the adapter, because you are passing the same reference to the adapter.
But still, debug the application and see when you do the remove on the tmpRevenues that Entry is actually getting removed.
I am quoting your comment,
if i update the "updateEntries" function as you showed i end up in
that scenario that my list is completely empty after deleting one
item. ?!? Maybe because of the refernce to the original list.
This solution will fix the above issue
Related
I am displaying a tab and which has two tags Category1 and Category2.
The list gets populated based on the data in the tables for category1 and category2. This works fine but to make the items in the listview respond in a certain way to clicks made on the listview is what I am stuck at. Like for example:
I am trying to make the checkbox become visible and checked when the user long clicks on the particular row in the listview. I am trying to code it but I have no idea where to begin. There aren't questions asked on this topic specifically. Below is the code that I have tried.
FragmentClass
public class CategoryFragment extends Fragment{
private ListView listView;
private String currentTabTag;
private CategoryAdapter1 categoryAdapter1;
private CategoryAdapter2 categoryAdapter2;
private MergeAdapter adapter = new MergeAdapter();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// inflate the layout for this fragment
View view = inflater.inflate(R.layout.activity_category_fragment_list, container, false);
// get references to widgets
listView = (ListView) view.findViewById (R.id.ListView);
// get the current tab
TabHost tabHost = (TabHost) container.getParent().getParent();
currentTabTag = tabHost.getCurrentTabTag();
// refresh the category list view
refreshCategoryList();
listView.setOnItemLongClickListener(this);
// return the view
return view;
}
public void refreshCategoryList() {
// get category list for current tab from database
Context context = getActivity().getApplicationContext();
CategoryDatabase categoryDatabase = new CategoryDatabase(context);
if(currentTabTag.equals("Category 1")) {
ArrayList<Category1> category1ArrayList = categoryDatabase.getAllCategory1(currentTabTag);
// create adapter and set it in the ListView widget
categoryAdapter1 = new Category1Adapter(context, category1ArrayList);
adapter.add(Category1Adapter);
}
else if(currentTabTag.equals("Category 2"))
{
ArrayList<Category2> category2ArrayList = categoryDatabase.getAllCategory2(currentTabTag);
// create adapter and set it in the ListView widget
categoryAdapter2 = new categoryAdapter2(context, category2ArrayList);
adapter.add(categoryAdapter2);
}
listView.setAdapter(adapter);
}
#Override
public void onResume() {
super.onResume();
refreshTaskList();
}
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
ItemsListLayout itemsListLayout = new ItemsListLayout(context);
switch (view.getId()) {
case R.id.listView: {
itemsListLayout.setMyListItems();
Toast.makeText(context,"Why cant I be called?",Toast.LENGTH_SHORT).show();
//registerForContextMenu(view);
}
/*default:
Intent intent = new Intent(context, MyCategoryAddEdit.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.putExtra("categoryId", category.getId());
intent.putExtra("editMode", true);
context.startActivity(intent);
break;*/
}
return false;
}
}
activity_category_fragment_list
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<ListView
android:id="#+id/prescriptorsListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:longClickable="true"
android:descendantFocusability="blocksDescendants">
</ListView>
</LinearLayout>
ItemsListLayout
public class ItemsListLayout extends RelativeLayout implements View.OnCreateContextMenuListener {
private CheckBox completedCheckBox;
private TextView nameTextView;
private TextView notesTextView;
private ListView listView;
private CategoryDatabase categoryDatabase;
private Context context;
public ItemsListLayout(Context context) { // used by Android tools
super(context);
}
public ItemsListLayout(Context context, Prescription p) {
super(context);
// set context and get db object
this.context = context;
categoryDatabase = new CategoryDatabase(context);
// inflate the layout
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.activity_my_category_items_list_layout, this, true);
// get references to widgets
completedCheckBox = (CheckBox) findViewById(R.id.completedCheckBox);
completedCheckBox.setVisibility(GONE);
nameTextView = (TextView) findViewById(R.id.nameTextView);
notesTextView = (TextView) findViewById(R.id.notesTextView);
//listView = (ListView) findViewById(R.id.listView);
// set listeners
//listView.setOnItemLongClickListener(this);
// set task data on widgets
setCategory1(p);
}
public void setCategory1(setCategory1 p) {
setCategory1 = p;
nameTextView.setText(setCategory1.getCategory1Name());
// Remove the notes if empty
if (setCategory1.getMedicineName().equalsIgnoreCase("")) {
notesTextView.setVisibility(GONE);
}
else {
notesTextView.setText(setCategory1.getDescriptionName());
}
}
/*#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
switch (view.getId()) {
case R.id.prescriptorsListView:
completedCheckBox.setVisibility(VISIBLE);
completedCheckBox.setChecked(true);
break;
default:
Intent intent = new Intent(context, MyCategoryAddEdit.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.putExtra("categoryId", category.getId());
intent.putExtra("editMode", true);
context.startActivity(intent);
break;
}
return false;
}
*/
public void setMyListItems()
{
completedCheckBox = (CheckBox) findViewById(R.id.completedCheckBox);
completedCheckBox.setVisibility(VISIBLE);
completedCheckBox.setChecked(true);
}
#Override
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo)
{
super.onCreateContextMenu(contextMenu);
//MenuInflater inflater = this.context.
}
}
activity_my_category_items_list_layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayoutTaskItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:descendantFocusability="blocksDescendants"
android:focusable="false"
android:focusableInTouchMode="false">
<CheckBox
android:id="#+id/completedCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_margin="5dp"
android:button="#drawable/btn_check"
android:textColor="#ffffff"
android:longClickable="true"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"/>
<TextView
android:id="#+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/completedCheckBox"
android:layout_alignBottom="#+id/completedCheckBox"
android:layout_toRightOf="#+id/completedCheckBox"
android:text=""
android:textColor="#android:color/white"
android:textSize="20dp"
android:textStyle="bold"
android:longClickable="true"
android:focusable="false"
android:clickable="false"
android:textIsSelectable="false"
android:focusableInTouchMode="false"/>
<TextView
android:id="#+id/notesTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/nameTextView"
android:layout_marginTop="-5dp"
android:layout_toRightOf="#+id/completedCheckBox"
android:paddingBottom="7dp"
android:textColor="#android:color/white"
android:textSize="18dp"
android:longClickable="true"
android:focusable="false"
android:clickable="false"
android:textIsSelectable="false"
android:focusableInTouchMode="false"/>
</RelativeLayout>
Any kind of help is appreciated. Especially code snippets if possible :).
I suggest you to read this blog, it has step by step procedure of what exactly you want to achieve. You can even clone the project from github.
I'm having trouble understanding how to use Intent to move list view elements. So I've made a separate project to practice this before actually figuring it out in my app. Basically, I want to have a "Favorites" activity that can be populated from something like a search results page. Instead of a search results page, I've just created a list view with some sample elements in them with a favorites button in each. I just need some guidance on how to go from there. Here is my code so far
MainActivity.java
public class MainActivity extends Activity {
private ArrayList<String> data = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.listView);
generateList();
listView.setAdapter(new MyListAdapter(this, R.layout.item_view, data));
}
private void generateList() {
for (int i = 0; i < 5; i++) {
data.add("row/item " + i);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private class MyListAdapter extends ArrayAdapter<String> {
private int layout;
public MyListAdapter(Context context, int resource, List<String>
objects) {
super(context, resource, objects);
layout = resource;
}
#Override
public View getView(final int position, View convertView, ViewGroup
parent) {
ViewHolder minViewHolder = null;
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(layout, parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.title = (TextView)
convertView.findViewById(R.id.list_item_text);
viewHolder.button = (Button)
convertView.findViewById(R.id.favorites_button);
viewHolder.button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v) {
Toast.makeText(getContext(), "sent item " + position + "
to favorites tab", Toast.LENGTH_SHORT).show();
// data.remove(position);
MyListAdapter.this.notifyDataSetChanged();
}
});
convertView.setTag(viewHolder);
} else {
minViewHolder = (ViewHolder) convertView.getTag();
minViewHolder.title.setText(getItem(position));
}
return convertView;
}
}
public class ViewHolder{
TextView title;
Button button;
}
}
activity_main.xml
<RelativeLayout 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:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/listView"
android:layout_centerHorizontal="true" /> </RelativeLayout>
item_view.xml
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text"
android:id="#+id/list_item_text"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true" />
<Button
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fav"
android:id="#+id/favorites_button"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
I haven't set up a second activity because I wanted ideas on how to create a list that is empty but can be populated by that favorites button press. Any help at all is greatly appreciated!
Update arraylist according to favourite and call
adapter.notifyDataChanged();
if listview is same,
otherwise for different listview call another listview with adapter and new filtered arraylist
I have implemented both a ViewHolder and a convertView in my listView.
My listView is populated by a custom adapter, with a list of bookings.
When I click on an item, an invisible layout slides in from right to left, to display buttons.
I can dismiss this overlaying layout by clicking on a dismiss button so that it gets hidden again.
On this overlaying layout, I have a delete Button, which enables me to delete the item.
So far so good.
When I erase an item the item disappears as expected, the adapter is then reloaded.
The item below takes the position of the deleted item, but remains invisible.
I know it is here, because I can still click on the item to trigger the overlaying View.
So the ovelaying view is visible but not the item. I have no idea why this is happening.
I suspect the ViewHolder to be responsible of this behaviour, but I can't find a solution.
Thank you for your help.
See video here : http://youtu.be/KBGEvbUq-V0
My Bookings Class :
public class BookingsListFragment extends Fragment {
private final String SHOP_NAME_KEY = "ShopName";
private final String SHOP_ADDRESS_KEY = "ShopAddress";
public static int mSelectedItem = -1;
private static ListView mBookingsListView;
private static BookingsListViewAdapter mBookingsListViewAdapter;
private static ArrayList<Booking> mBookings;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(getActivity()));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bookings_list_fragment, container, false);
configureListView(view);
return view;
}
#Override
public void onResume() {
super.onResume();
mSelectedItem = -1;
}
private void configureListView(View view) {
mBookings = BookingsHandler.getBookings();
mBookingsListView = (ListView) view.findViewById(R.id.bookingsListView);
mBookingsListViewAdapter = new BookingsListViewAdapter();
mBookingsListView.setAdapter(mBookingsListViewAdapter);
mBookingsListView.setTextFilterEnabled(true);
}
public static void updateBookingsListView(ArrayList<Booking> mBookingsList){
mBookings = mBookingsList;
mBookingsListViewAdapter.notifyDataSetChanged();
}
static class ViewHolder {
LinearLayout bookingItemLL;
RelativeLayout optionsOverlay;
TextView productName;
TextView price;
TextView shopName;
TextView endDate;
ImageView productImage;
LinearLayout placeholderLL;
Button cancelBooking;
Button displayDirections;
Button callShop;
ImageView discardOverlay;
}
private class BookingsListViewAdapter extends BaseAdapter {
private static final int TYPE_ITEM = 0;
private static final int TYPE_PLACEHOLDER = 1;
#Override
public int getCount() {
if (mBookings != null)
return mBookings.size();
else
return 1;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
// Define a way to determine which layout to use
if (mBookings != null && mBookings.size() > 0)
return TYPE_ITEM;
else
return TYPE_PLACEHOLDER;
}
#Override
public int getViewTypeCount() {
return 2; // Number of different layouts
}
#Override
public View getView(final int position, View convertView, ViewGroup viewGroup) {
int type = getItemViewType(position);
final ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
switch (type){
case TYPE_ITEM :
convertView = LayoutInflater.from(getActivity()).inflate(R.layout.bookings_item, null);
holder.bookingItemLL = (LinearLayout) convertView.findViewById(R.id.bookingItemLL);
holder.optionsOverlay = (RelativeLayout) convertView.findViewById(R.id.bookingOptionsOverlay);
holder.productName = (TextView) convertView.findViewById(R.id.bookingProductName);
holder.price = (TextView) convertView.findViewById(R.id.bookedProductPrice);
holder.shopName = (TextView) convertView.findViewById(R.id.bookingShopName);
holder.endDate = (TextView) convertView.findViewById(R.id.bookingEndDate);
holder.productImage = (ImageView) convertView.findViewById(R.id.bookedProductImage);
holder.displayDirections = (Button) convertView.findViewById(R.id.routeShop);
holder.cancelBooking = (Button) convertView.findViewById(R.id.cancelBooking);
holder.callShop = (Button) convertView.findViewById(R.id.callShop);
holder.discardOverlay = (ImageView) convertView.findViewById(R.id.discardOverlay);
break;
case TYPE_PLACEHOLDER :
convertView = LayoutInflater.from(getActivity()).inflate(R.layout.booking_placeholder, null);
holder.placeholderLL = (LinearLayout) convertView.findViewById(R.id.placeHolderLL);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
if(type == 0) {
if(position == mSelectedItem){
holder.optionsOverlay.setVisibility(View.VISIBLE);
configureOverlayButtons(holder);
}
holder.bookingItemLL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mSelectedItem != position && mSelectedItem != -1){
View item = mBookingsListView.getChildAt(mSelectedItem - mBookingsListView.getFirstVisiblePosition());
if(item != null){
RelativeLayout overlayOptions = (RelativeLayout) item.findViewById(R.id.bookingOptionsOverlay);
overlayOptions.setVisibility(View.GONE);
}
}
Animation slideInAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.booking_options_overlay_animation);
holder.optionsOverlay.startAnimation(slideInAnimation);
holder.optionsOverlay.setVisibility(View.VISIBLE);
mSelectedItem = position;
configureOverlayButtons(holder);
}
});
final Booking booking = mBookings.get(position);
holder.productName.setText(booking.getName().toUpperCase());
holder.price.setText("Prix lors de la réservation : " + String.format("%.2f", Float.valueOf(booking.getPrice())) + " €");
holder.shopName.setText(booking.getShopName());
holder.endDate.setText(booking.getEndDate());
holder.productImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.product_placeholder)
.showImageOnFail(R.drawable.product_no_image_placeholder)
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(BeeWylApiClient.getImageUrl(booking.getImageURL()),holder.productImage, options);
}
if(type == 1){
holder.placeholderLL.setLayoutParams(BeeWylHelper.getPlaceHolderSizeForFreeScreenSpace(getActivity(),0));
}
return convertView;
}
private void configureOverlayButtons(final ViewHolder holder){
holder.cancelBooking.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder ab = new AlertDialog.Builder(getActivity());
ab.setMessage("Annuler la réservation ?").setPositiveButton("Oui", dialogClickListener)
.setNegativeButton("Non", dialogClickListener).show();
}
});
holder.displayDirections.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
launchMapActivity();
}
});
holder.callShop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
launchDialer();
}
});
holder.discardOverlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Animation hideOverlayAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.booking_overlay_dismiss);
holder.optionsOverlay.startAnimation(hideOverlayAnimation);
holder.optionsOverlay.setVisibility(View.GONE);
holder.optionsOverlay.clearAnimation();
}
});
}
private void sendCancelBookingToAPI(String id_booking) throws JsonProcessingException {
BeeWylApiClient.cancelBooking(id_booking, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int i, Header[] headers, byte[] bytes) {
try {
Log.v("xdebug CANCEL", new String(bytes, "UTF_8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) {
Log.v("xdebug CANCEL ERROR", String.valueOf(throwable));
}
});
}
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which){
case DialogInterface.BUTTON_POSITIVE:
Animation hideOverlayAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.booking_overlay_dismiss);
mBookingsListView.getChildAt(mSelectedItem-mBookingsListView.getFirstVisiblePosition()).startAnimation(hideOverlayAnimation);
new Handler().postDelayed(new Runnable() {
public void run() {
try {
sendCancelBookingToAPI(mBookings.get(mSelectedItem).getId());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
mBookings.remove(mSelectedItem);
mSelectedItem = -1;
updateBookingsListView(mBookings);
}
}, hideOverlayAnimation.getDuration());
break;
case DialogInterface.BUTTON_NEGATIVE:
dialog.cancel();
break;
}
}
};
}
}
And the item inflated :
<?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="wrap_content"
android:paddingTop="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
>
<LinearLayout
android:id="#+id/bookingItemLL"
android:layout_width="match_parent"
android:layout_height="151dp"
android:orientation="horizontal"
android:weightSum="100"
android:background="#drawable/product_item_rectangle"
>
<ImageView
android:id="#+id/bookedProductImage"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#android:color/white"
android:src="#drawable/nivea"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical"
>
<TextView
android:id="#+id/bookingProductName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="BRUME NIVEA"
android:textColor="#color/ProductsBlue"
android:textSize="16dp"
android:textStyle="bold"
/>
<TextView
android:id="#+id/bookedProductPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Prix lors de la réservation : 24,90€"
android:textSize="12dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:textColor="#color/ProductsBlue" android:layout_gravity="left"
/>
<TextView
android:id="#+id/bookingShopName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:text="Magasin"
android:textSize="12dp"
android:textColor="#color/ProductsBlue"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:text="Réservé jusqu'au"
android:textSize="12dp"
android:textColor="#color/ProductsBlue" />
<TextView
android:id="#+id/bookingEndDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="-"
android:textSize="12dp"
android:textColor="#color/ProductsBlue" />
</LinearLayout>
</LinearLayout>
<RelativeLayout android:id="#+id/bookingOptionsOverlay"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#EEFFFFFF"
android:visibility="gone">
<ImageView
android:id="#+id/discardOverlay"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:src="#drawable/ic_discard_booking_overlay"
android:padding="5dp"
/>
<Button android:id="#+id/callShop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="APPELER"
android:layout_weight="1"
android:background="#00000000"
android:drawableTop="#drawable/booking_call"
android:textColor="#color/ProductsBlue"
android:textSize="14dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:drawablePadding="20dp"
android:layout_marginLeft="20dp"
/>
<Button android:id="#+id/cancelBooking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ANNULER"
android:layout_weight="1"
android:background="#00000000"
android:drawableTop="#drawable/booking_cancel"
android:textColor="#color/ProductsBlue"
android:textSize="14dp"
android:layout_centerInParent="true"
android:drawablePadding="20dp"
/>
<Button android:id="#+id/routeShop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ITINERAIRE"
android:layout_weight="1"
android:background="#00000000"
android:drawableTop="#drawable/booking_route"
android:textColor="#color/ProductsBlue"
android:textSize="14dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:drawablePadding="20dp"
android:layout_marginRight="20dp"
/>
</RelativeLayout>
</RelativeLayout>
Your problem comes from re-using the convertView.
When the previous item got a click the OnClickListener fired and in there the visibility for the item was set to GONE. Later on this same view got recycled and passed to getView() as the convertView. Because you are re-using it without resetting any changes that were made you are now working with a View for a new item that is not in a known state. You should make sure you undo any changes before using a convertView.
The quick fix is to not re-use the convertView that is passed into getView(). So, in your code where you check if you can re-use the convertView:
if(convertView == null)
Sabotage that check just to see if things start working:
if(true)
If that does the trick you will probably want to fix it properly.
In the else clause of the above check, you are getting the item holder from the tag. Also undo any changes that your OnClickListeners could have made. You want to start with a View for a new item in a known state. You should initialize it explicitly. For example:
if(convertView == null) {
// ... snipped all the initialization ...
} else {
holder = (ViewHolder)convertView.getTag();
convertView.setVisibility(View.VISIBLE);
}
Update
I have never used a 'heterogenous' adapter so I can't really answer why "the convertView is reusing the overlay View instead of my item's root View." The Android developer documentation for Adapter.getView() says about the convertView argument:
The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view. Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int)).
The emphasized bit says that you cannot depend on the system to pass you a convertView of the right type, while the last sentence says the opposite (as I read it).
Basically, I don't know why it's not working. I guess in the test where you check if you must inflate a new view yourself
if(convertView == null)
you should also check if it is the right kind of view:
if(convertView == null || getItemViewTypeFromView(convertView) != type)
Where getItemViewTypeFromView() is something like this:
private int getItemViewTypeFromView(View view) {
switch (view.getId()) {
case R.id.item_layout_root:
return TYPE_ITEM;
case R.id.placeholder_layout_root:
return TYPE_PLACEHOLDER;
default:
throw new UnsupportedOperationException();
}
}
In the item and placeholder layouts, give the root elements an id so you distinguish between them. So something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_layout_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp" >
... snipped the elements that make up the body of the layout ...
</RelativeLayout>
I haven't tried the above, so I hope it works for you.
Good luck!
Please, help me to find a solution how to set my checkboxes visible when I click on menu item "delete" and set invisible when I press "confirm" or "cancel".
I use SimpleCursorAdapter, cuz listview is filled from database with cursor loader...
here is my code:
MainActivity
public class MainActivity extends ActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor>{
final String LOG = "phonebookLogs";
private static final int LOADER_ID = 1;
Intent intentAddContact;
ListView lvMain;
CheckBox cbxList;
MenuItem addContact, deleteContact, settings, expimp, confirmDelete, cancelDelete;
DB mDB;
SimpleCursorAdapter scAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//find ListView
lvMain = (ListView)findViewById(R.id.lvMain);
//calling for creating adapter
onCreateAdapter();
}
public void onCreateAdapter(){
String[] from = new String[] { DB.COL_LINK, DB.COL_SURNAME, DB.COL_NAME };
int[] to = new int[] { R.id.ivPhoto, R.id.tvSurname, R.id.tvName, };
if(to[0] == 0){
to[0] = R.drawable.default_contact;
}
scAdapter = new SimpleCursorAdapter(this, R.layout.item, null, from, to, 0);
lvMain.setAdapter(scAdapter);
// loader for reading data
getSupportLoaderManager().initLoader(LOADER_ID, null, this);
}
public void deleteRecord(){
//when MenuItem "Delete" is clicked
addContact.setVisible(false);
deleteContact.setVisible(false);
settings.setVisible(false);
expimp.setVisible(false);
confirmDelete.setVisible(true);
cancelDelete.setVisible(true);
}
public void onCancelDeleteRecord(){
////when MenuItem "cancel" is clicked
addContact.setVisible(true);
deleteContact.setVisible(true);
settings.setVisible(true);
expimp.setVisible(true);
confirmDelete.setVisible(false);
cancelDelete.setVisible(false);
}
public void confirmDeleteRecord(){
////when MenuItem "confirm" is clicked
addContact.setVisible(true);
deleteContact.setVisible(true);
settings.setVisible(true);
expimp.setVisible(true);
confirmDelete.setVisible(false);
cancelDelete.setVisible(false);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu)
{
//find all MenuItems
addContact = menu.findItem(R.id.addContact);
deleteContact = menu.findItem(R.id.deleteContact);
settings = menu.findItem(R.id.settings);
expimp = menu.findItem(R.id.expimp);
confirmDelete = menu.findItem(R.id.confirmDelete);
cancelDelete = menu.findItem(R.id.cancelDelete);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.addContact:
intentAddContact = new Intent(this, NewContact.class);
startActivity(intentAddContact);
break;
case R.id.deleteContact:
deleteRecord();
break;
case R.id.settings:
break;
case R.id.expimp:
break;
case R.id.confirmDelete:
confirmDeleteRecord();
break;
case R.id.cancelDelete:
onCancelDeleteRecord();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle bndl) {
return new MyCursorLoader(this, mDB);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
scAdapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
scAdapter.swapCursor(null);
}
}
DB.class and CursorLoader.class are fine, simple query and loading.
and my XML-file for ListView
<?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"
android:layout_margin="#dimen/margin"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="#dimen/margin"
android:layout_weight="0.5"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/margin"
android:orientation="vertical" >
<ImageView
android:id="#+id/ivPhoto"
android:layout_width="#dimen/imageWidth"
android:layout_height="#dimen/imageHeight"
android:layout_gravity="center_vertical"
android:contentDescription="#string/photoDescription"
android:src="#drawable/default_contact" />
</LinearLayout>
<TextView
android:id="#+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="#dimen/margin"
android:text=""
android:textSize="#dimen/textSize" />
<TextView
android:id="#+id/tvSurname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="#dimen/margin"
android:text=""
android:textSize="#dimen/textSize" />
</LinearLayout>
<CheckBox
android:id="#+id/cbxList"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="#dimen/margin"
android:layout_weight="0.2"
android:visibility="invisible" />
</LinearLayout>
I have read a lot already, but how to realize it without creating my own adapter still can't get...
will be thanksfull to any help in finding solution!
You can set your checkbox as visible with: cbxlist.setVisibility(View.VISIBLE);
And if you want it completely gone, use (ie make it look as if it was never added): cbxlist.setVisibility(View.GONE);
If you want it to be just invisible, but still occupy the same space: cbxlist.setVisibility(View.INVISIBLE);
Edit:
for(int i = 0; i < lvMain.getChildCount(); i++) {
lvMain.getChildAt(i).findViewById(R.id.cbxList).setVisibility(View.VISIBLE);
}
That should set all of your checkbox's to visible. Similarly call for View.INVISIBLE when required.
If you want to change visibility of each checkbox in your listview (assuming your XML is your list row view), you need to create your custom adapter and override getView method.
In there, you will do the following:
#Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
cBox = (CheckBox ) convertView.findViewById(R.id.cbxList);
cBox.setVisibility(View.GONE);
return convertView;
}
I would like to get a click anywhere on the row of my ListActivity to proceed to another Activity. Currently, i am able to see the row position in a log when the ImageView is clicked, but the click is not registered anywhere else in the row. Furthermore, the Toast command crashes the program. Any assistance toward my stated goal would be much appreciated. Thanks!
event_row.xml:
<?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:contentDescription="#string/event_icon_desc"
android:src="#drawable/event_icon" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="#+id/event_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:textIsSelectable="true" />
<TextView
android:id="#+id/location_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:textIsSelectable="true" />
<TextView
android:id="#+id/date_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:textIsSelectable="true" />
</LinearLayout>
</LinearLayout>
display_events_activity.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"
android:id="#+id/event_ll" >
<ListView
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<TextView
android:id="#+id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="#string/tv_empty" />
</LinearLayout>
DisplayEvents.java:
public class DisplayEvents extends ListActivity {
private ProgressDialog progressDialog = null;
private ArrayList<Event> events = new ArrayList<Event>();
private EventAdapter eventAdapter;
private Runnable viewEvents;
//private final int ITEMS_PER_VIEW = 10;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.disp_events_activity);
new EventTask().execute();
this.eventAdapter = new EventAdapter(this, R.layout.event_row, this.events);
setListAdapter(this.eventAdapter);
viewEvents = new Runnable() {
#Override
public void run() {
getEvents();
}
};
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
private void getEvents() {
runOnUiThread(adaptEvents);
}
private Runnable adaptEvents = new Runnable() {
#Override
public void run() {
if (events != null && events.size() > 0) {
eventAdapter.notifyDataSetChanged();
for (int i=0; i<events.size(); i++)
eventAdapter.add(events.get(i));
}
progressDialog.dismiss();
eventAdapter.notifyDataSetChanged();
}
};
private class EventAdapter extends ArrayAdapter<Event> {
private ArrayList<Event> events;
public EventAdapter(Context context, int textViewResourceId, ArrayList<Event> events) {
super(context, textViewResourceId, events);
this.events = events;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.event_row, null);
}
Event event = events.get(position);
if (event != null) {
TextView event_tv = (TextView) v.findViewById(R.id.event_tv);
TextView loc_tv = (TextView) v.findViewById(R.id.location_tv);
TextView date_tv = (TextView) v.findViewById(R.id.date_tv);
if (event_tv != null)
event_tv.setText("Event: " + event.getEventName());
if (loc_tv != null)
loc_tv.setText("Location: " + event.getPlace() + " - " + event.getCity());
if (date_tv != null)
date_tv.setText("Date: " + event.getDate());
}
v.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
Log.v("text", "Image clicked, row %d"+position);
Toast.makeText(DisplayEvents.this, position, Toast.LENGTH_LONG).show();
}
});
return v;
}
}
public class EventTask extends AsyncTask<Void, Void, ArrayList<Event>> {
#Override
protected ArrayList<Event> doInBackground(Void... Void) {
ArrayList<Event> dbEvents = new ArrayList<Event>();
BuyTicketsConnection buyConnection = new BuyTicketsConnection();
dbEvents = buyConnection.getEventsList();
return dbEvents;
}
protected void onPostExecute(ArrayList<Event> dbEvents) {
events = dbEvents;
Thread thread = new Thread(null, viewEvents, "GetEventsBackground");
thread.start();
progressDialog = ProgressDialog.show(DisplayEvents.this, "Please wait...", "Retrieving events...", true);
}
}
}
EDIT:
I've now got this method:
#Override
protected void onListItemClick (ListView l, View v, int position, long id){
Log.v("text", "Image clicked, row %d"+position);
Toast.makeText(DisplayEvents.this, position, Toast.LENGTH_LONG).show();
}
in my DisplayEvents (ListActivity) class, but still no dice. Also, i'd like the click to respond to anything in that list item (note there are many views in that row layout which should inherit the click event handler).
it seems that the issue is how i should get all of the views (ImageView and 3 TextViews) in event_row.xml to get associated with this OnListItemClick handler. how is this entire row (comprised of 4 views in 2 LinearLayouts) recognized in the context of that OnListItemClick handler?
Use setOnItemClickListener on your DisplayEvents Activity and remove setOnClickListener from your EventAdapter.
The problem is that some of your item views are focusable and/or clickable (thus they eat the click events and that causes the ListView to not be able to receive the event in order to know that you clicked on a list item). For a list to trigger item-click events, it must be able to receive the touch events without them being eaten by the children.
In your item layout, you have 3 TextViews that all have textIsSelectable=true, and that means the TextView itself will eat the touch events. You need to disable that in order to trigger the onListItemClick() events.
I encountered this problem and removing
android:textIsSelectable="true"
will solve it.