I have a ListView whose rows comprise of some TextViews and a button. Upon a user pressing the button, I want to remove that parent that houses the button from the ListView. How do I access my custom ArrayAdapter's fields from within a nested method (onClickListener) though? All I have to work with is the View v. Am I suppose to call v.getParent() multiple times, or is there a better way to do it?
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Action item = this.getItem(position);
if (convertView == null) {
convertView = inflater.inflate(R.layout.action_holder_layout,
parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView
.findViewById(R.id.action_holder_title);
holder.finishBtn = (Button) convertView
.findViewById(R.id.finish_action_button);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
holder.title.setText(item.getActionName());
holder.finishBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//REMOVE THE ACTION FROM THE ADAPTER'S ARRAYLIST
}
});
return convertView;
}
static class ViewHolder {
private TextView title;
private Button finishBtn;
}
Make item final:
final Action item = this.getItem(position);
and you can access it from onClick. Not a beautiful solution IMO, but will work:
remove(item);
If you need access to your ListView (and consequently its Adapter), a better way than using getParent().getParent()... would be to use the setTag() method on your View. Since you are already using it to insert a ViewHolder as a Tag of your view, why not add another field
ListView parentListView;
to your ViewHolder and retrieve it later in the onClick?
You could also set the ListView directly as a tag on your button.
You could also just access the adapter or other methods and fields directly from your onClick code since you're using an anonymous class.
Related
What is best practice to show dialog (like date picker) on control thats in Listview view?
Is it OK to create custom control that embeds dialog logic and place it into Listview views?
EDIT:
For example:
I have textEdit that is placed in Listview. I want to show date picker when user click the textEdit. I want to know what is the best place to put dialog logic to.
You can use setOnItemClickListener in your adapter logic for the ListView.
You then need to create an instance of OnItemClickListener and override the OnClick() method and perform your click logic there. Below is a very simple example to demonstrate this concept.
public class MySimpleListAdapter implements ListAdapter {
// Set your constructors and so on
. . .
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.simple_row_layout, parent, false);
viewHolder = new ViewHolder();
viewHolder.textView = (TextView) convertView.findViewById(R.id.simple_text_view);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (adapterData != null) {
viewHolder.textView.setText(item.getTaskTitle()); // This is logic from my SQ Lite database
viewHolder.textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Implement your logic for launching a DialogFragment here
}
});
}
return convertView;
}
}
Then finally, in your Activity where you're displaying the ListView, just set the MySimpleListAdapter to your ListView and you should be good to go.
My code above uses a TextView widget instead of an EditText, but it should be simple enough to replace it with the EditText widget.
I have created a ListView and its custom Adapter. But due to some reason I am not allowed to get items from ViewHolder.
In my case ViewHolder has only one variable and that is of LinearLayout. LinearLayout contains the other child views(which is decided and created at run time). When I use ViewHolder and set the tag of holder object, on scroll I am getting the same views again.
Is there any other way to stop adapter to create views while scrolling ?
Or, while scrolling how can we clear the references of views ?
I have find this but I don't think this will work.
setRecyclerListener(new RecyclerListener() {
#Override
public void onMovedToScrapHeap(View view) {
//from here can we use this to clean the memory
}
});
ViewHolder is meant as a holder to contain ids of listitem layout.
It is optimization to avoid calling findViewById everytime new listitem is created for display by going through data container e.g. arrayList.
You cannot stop adapter in between creating item views.
Only items on display are created.
convertView acts as object being recycled for creating subsequent view while scrolling up/down.
You will not be able to use view holder for the purpose you are trying to achieve.
Sample usage as below.
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
ViewHolder viewHolder = null;
if(convertView == null)
{
v = LayoutInflater.from(StockDetailsActivity.this).inflate(R.layout.stock_details_list_item, null);
viewHolder = new ViewHolder();
viewHolder.model_name_tv = (TextView) v.findViewById(R.id.model_name);
viewHolder.model_type_iv = (ImageView) v.findViewById(R.id.model_type_icon);
viewHolder.model_type_tv = (TextView) v.findViewById(R.id.model_type_desc);
viewHolder.model_stock_tv = (TextView) v.findViewById(R.id.model_stock_value);
v.setTag(viewHolder);
}
else
viewHolder = (ViewHolder) v.getTag();
stockCursor.moveToPosition(position);
// logic to update data to views as appropriate goes here
return v;
}
public class ViewHolder{
public TextView model_name_tv;
public ImageView model_type_iv;
public TextView model_type_tv;
public TextView model_stock_tv;
}
I am using Custom ListViewAdapter for displaying, well, a list.
Each row in the list has 3 buttons, i.e. listeners attached.
But I am finding it very disturbing, that during each scroll the new OnClickListeners are being created, even for those rows, where convertView exists, as a non-null value.
// The most common approach to convert view, as I understand:
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.listview_item, parent, false);
} else {
view = convertView;
}
final TextView textView = (TextView) view.findViewById(R.id.txtProduct);
textView.setText(name);
textView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
...
}
});
... two more listeners, with the same approach
return view;
as per my experience with Java, Spider-sense "ting-a-lings" - seems that creating and throwing away the same listener approach is garbage-collector abusing.
I am not sure when the old listener had been collected, if it had...
Is there a way to use the old listener, instead of creating a new one? (some kind of cache data structure)
You can set your List Adapter class to inherit from the View.OnClickListener interface. Then, simply set
textView.setOnClickListener(this);
And handle the click in your adapter class' onClick method. To know, for example, which row is clicked, add this line prior to the one above:
textView.setTag(position);
Then, in onClick, you can know which position in the list you are handling by getting this tag:
public void onClick(View v) {
Object item = myList.get((Integer) v.getTag());
//handle click event
}
i have a ListView with a onClicklListener.
The ListView has a row Layout of say /res/listitem_a
now after an onClickevent of the any listitem , i want to change the layout of
only that listitem to say /res/listitem_b..
any help on how shall i proceed.
Use BaseAdapter and modify in getView call.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.custom_layout, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// Change text size
holder.text.setTextAppearance(context,R.style.customStyle);
return convertView;
}
static class ViewHolder {
TextView text;
}
And you can use position variable in getView call to change specific row. Hope this help!!!
You can use ViewFlipper as layout of the rows. With ViewFlipper you can specify as many layouts as you want and flip among them when something happen (like a click event). Here is a good tutorial about ViewFlipper.
Moreover, you should implement a custom adapter, extending BaseAdapter, and overriding the getView method.
#Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.your_row_layout, null); //this will inflate the layout into each row
}
//from here on, assign the information to display to the layout widgets
Hope I've helped you.
I am using a customised BaseAdapter to display items on a ListView. The items are just strings held in an ArrayList.
The list items have a delete button on them (big red X), and I'd like to remove the item from the ArrayList, and notify the ListView to update itself.
However, every implementation I've tried gets mysterious position numbers given to it, so for example clicking item 2's delete button will delete item 5's. It seems to be almost entirely random.
One thing to note is that elements may be repeated, but must be kept in the same order. For example, I can have "Irish" twice, as elements 3 and 7.
My code is below:
private static class ViewHolder {
TextView lang;
int position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.language_link_row, null);
holder = new ViewHolder();
holder.lang = (TextView)convertView.findViewById(R.id.language_link_text);
holder.position = position;
final ImageView deleteButton = (ImageView)
convertView.findViewById(R.id.language_link_cross_delete);
deleteButton.setOnClickListener(this);
convertView.setTag(holder);
deleteButton.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.lang.setText(mLanguages.get(position));
return convertView;
}
I later attempt to retrieve the deleted element's position by grabbing the tag, but it's always the wrong position in the list. There is no noticeable pattern to the position given here, it always seems random.
// The delete button's listener
public void onClick(View v) {
ViewHolder deleteHolder = (ViewHolder) v.getTag();
int pos = deleteHolder.position;
...
...
...
}
I would be quite happy to just delete the item from the ArrayList and have the ListView update itself, but the position I'm getting is incorrect so I can't do that.
Please note that I did, at first, have the deleteButton clickListener inside the getView method, and used 'position' to delete the value, but I had the same problem.
Any suggestions appreciated, this is really irritating me.
You have to set the position each time. Your implementation only sets the position on the creation of the view. However when the view is recycled (when convertView is not null), the position will not be set to the correct value.
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.language_link_row, null);
holder = new ViewHolder();
holder.lang = (TextView)convertView.findViewById(R.id.language_link_text);
final ImageView deleteButton = (ImageView)
convertView.findViewById(R.id.language_link_cross_delete);
deleteButton.setOnClickListener(this);
convertView.setTag(holder);
deleteButton.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.lang.setText(mLanguages.get(position));
holder.position = position;
return convertView;
}
you need to implement OnItemClickListener interface, and delete the item in the onItemClick method, one parameter of the method is the position.
My final solution was to use the accepted answer by Greg and the following:
Store the holders in a HashMap, with the item positions as the keys (this is initialised as empty in the constructor)
private HashMap mHolders;
Use this as the onClickListener method:
public void onClick(View v) {
ViewHolder deleteHolder = (ViewHolder) v.getTag();
int pos = deleteHolder.position;
mHolders.remove(pos);
ViewHolder currentHolder;
// Shift 'position' of remaining languages
// down since 'pos' was deleted
for(int i=pos+1; i<getCount(); i++){
currentHolder = mHolders.get(i);
currentHolder.position = i-1;
}
mLanguages.remove(pos);
notifyDataSetChanged();
}
[Please excuse the weird formatting. The code embedding isn't working properly]