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.
Related
What do I want to achieve?
In the below SS, when user touches 'vote' button, these vertical progress bars (custom) will be set according to the voting percentages retrieved from server for that particular row.
What is the obstacle?
I have onClickListener inside getView of the CustomAdapter, and when I manipulate the ProgressBar instance (which is in ViewHolder Class), supposingly I want to see the updated ProgressBar on ONLY the one row of the listview that has triggered that action, but, I see every once 3 rows that I scroll down.
Example: I clicked first row, so first row has updated its progress bar, but 4th, 7th, 10th... rows are also updated EVEN IF I don't touch 'vote button'.
My Guessing
I think this problem is related to recycling the view, the weird number is 3 in this case but when I make rows smaller it goes '4', so that is the only clue I have.
SS & Codes
ScreenShot: bit.ly/sofscreenshot
Code:
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = mInflater.inflate(layout, null);
holder = new ViewHolder();
//some more initialization
holder.pb1 = (ProgressBar) convertView.findViewById(R.id.leftProgress);
holder.pb2 = (ProgressBar) convertView.findViewById(R.id.rightProgress);
holder.leftVoteButton = (Button) convertView.findViewById(R.id.leftButton);
holder.rightVoteButton = (Button) convertView.findViewById(R.id.rightButton);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
/Some codes...
holder.pb1.setProgress(50);
holder.pb2.setProgress(50);
}
});
}
private class ViewHolder {
//some more objects
ProgressBar pb1;
ProgressBar pb2;
Button leftVoteButton;
Button rightVoteButton;
}
All the answers and comments are appreciated, have a great day and thank you.
You're doing it wrong.
The problem is that you need to have a Model somewhere, and change its status. Then the view is updated regarding the model status.
For example, let's say that this is a "StackOverflow" app, and you have a list of answers. The user upvote the second answer. This means that the second element of the List is upvoted.
Now what?
When the adapter is going through your list of object it will "fire" the getView method for that position. Then you have to update that position according to your model. So, if the position is 1, the adapter is trying to show the second Answer, and you have to set the button to "upvoted". Otherwise you have to set it as "normal".
private List<Answer> answers;
public View getView(int position, View convertView, ViewGroup parent) {
// here get your view (or initialize it)
// get the matching answer
Answer answer = answers.get(position);
if(answer.isUpvoted()) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
} else {
holder.pb1.setVisibility(View.INVISIBLE);
holder.pb2.setVisibility(View.INVISIBLE);
}
holder.leftVoteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.pb1.setVisibility(View.VISIBLE);
holder.pb2.setVisibility(View.VISIBLE);
// not sure on how to get this answer here
// you probably have to go "upper" and manage the click from the ListView
answer.setUpvoted(true);
}
});
}
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;
}
Im trying to lock button after user click button on costume list.
the item of list fetched from hayoola and then i use viewholder to show data
to user in list.
if user click accept or refuse button. the result will be passed to hayoola and
the selected button will be disabled
Problem is after user click button wrong button getting disable. and after five click all remaining button getting disable too.
here is my adapter code :
static class myviewholder {
TextView textfromhayoola;
ImageButton buttonaccpet;
ImageButton buttonrefuse;
}
public View getView(int position, View convertView, ViewGroup parent) {
try {
final myviewholder viewHolder;
final int itemlocation = position;
if (convertView == null) {
convertView = inflater.inflate(R.layout.listforuser, null);
viewHolder = new myviewholder();
viewHolder.textfromhayoola = (TextView) convertView.findViewById(R.id.textfromhayoola);
viewHolder.buttonaccpet= (ImageButton) convertView.findViewById(R.id.buttonaccpet);
viewHolder.buttonrefuse= (ImageButton) convertView.findViewById(R.id.buttonrefuse);
convertView.setTag(viewHolder);
}else{
viewHolder = (myviewholder) convertView.getTag();
}
HashMap<String, String> myhayooladata= new HashMap<String, String>();
myhayooladata = data.get(position);
if (myhayooladata != null) {
viewHolder.textfromhayoola.setTag(myhayooladata.get("id_textfromhayoola"));
viewHolder.textfromhayoola.setText(myhayooladata.get("textfromhayoola"));
viewHolder.buttonaccpet.setTag(getItemId(position));
viewHolder.buttonrefuse.setTag(getItemId(position));
viewHolder.buttonaccpet.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
performOnBackgroundThread(new Runnable() {
public void run() {
passdatatohayoola(viewHolder.textfromhayoola.getTag().toString(),"accpet");
}
});
arg0.findViewWithTag(viewHolder.buttonaccpet.getTag()).setEnabled(false);
arg0.findViewWithTag(viewHolder.buttonrefuse.getTag()).setEnabled(false);
}
});
viewHolder.buttonrefuse.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
performOnBackgroundThread(new Runnable() {
public void run() {
passdatatohayoola(viewHolder.textfromhayoola.getTag().toString(),"refuse");
}
});
arg0.findViewWithTag(viewHolder.buttonaccpet.getTag()).setEnabled(false);
arg0.findViewWithTag(viewHolder.buttonrefuse.getTag()).setEnabled(false);
}
});
Hehe. Do you think that. Just "n/number of your records" view(s) will be created?
Assume that:
If you have 100 records and 10 rows will be draw.
And 10 rows won't re-draw.
Button accept of row 1 is disabled that mean Button accept of row 11 + n is the same. :D
May be that's your problem.
And Please read this:
Let I tell you something about ViewHolder pattern.
When you load data to ListView or GridView or something like these.
If your data has 1000 records, ... The ListView doesn't load 1000 records. It's just load "n records". "n" is number of record which compatible with your screen device.
Example: n = 10.
When you scroll down, The ListView will load more data. In this case, each row (view of row) will re-draw.
And with ListView which has large data, when use scroll up or down, you will see your app will be slow... because view is re-drawing.
And ViewHolder is the pattern which help you solve that problem.
When you use ViewHolder pattern, Just "n" (in this example n = 10) views will be created.
So, In your problem. I can tell you that:
May be you are implemented ViewHolder is incorrect.
OK. Let I tell you about the solution.
if (convertView == null) {
//You just findView and set for view
// viewHolder = new ViewHolder();
// viewHolder.yourBtn = convertView.findViewById(R.id.some_view_id);
// convertView.setTag(viewHolder);
} else {
//Right here, you will get viewHolder, because convertView is not null. It mean the viewHolder is existed.
}
And right here, It mean every time you will set data (You won't cache data, just cache VIEW)
Assume that Your data is an object and object has property which stand for status of button.
MyObject {
boolean objStatus;
}
and when you set data
viewHolder.yourBtn.setEnable(instanceOfMyObject.objStatus);
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.
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
}