I have a Listview which shows Facebook friend image and name.There is a Button on each row on the right side to do some operation withe that particular Facebook friend.The Button can either have 3 backgrounds based on some condition. Those are as Follows,
1.Set the button background to "Background1.png" if that friend is not invited.
2.Set the button background to "background2.png" if that friend is invited.
3.Set the button background to "background3.png" if that friend had completed the survey.
I have some given conditions which checks whether the friend is invited or not.Also for the survey thing.Attached screenshot shows what I need actually.
My problem is the button background changes while scrolling as Listview reuses the position.I cant use general views to show it as number of Facebook friend varies and it will show memory issue.
Below is what I have tried so far. Please guide me how to achieve this.
Code:
#Override
public View getView(int position, View convertView,
ViewGroup parent) {
View vi = convertView;
if (convertView == null)
vi = mInflater.inflate(R.layout.category_listview, null);
Facebook_FriendImage=(SmartImageView)vi.findViewById(R.id.category_image);
Facebook_FriendName = (TextView) vi.findViewById(R.id.category_name);
invite = (Button) vi.findViewById(R.id.invite);
invite.setTag(position);
if (position==2 || position==3) {
invite.setBackgroundResource(R.drawable.background1);
}
else if (position==5 || position==8) {
invite.setBackgroundResource(R.drawable.background2);
}
else {
invite.setBackgroundResource(R.drawable.background3);
}
//Snippet for loading FB Friend Image
int loader = R.drawable.no_image;
Facebook_FriendImage.setScaleType(ImageView.ScaleType.FIT_XY);
Facebook_FriendImage.setImageUrl(GlobalClass.FACEBOOK_FRIEND_IMAGE_URL.get(position), loader);
//Snippet for Loading FB Friend Name
String unicode=new String(GlobalClass.FACEBOOK_FRIEND_NAME.get(position));
Typeface font= Typeface.createFromAsset(getAssets(), "NotoSans-Bold.ttf");
Facebook_FriendName.setTypeface(font);
Facebook_FriendName.setText(unicode);
return vi;
}
The if conditions I have given here is just for simplification.
solution may be this:
simply delete only condition.
if(convertView == null)
may this help.
you should also try to make new View:
View vi = convertView;
if(vi == null) {
//your code
}
return vi;
Use a Holder to your ListView like this :
public View getView(int position, View convertView,
ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null){
convertView = mInflater.inflate(R.layout.category_listview, null);
holder = new ViewHolder();
holder.Facebook_FriendImage=(SmartImageView)convertView.findViewById(R.id.category_image);
holder.Facebook_FriendName = (TextView) convertView.findViewById(R.id.category_name);
holder.invite = (Button) convertView.findViewById(R.id.invite);
invite.setTag(position);
convertView.setTag(holder);
}
else {
holder = (ViewHolder)convertView.getTag();
}
if (position==2 || position==3) {
holder.invite.setBackgroundResource(R.drawable.background1);
}
else if (position==5 || position==8) {
holder.invite.setBackgroundResource(R.drawable.background2);
}
else {
holder.invite.setBackgroundResource(R.drawable.background3);
}
//Snippet for loading FB Friend Image
int loader = R.drawable.no_image;
holder.Facebook_FriendImage.setScaleType(ImageView.ScaleType.FIT_XY);
holder.Facebook_FriendImage.setImageUrl(GlobalClass.FACEBOOK_FRIEND_IMAGE_URL.get(position), loader);
//Snippet for Loading FB Friend Name
String unicode=new String(GlobalClass.FACEBOOK_FRIEND_NAME.get(position));
Typeface font= Typeface.createFromAsset(getAssets(), "NotoSans-Bold.ttf");
holder.Facebook_FriendName.setTypeface(font);
holder.Facebook_FriendName.setText(unicode);
return convertView;
}
and the class ViewHolder should be like this :
class ViewHolder{
SmartImageView Facebook_FriendImage;
TextView Facebook_FriendName;
Button invite;
}
This is the problem of view recycling i also faced the same problem. Go through link nelow it solved my problem
check box in listview not working properly
Related
I'm working on making a custom ArrayAdapter so that my list has the first element in a different color.
The thing is, when I execute this code in the get view method:
#Override
public View getView(int position, View convertView, ViewGroup parent){
Club club = (Club)getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false);
}
TextView textView = (TextView)convertView.findViewById(R.id.txtListItem);
if(position == 0 && club.getName().contains("All")){
textView.setTextColor(ContextCompat.getColor(getContext(), R.color.orange));
}
textView.setText(club.getName());
return convertView;
}
The first item is orange, yes, but the 10th one (below the screen) is also when I scroll down :( in another list with more elements than what the screen can hold, I have several that are orange.. I don't understand why, please help!
This is happening because views are recycling/reused. You are setting orange color for the first item but not setting default color for the rest. Just add an else clause to your if statement above, something like this
if(position == 0 && club.getName().contains("All")){
Log.d(ClubAdapter.class.getName(), club.getName());
textView.setTextColor(ContextCompat.getColor(getContext(), R.color.orange));
}else{
textView.setTextColor(ContextCompat.getColor(getContext(), R.color.your_default_color));
}
Most likely this happens because Android is recycling the view which was originally used for the first row. You need an else clause to set the color back to normal.
This happens because the adapter tries to reuse as many views as possible for performance improvement. That's why you should not create a view in getView, but reuse them (only create if it is null):
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false);
}
Even though you shouldn't create a new view, you have to make sure that you set the parameters that you want for each getView call (also called "binding"), so:
if(position == 0){
//set your first view color and whatever
}else{
//set your common view colors..
}
Because listview recycle views Google suggests to use the ViewHolder pattern. You should also set a default color if the cell is different. It would look something like this.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView == null){
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.textView = (TextView) convertView.findViewById(R.id.txtListItem);
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag();
}
if(position == 0 && club.getName().contains("All")){
viewHolder.textView.setTextColor(ContextCompat.getColor(getContext(), R.color.orange));
}else{
//Set default color
}
viewHolder.textView.setText(club.getName());
return convertView;
}
static class ViewHolder {
protected TextView textView;
}
I have AudioListFile with Player Program.
When i click an item of GridView, sound will be played.!
enter image description here
Now, i wonder, How can i update ProgressBar in GridView?
I need mediaplayer.getDuration() in adapter.
please help me. thank you so much.
My adapter:
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// Initialize views
holder = new Holder();
convertView = inflater.inflate(R.layout.raw_audio_list_item, null);
holder.tvFileName = (TextView) convertView.findViewById(R.id.row_audio_list_item_tv_file_name);
holder.tvFileSize = (TextView) convertView.findViewById(R.id.row_audio_list_item_tv_file_size);
holder.pbPlayProgress = (ProgressBar) convertView.findViewById(R.id.audio_file_list_pb);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
// get the map object from list
AudioDataModel audioFileModel = audioFileList.get(position);
// set data to view
holder.tvFileName.setText(audioFileModel.getFileName());
holder.tvFileSize.setText(audioFileModel.getFileSize());
holder.pbPlayProgress.setMax(Integer.parseInt(audioFileModel.getFileDuration()));
convertView.setId(Integer.valueOf(audioFileModel.getFileId()));
return convertView;
}
actually you can easily do this but you have to use sparseIntegerHashmap to track the current progress for particular view because gridview or listview recycle the view they reuse the view for efficiency. so first check in getview method check whether this particular view are for this particular progress bar (means it not reused ) then set it's value from hashmap and if this view is newly created (no entry found in hashmap then) please apply default value for progress bar . simple you have to track individual view with progress in hashmap . current code might not be working because i am not on my workstation right now hope you will understand e.g
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// Initialize views
holder = new Holder();
convertView = inflater.inflate(R.layout.raw_audio_list_item, null);
holder.tvFileName = (TextView) convertView.findViewById(R.id.row_audio_list_item_tv_file_name);
holder.tvFileSize = (TextView) convertView.findViewById(R.id.row_audio_list_item_tv_file_size);
holder.pbPlayProgress = (ProgressBar) convertView.findViewById(R.id.audio_file_list_pb);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
// get the map object from list
AudioDataModel audioFileModel = audioFileList.get(position);
// set data to view
holder.tvFileName.setText(audioFileModel.getFileName());
holder.tvFileSize.setText(audioFileModel.getFileSize()); holder.pbPlayProgress.setMax(Integer.parseInt(audioFileModel.getFileDuration()));
convertView.setId(Integer.valueOf(audioFileModel.getFileId()));
//your unique id for compare
if(map.get(audioFileModel.getFileId())==holder.getId) {
holder.pbPlayProgress.setProgress(map.get(audioFileModel.getFileId()));
} else {
holder.pbPlayProgress.setProgress(//add default value );
// also add entry in hashmap
map.add(audioFileModel.getFileId(),default value)
}
return convertView;
}
Currently my listview is working fine to return one view but I want it to
check when the currentVerse.getVerseNumber()==1 and return two different views (rows) if the condition is true. I cannot figure it out, Any help will be highly appreciated.
public View getView(int position, View convertView, ViewGroup parent) {
VersesModel currentVerse = verses.get(position);
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(
R.layout.verses_custom_list, parent, false);
viewHolder = new ViewHolder();
font = Typeface.createFromAsset(convertView.getContext().getAssets(), "my_font.ttf");
viewHolder.textView = (TextView) convertView.findViewById(R.id.textView_Verse);
viewHolder.nView = (TextView) convertView.findViewById(R.id.textView_verseNumber);
viewHolder.textView.setTypeface(font);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.textView.setText(currentVerse.getVerseText().toString());
viewHolder.nView.setText(currentVerse.getVerseNumber() + "");
convertView.setTag(viewHolder);
return convertView;
}
Its simple according to me ....
Create a layout with both views in the same layout which you want to show...
And suppose for condition true you want to show LinearLayout-A and for false u want to show LinearLayout-B then its simple use View.Visibility
if(true)
{
LinearLayout-B.setVisibility(View.GONE);
LinearLayout-A.setVisibility(View.VISIBLE);
}
else
{
LinearLayout-A.setVisibility(View.GONE);
LinearLayout-B.setVisibility(View.VISIBLE);
}
btw im using both because rememember listview paints everytime the view is created after its hidden so to make it easy and robust otherwise it wud just mess up alot...
Hope it helps u ...
Thanks
i have a big problem with my ListViewAdapter.
My listview shows 2 entrys at the same time. Each entry should get a different picture from my server.
The first and the second entrys working fine, but if i'm scrolling down, the next entrys will have the same pictures.
My Code looks like this:
if (viewHolder.imgPic != null) {
String strUrl = mainUrl+list.get(position).getUrl();
new ImageDownload(viewHolder.imgPic).execute(strUrl);
}
I'm checking the view and just doing it, if it's null.
Can someone help me?
Thanks
from your question I can assume that you don't know about the ListView recycling mechanisem
basically, view that that not visible anymore (after user scrolled it away from sight) it been recycled to displayed new item that need to shown. that's the convertView parameter at the getView() method...
probably you are see the same image because the recycled view stays with the same image..
also there is the issue of the asynchronous task (your ImageDownload class) that can finish it execute when the original item that started the request already been recycled.
I recommend you to "dig" as dipper as you can to understand all about ListView - this is one of the most complex and important UI component. reading the post I linked you is a good start.
also this video is very important:
http://www.youtube.com/watch?v=wDBM6wVEO70
Here is my GetView:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = null;
if(rowResourceId!=R.layout.search_empty_list) {
if (convertView == null) {
LayoutInflater inflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflator.inflate(rowResourceId, null);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.textName = (TextView) view.findViewById(R.id.textView1);
viewHolder.imgPic = (ImageView) view.findViewById(R.id.imgPic);
if (viewHolder.imgPic != null) {
String strUrl = mainUrl+list.get(position).getUrl();
new ImageDownload(viewHolder.imgPic).execute(strUrl);
}
view.setTag(viewHolder);
} else {
view = convertView;
}
ViewHolder holder = (ViewHolder) view.getTag();
holder.textName.setText(list.get(position).getName());
} else {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(rowResourceId, parent, false);
TextView textView1 = (TextView) view.findViewById(R.id.textView1);
textView1.setText(list.get(0).getName());
}
return view;
}
I am trying to parse this JSON response onto a ListView in android where I am trying to modify the listview by showing an image depending upon the gender and adding a button for male records.
The logic is working fine, except for the last two entries.John Wayne & Leonardo Dicaprio are having details button, same is expected for Johnny Depp & Ravi Tambda.Please guide me for the same.
Using this code, I am trying to differentiate them according to the genders
if(rowData.mgender.equalsIgnoreCase("male"))
{
imageViewStatus.setImageResource(R.drawable.male);
}
else{
imageViewStatus.setImageResource(R.drawable.female);
button.setVisibility(View.GONE);
}
EDIT 1
public View getView(final int position, View convertView, ViewGroup parent){
ViewHolder holder = null;
TextView title = null;
TextView detail = null;
TextView data=null;
TextView message=null;
ImageView imageViewStatus=null;
Button button=null;
final RowData rowData= getItem(position);
if(null == convertView)
{
convertView = layoutInflater.inflate(R.layout.record, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
button=(Button)holder.getProceedButton();
message=holder.getEmail();
message.setText(rowData.mEmail);
title = holder.getName();
title.setText(rowData.mName);
detail = holder.getAddress();
detail.setText(rowData.mAdress);
data= holder.getPhoneNumber();
data.setText(rowData.mMobile+" "+rowData.mOffice);
imageViewStatus=holder.getImage();
System.out.println("This is the gender "+rowData.mgender);
if(rowData.mgender.equalsIgnoreCase("male"))
{
imageViewStatus.setImageResource(R.drawable.male);
}
else{
imageViewStatus.setImageResource(R.drawable.female);
System.out.println("Button Visibility"+button.getVisibility());
button.setVisibility(View.GONE);
}
return convertView;
}
Keep in mind that ListView children are always reused while you scroll them. When you set a property according to some condition in getView(), you have to revert that property when the condition isn't met. You're hiding the button when the gender is female, but when this view gets reused to populate a male contact, the button is still invisible and you have to set it as visible again.
Check this out:
if(rowData.mgender.equalsIgnoreCase("male"))
{
imageViewStatus.setImageResource(R.drawable.male);
button.setVisibility(View.VISIBLE); // You need to add this line in your code
} else {
imageViewStatus.setImageResource(R.drawable.female);
button.setVisibility(View.GONE);
}