I have created a simple list view with two different text views (shown below). I want to have a series of rows that will display more information when clicked--one text view hidden that can be toggled by clicking the other text view. I think my problem might be the linear layout encapsulating the two text views, but I'm not sure. The slide_up and slide_down animations were basic ones taken from a website.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:textSize="20sp"
android:clickable="true"
android:onClick="toggle_contents"/>
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="example"
android:textSize="20sp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"/>
</LinearLayout>
The list view is populated from a content provider as follows:
cursor.moveToFirst();
ArrayList<String> listItems = new ArrayList<String>();
ArrayList<String> listItems2 = new ArrayList<String>();
// listItems = new ArrayList<ListItem>();
ListView mylist=(ListView) findViewById(R.id.list);
do{
String id=cursor.getString(cursor.getColumnIndex("id"));
String name=cursor.getString(cursor.getColumnIndex("name"));
// String name_str = id +" : "+ name;
listItems2.add(id);
listItems.add(name);
cursor.moveToNext();
}while(!cursor.isAfterLast());
CustomListAdapter adapter=new CustomListAdapter(this, listItems2, listItems);
mylist.setAdapter(adapter);
I want to toggle between visible and invisible with the slide up and slide down animations:
public void toggleContents(View v){
if(txt_hidden.isShown()){
slide_up(this, txt_hidden);
txt_hidden.setVisibility(View.GONE);
}
else{
txt_hidden.setVisibility(View.VISIBLE);
slide_down(this, txt_hidden);
}
//txt_hidden.setVisibility(txt_hidden.isShown()? View.GONE : View.VISIBLE);
}
#Override
public void onDestroy() {
super.onDestroy();
}
public static void slide_down(Context ctx, View v){
Animation a = AnimationUtils.loadAnimation(ctx, R.anim.slide_down);
if(a != null){
a.reset();
if(v != null){
v.clearAnimation();
v.startAnimation(a);
}
}
}
public static void slide_up(Context ctx, View v){
Animation a = AnimationUtils.loadAnimation(ctx, R.anim.slide_up);
if(a != null){
a.reset();
if(v != null){
v.clearAnimation();
v.startAnimation(a);
}
}
}
How do you set the txt_hidden variable? I believe it might be pointing to a view that is different than the one you are actually trying to change the visibility for.
I would suggest that you don't set the click listener in the xml file, but rather in getView() of your adapter when you are building your list item view:
private class ViewHolder
{
TextView mTextView1;
TextView mTextView2
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.list_view_item_1, parent, false);
viewHolder = new ViewHolder();
viewHolder.mTextView1 = (TextView)convertView.findViewById(R.id.text1);
viewHolder.mTextView2 = (TextView)convertView.findViewById(R.id.text2);
viewHolder.mTextView1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// do your animation here
}
});
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolder)convertView.getTag();
}
viewHolder.mTextView1.setText(mElements1.get(position));
viewHolder.mTextView2.setText(mElements2.get(position));
return convertView;
}
add tag to the view that you set OnClickListener() to, for using animations on another view
...
view.setOnClickListener(this);
view.setTag(textViewToShowOnClick);
...
public void onClick(View v){
...
TexView textViewToShowOnClick = (TextView) v.getTag();
v.slideUp(...);
...
}
Related
I am having a problem with a clickListener on my gridview. The LongClickListener works without issue. But I cannot seem to get any response from the click Listener.
My code is below.
Im confused as to why the long click works but not the normal click,
Any pointers would be appreciated
Thanks
final GridView gridView = (GridView) findViewById(R.id.grid_view);
gridView.setNumColumns(numOfColumns);
gridView.getLayoutParams().width = (CELL_WIDTH * numOfColumns);
gridView.getLayoutParams().height = (CELL_WIDTH * numOfRows);
....
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Log.d("ABCD", "Position Single Click is " + position);
// Ideally in here I want to put to open a soft keyboard for the user to enter a value
// InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
// imm.showSoftInput(gridView, InputMethodManager.SHOW_IMPLICIT);
}
});
gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("ABCD", "Position Long Click is " + position);
return true;
}
});
grid_view is
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal">
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_grid_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"/> <<--- I WANT THIS TO GET THE CLICK
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
</LinearLayout>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listId"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp" />
</LinearLayout>
GridCell in the grid view is
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:padding="0dp" android:layout_margin="0dp"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
>
<TextView
android:id="#+id/grid_item_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="1dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:textSize="10px"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
>
</TextView>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/grid_item_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#+id/celllabel"
android:background="#android:color/transparent"
android:paddingLeft="5dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:layout_margin="0dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"
android:cursorVisible="false">
</EditText>
</RelativeLayout>
The adapter class has a getView and is as below
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
MyObject obj = myObjects.get(position);
if (convertView == null) {
gridView = inflater.inflate(R.layout.grid_cell, null);
String textColour = "#000000";
TextView textView = (TextView) gridView.findViewById(R.id.grid_item_label);
textView.setText(Html.fromHtml(String.format("<font color='%s'>%s</font>", textColour, obj.getValue())));
TextView superScriptTv = (TextView) gridView.findViewById(R.id.grid_item_number);
if (obj.getNumber() > 0) {
superScriptTv.setText(Html.fromHtml(String.format("<font>%s</font>", cell.getNumber())));
}
} else {
gridView = convertView;
}
gridView.setBackgroundColor(obj.getBackgroundColour());
return gridView;
}
EDIT
Really banging my head against a wall here now :)
Im updating the code sample so have more data. Ive noticed that in my adapter if I do not set the text on the textview with ID = R.id.grid_item_number then it works. As soon as I set text on it then I lose the click listener.
The linked question/answer doesnt help from what I can see. Can anyone help with my stupidity?
EDIT
Adapter code has been added.
Thanks in advance.
The problem is with the EditText inside the row_cell. When you click on the item, it takes focus and prevents the whole item to be clickable again. As you noticed, only long click works.
Here you have a similar problem.
To resolve that issue I would move your OnItemClickListeners from the Activity / Fragment to your GridViewAdapter, so instead of:
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Log.d("ABCD", "Position Single Click is " + position);
// Ideally in here I want to put to open a soft keyboard for the user to enter a value
// InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
// imm.showSoftInput(gridView, InputMethodManager.SHOW_IMPLICIT);
}
});
gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Log.d("ABCD", "Position Long Click is " + position);
return true;
}
});
I would do something like that:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridViewItem;
if (convertView == null) {
gridViewItem = new View(mContext);
gridViewItem = layoutInflater.inflate(R.layout.grid_cell, null);
TextView textView = (TextView)gridViewItem.findViewById(R.id.grid_item_number);
textView.setText(mValues[position]);
EditText editText = (EditText)gridViewItem.findViewById(R.id.grid_item_label);
} else {
gridViewItem = (View) convertView;
}
gridViewItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("GRID", "onClick: " );
}
});
gridViewItem.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Log.e("GRID", "onLongClick: " );
return true;
}
});
return gridViewItem;
}
It will prevent this strange behaviour you are struggling right now.
For the sake of that example please find my code below:
MainActivity layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="io.github.mmbs.gridviewcheck.MainActivity"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp">
<GridView
android:id="#+id/gridView"
android:numColumns="auto_fit"
android:columnWidth="100dp"
android:stretchMode="columnWidth"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true">
</GridView>
</android.support.constraint.ConstraintLayout>
Grid cell layout:
<?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:layout_margin="0dp"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:padding="8dp">
<TextView
android:id="#+id/grid_item_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="0dp"
android:paddingLeft="1dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:textSize="20sp"
android:text="TEXTVIEW">
</TextView>
<EditText
android:id="#+id/grid_item_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:background="#android:color/transparent"
android:cursorVisible="false"
android:layout_below="#id/grid_item_number"
android:text="EDITTEXT">
</EditText>
</RelativeLayout>
Please take into consideration, that I removed all layouts attributes responsible for focusability.
MyGridAdapter:
public class MyGridViewAdapter extends BaseAdapter {
private Context mContext;
private final String[] mValues;
public MyGridViewAdapter(String[] values, Context context) {
mValues = values;
mContext = context;
}
#Override
public int getCount() {
return mValues.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridViewItem;
if (convertView == null) {
gridViewItem = new View(mContext);
gridViewItem = layoutInflater.inflate(R.layout.grid_cell, null);
TextView textView = (TextView)gridViewItem.findViewById(R.id.grid_item_number);
textView.setText(mValues[position]);
EditText editText = (EditText)gridViewItem.findViewById(R.id.grid_item_label);
} else {
gridViewItem = (View) convertView;
}
gridViewItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("GRID", "onClick: " );
}
});
gridViewItem.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Log.e("GRID", "onLongClick: " );
return true;
}
});
return gridViewItem;
}
}
If you like, I can also share this code on github, so you will have a fully operational example.
The second option is to leave your implementation as it is and do hacks when the EditText is focusable and enabled. Here you have related topics:
Android: Force EditText to remove focus?
ListView items focus behaviour
Focusable EditText inside ListView
Android: Force EditText to remove focus?
What is more, please take into account that answer:
Do not use clickable objects in the grid. In that case Android cannot handle the click event of GridView.
Instead, use something to show a similar user interface view. Then handle that object's click actions.
Don't: put Button in the GridView to perform some click actions.
Do: put an ImageView instead of ImageButton and handle ImageView's click events.
Edit: Please find a link to the project on my Github.
use gridView.setOnItemClickListener(.....) and to your root view add below line
android:descendantFocusability="blocksDescendants"
The ViewGroup will block its descendants from receiving focus.
Try to add
android:focusable="false"
android:focusableInTouchMode="false"
in your GridCell -->TextView
Try using recycler view if you are comfortable with it.
It doesn't gives any such problem in addition to this it has its own advantages.
Follow the link for complete explanation.
If you have any focus-able view in you your row layout, then the onItemClickListener will not be called. For this problem you need to customize your getView code and set onClickListener() on convertView and pass the callback to activity using the Interface. I have updated the code of your Adapter as below. Now you need to implement the GridViewItemClickListener on your activity and pass the instance while creating the Adapter instance.
public class MyGridViewAdapter extends BaseAdapter {
private Context mContext;
private final String[] mValues;
private GridViewItemClickListener mListener;
public MyGridViewAdapter(String[] values, Context context,GridViewItemClickListener listener ) {
mValues = values;
mContext = context;
mListener = listener;
}
#Override
public int getCount() {
return mValues.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridViewItem;
if (convertView == null) {
gridViewItem = new View(mContext);
gridViewItem = layoutInflater.inflate(R.layout.grid_cell, null);
TextView textView = (TextView)gridViewItem.findViewById(R.id.grid_item_number);
textView.setText(mValues[position]);
EditText editText = (EditText)gridViewItem.findViewById(R.id.grid_item_label);
} else {
gridViewItem = (View) convertView;
}
gridViewItem.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Log.e("GRID", "onLongClick: " );
return true;
}
});
//Add an OnclickListener here
gridViewItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mListener != null){
mListener.onGridItemClick(v, position);
}
}
});
return gridViewItem;
}
public interface GridViewItemClickListener{
void onGridItemClick(View v, int index);
}
}
You can user click listeners inside the adapter it will work ,it should work like this
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
MyObject obj = myObjects.get(position);
if (convertView == null) {
gridView = inflater.inflate(R.layout.grid_cell, null);
String textColour = "#000000";
TextView textView = (TextView) gridView.findViewById(R.id.grid_item_label);
textView.setText(Html.fromHtml(String.format("<font color='%s'>%s</font>", textColour, obj.getValue())));
TextView superScriptTv = (TextView) gridView.findViewById(R.id.grid_item_number);
superScriptTv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// here you can use every item of grid acc to position
}
});
if (obj.getNumber() > 0) {
superScriptTv.setText(Html.fromHtml(String.format("<font>%s</font>", cell.getNumber())));
}
} else {
gridView = convertView;
}
gridView.setBackgroundColor(obj.getBackgroundColour());
return gridView;
}
how can the click listener work if you set the root element in GridCell.java as non-clickable ?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:padding="0dp" android:layout_margin="0dp"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
Remove those lines from each of your components in the GridCell.java
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
I don't think this is required for GridView, but sometimes for RecyclerView it is required to have android:clickable="true" on the root componenent of GridCell.java
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!
I have a list view every item in list contains textviews and button what I want is when I click on the button of any item in list I want to printout the position of button if i clicked the button in first line i want to print out 0 and go on but it doesn't work
this is my method to display view
private void displayListView() {
Cursor cursor = dbHelper.fetchAllCountries();
// The desired columns to be bound
String[] columns = new String[] {
PhonesDbAdapter.KEY_NAME,
PhonesDbAdapter.KEY_CONTINENT,
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.continent,
R.id.name
};
// create the adapter using the cursor pointing to the desired data
//as well as the layout information
dataAdapter = new SimpleCursorAdapter(
this, R.layout.phone_layout,
cursor,
columns,
to,
0);
listView = (ListView) findViewById(R.id.listView1);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
}
public void print(View v)
{
}
and here how my item look like
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dip"
android:background="#f0f0f0" >
<TextView
android:id="#+id/continent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="#275a0d"/>
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="TextView"
android:textColor="#000"/>
<Button
android:id="#+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="#drawable/ic_launcher"/>
<Button
android:id="#+id/call"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="#+id/button1"
android:background="#drawable/ic_launcher"
android:onClick="print"/>
</RelativeLayout>
and this the layout that contain the listview
<?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"
android:background="#f0f0f0">
<EditText android:id="#+id/myFilter" android:layout_width="match_parent"
android:layout_height="wrap_content" android:ems="10">
</EditText>
<ListView android:id="#+id/listView1" android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
i spent alot of time with this problem and i hope that u can help me and im really sorry for my bad english
Are you implementing the method getView() on your adapter?
That's where you want to add the OnClickListener to your Button
Once you do that you can set the position as the tag of the Button and retrieve it on the onClick method.
Something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.your_item_layout, parent, false);
Button myButton = (Button) row.findViewById(R.id. call);
myButton.setOnClickListener(new OnClickListener {
#Override
public void onClick(View view) {
int position = (int)view.getTag();
//Do whatever you want with the position here
}
});
myButton.setTag(position);
return row;
}
From last 3 three years I am not working on android so I am not sure my suggestion is correct or not. You need to test it for you purpose.
In your above case you cannot use onItemClickListner because it can only be used when you are tracking your complete list row. What I think is you can create a custom adapter and override its getView method. In getView method you will have your position and you can set button click listner for you button in getview. So In this way you can get your button position.
You try it once if you need some code then I can try to write it for you...
THIS IS HOW YOU CAN CREATE CUSTOM ADAPTER.
public class my_custom_adapter extends ArrayAdapter<String> {
private Context context = null;
ArrayList<String> elements = null;
public my_custom_adapter(Context context, int type, ArrayList<String> elements)
{
super(context, type, elements);
this.elements = elements;
this.context = context;
}
//THIS IS SIMPLY A CLASS VIEW WILL HOLD DIFFERENT VIEWS OF YOUR ROW.
static class ViewHolder
{
public TextView tv;
public Button cb;
}
#Override
public View getView (final int position, View convertView, ViewGroup parent)
{
View rowView = convertView;
ViewHolder holder = null;
if (rowView == null) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// HERE I AM INFLATING LISTVIEW LAYOUT.
rowView = inflater.inflate(R.layout.inflated_layout, null, false);
holder = new ViewHolder();
holder.cb = (Button) rowView.findViewById(R.id.checkBox1);
holder.tv = (TextView) rowView.findViewById(R.id.textView1);
rowView.setTag(holder);
} else {
holder = (ViewHolder) rowView.getTag();
}
if (holder != null) {
holder.cb.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// IF YOUR BUTTON TAG HERE AND YOU CAN HAVE POSITION USING "position" PARAMETER
}
});
}
return rowView;
}
}
I am trying to show a list that when I pressed it in that position I would like to show an image that is gone and to change the typeface of that row. So first I have a xml file with a simple list and then another xml for each row, this one:
<ImageView
android:id="#+id/icono"
android:layout_gravity="center"
android:layout_marginRight="5dip"/>
<RelativeLayout
android:id="#+id/RelativeLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:text="TextView" />
<ImageView
android:id="#+id/image_tick_ingred"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="false"
android:layout_alignParentLeft="false"
android:layout_alignParentRight="true"
android:layout_alignParentTop="false"
android:layout_alignRight="#id/textView1"
android:layout_centerVertical="true"
android:src="#android:drawable/presence_online" />
</RelativeLayout>
So that is the code to each row, contains an image on the left then a text and finally another image on the right of the row. And the java code:
public class TabIngred extends ListActivity {
ImageView img;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.rellenas_ingr); //the list xml layout
Bundle extras = getIntent().getExtras(); //get data
if (extras != null) {
String ingredientesLista[] = extras.getStringArray("ingredientesLista");
int[] ingredientesImagen=extras.getIntArray("ingredientesImagen");
setListAdapter (new IngredienteAdapter(this, R.layout.rellenas_ingr_fila, ingredientesLista, ingredientesImagen));
getListView().setChoiceMode(2);
}
}
private class IngredienteAdapter extends ArrayAdapter<String>{
private String listaIngred[];
private int[] listaImagenes;
public IngredienteAdapter(Context context, int textViewResourceId, String ingredientesLista[], int[] ingredientesImagen) {
super(context, textViewResourceId, ingredientesLista);
this.listaIngred = ingredientesLista;
this.listaImagenes=ingredientesImagen;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// XML de la vista de la fila
v = vi.inflate(R.layout.rellenas_ingr_fila, null); //the row for the listview
}
String ingred = listaIngred[position];
int imagen=listaImagenes[position];
int tickImagen = R.drawable.tick_ingred;//set the image to a tick
if (ingred != null) {
TextView ttitulo = (TextView) v.findViewById(R.id.textView1);
if (ttitulo != null) {
ttitulo.setText(ingred);
}
ImageView timagen = (ImageView) v.findViewById(R.id.icono);
if (timagen != null) {
timagen.setImageResource(imagen);
}
ImageView ttick = (ImageView) v.findViewById(R.id.image_tick_ingred);
if (ttick != null) {
ttick.setImageResource(tickImagen);
ttick.setVisibility(View.GONE);
}
}
return v;
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
img=(ImageView)v.findViewById(R.id.image_tick_ingred);
TextView t= (TextView)v.findViewById(R.id.textView1);
if(img.getVisibility()==View.GONE){
t.setTypeface(null, Typeface.BOLD);
img.setVisibility(View.VISIBLE);
}else {
t.setTypeface(null, Typeface.NORMAL);
img.setVisibility(View.GONE);
}
}
}
So, I define the getview(), I put the image on gone (thats works), but then the OnlistItemClick() doesn´t work. First when I selected an Item the image isn´t visible and apart for that indefferently the position I pressed the first row is which change the typeface, not the item what I pressed.
Thanks :)
in you onListItemClick you need to use the View
for example:
img=(ImageView)findViewById(R.id.image_tick_ingred);
TextView t= (TextView)findViewById(R.id.textView1);
should be
img=(ImageView)v.findViewById(R.id.image_tick_ingred);
TextView t= (TextView)v.findViewById(R.id.textView1);
notice the v that is the view of the row clicked
The problem was that I define getListView().setChoiceMode(2); and that was because it doesn´t work, I delete the line and works perfect!!
Thanks after all!
I am using a ListView with a custom adapter for displaying items as pairs of title/subtitle TextViews.
Unfortunately, I can select an item just by clicking on its upper half, the one occupied by the title
Here is the code I am using:
journal_list.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawSelectorOnTop="false"
android:background="#fff"
android:cacheColorHint="#fff"
android:paddingBottom="6dp"
/>
journal_list_item.xml for the list item's layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
>
<TextView
android:id="#+id/journal_entry_date"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="#000"
android:textSize="18sp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
/>
<TextView
android:id="#+id/journal_content"
android:paddingLeft="28dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textSize="16sp"
android:textColor="#555"
android:inputType="textMultiLine"
android:maxLines="2"
android:minLines="1"
android:layout_below="#id/journal_entry_date"
android:layout_alignParentLeft="true"
/>
Also the code for the adapter:
private class JournalAdapter extends ArrayAdapter<JournalEntry> {
Context ctxt;
public JournalAdapter(Context ctxt) {
super(ctxt, R.layout.journal_list_item);
this.ctxt = ctxt;
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
JournalHolder holder;
if (row == null) {
row = ((LayoutInflater)ctxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).
inflate(R.layout.journal_list_item, parent, false);
holder = new JournalHolder(row);
row.setTag(holder);
} else {
holder = (JournalHolder) row.getTag();
}
JournalEntry crt = getItem(position);
holder.getDate().setText(crt.dateWritten);
holder.getContent().setText(crt.content);
return row;
}
}
private static class JournalHolder {
private TextView date;
private TextView content;
private View base;
public JournalHolder(View base) {
this.base = base;
}
public TextView getDate() {
if (date == null)
date = (TextView) base.findViewById(R.id.journal_entry_date);
return date;
}
public TextView getContent() {
if (content == null)
content = (TextView) base.findViewById(R.id.journal_content);
return content;
}
}
Code from the ListActivity's onCreate() method:
private JournalAdapter adapter;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.journal_list);
adapter = new JournalAdapter(this);
setListAdapter(adapter);
registerForContextMenu(getListView());
}
Also, I am calling a method called updateList() in onResume()
private void updateList() {
adapter.clear();
Cursor cursor = helper.listJournal(start, end);
cursor.moveToFirst();
JournalEntry crt;
while (!cursor.isAfterLast()) {
crt = new JournalEntry();
crt.dateWritten = cursor.getString(cursor.getColumnIndex("date_written"));
crt.content = cursor.getString(cursor.getColumnIndex("content"));
crt.id = cursor.getInt(cursor.getColumnIndex("entry_id"));
adapter.add(crt);
cursor.moveToNext();
}
cursor.close();
adapter.notifyDataSetChanged();
}
List item clicking is handled in the onListItemClick of my ListActivity, as follows:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = new Intent(this, JournalEditor.class);
intent.setAction(Intent.ACTION_EDIT);
intent.putExtra("entry_id", adapter.getItem(position).id);
startActivity(intent);
}
Apparently, I am forgetting something important, and that's why this whole weird thing happens.
I have found a similar discussion here: http://groups.google.com/group/android-developers/browse_thread/thread/5636c8ea74033657 but it wasn't very helpful.
The problem may be from the clicking of the textviews. When you set the views of the listview items as clickable, they override the click of the listview item and the click will only work when the specific view of the item is clicked.
If this is the case, do the following for your two textviews in the getView() function:
TextView tv;
tv.setFocusable(false);
tv.setClickable(false);
Then set the listview to clickable: listView1.setClickable(true);