When i populate an ArrayAdapter, at first the rows in the visible part of the screen will be blank except the first row, which will be an undesired result. Then when I scroll it down and then come back all the screen will be set correctly...please some one tell me why this behavior happens ?
Code :
homeListView = (ListView) findViewById(R.id.home_list);
homeArrayAdapter = new HomeListArrayAdapter(this, R.id.home_list,
homeList);
homeListView.setAdapter(homeArrayAdapter);
Next Part as Class extending ArrayAdapter, an Inner Class to Activity:
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.home, null);
}
Home item = (homeList).get(position);
if (item != null) {
if (item.type_text == Home.SIMPLE_SHOUT) {
TextView tv1 = (TextView) findViewById(R.id.hTextView01);
if (tv1 != null) {
tv1
.setText(String.format(" %s\t:",
item.friend_name));
final String item_friend_id_01 = String
.valueOf(item.users_id);
tv1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast toast = Toast.makeText(Tranz.this,
item_friend_id_01, Toast.LENGTH_LONG);
toast.show();
}
});
}
TextView tv2 = (TextView) findViewById(R.id.hTextView02);
if (tv2 != null) {
tv2.setText(String.format("\t%s", item.message));
tv2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
TextView tv3 = (TextView) findViewById(R.id.hTextView03);
if (tv3 != null) {
tv3.setVisibility(0);
tv3.setText(null);
tv3.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
ImageView imageView = (ImageView) findViewById(R.id.file_image);
if (imageView != null) {
imageView.setBackgroundDrawable(null);
}
} else if (item.type_text == Home.FILE_SHOUT) {
TextView tv1 = (TextView) findViewById(R.id.hTextView01);
The code is getting truncated, so the whole code is not appearing here. I'm sorry about that. Here what i have tried is to put the values in the object of class Home into two textViews and an ImageView, which may vary upon the value returned by variable item.type_text
Also if somebody can tell me another best practice to do this, please help.
I'm not sure if this is the problem, but perhaps this will help.
I see you're passing null to LayoutInflater.inflate as the second parameter. Perhaps if you pass parent (which is the ViewGroup passed in as the third argument to getView(), probably your ListView) you will get better results.
I had a problem similar to this that i solved by using view.findViewById(R.id.hTextVew01) instead of just findViewById(R.id.hTextView01)
Related
My program is supposed to show a list of Apps and their data usage. If you click that app, it starts a new activity page and gives more information about that app. Everything works fine on the initial page and all apps show accurate information, but after I scroll down it messes up - the "more info" page shows a different app than the one I clicked.
I'm pretty sure the problem is around the onclick event not being binded to the holder? However I can't figure out what to do there. It should be noted that the problem is fixed if i get rid of the if(convertView == null)/else condition, but I know that is bad practice since we don't want to keep re-generating the previous items
Here is the getView code in my customAdapter:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
final DataUsageModel model;
model = getItem(position);
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.customlayout, null);
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intentMoreInfo = new Intent(view.getContext(),MoreInfoActivity.class);
intentMoreInfo.putExtra("wifiUsage",model.getWifiUsage());
intentMoreInfo.putExtra("mobileUsage",model.getMobileUsage());
intentMoreInfo.putExtra("appName",model.getName());
intentMoreInfo.putExtra("pname",model.getPname());
context.startActivity(intentMoreInfo);
}
});
holder.nameText = (TextView) convertView.findViewById(R.id.textViewWord);
holder.totalUsageText = (TextView) convertView.findViewById(R.id.textViewDescription);
holder.imageView = (ImageView) convertView.findViewById(R.id.iconImageView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.nameText.setText(model.getName());
long usageInMB = (model.getWifiUsage() + model.getMobileUsage())/(1024*1024);
holder.totalUsageText.setText(String.valueOf(usageInMB) + " MB");
holder.imageView.setImageDrawable(model.getImageRes());
return convertView;
}
public class ViewHolder {
TextView nameText;
TextView totalUsageText;
ImageView imageView;
}
Here is my code for the new activity started:
public class MoreInfoActivity extends AppCompatActivity {
TextView wifiUsage;
TextView mobileUsage;
ImageView icon;
TextView appName;
Button backButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.more_info_layout);
wifiUsage = findViewById(R.id.moreInfoWifi);
mobileUsage = findViewById(R.id.moreInfoMobile);
appName = findViewById(R.id.moreInfoAppName);
icon = findViewById(R.id.moreInfoIcon);
backButton = (Button)findViewById(R.id.moreInfoBack);
backButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
appName.setText(getIntent().getStringExtra("appName"));
wifiUsage.setText("Wifi Usage: " + String.valueOf(getIntent().getLongExtra("wifiUsage",0)/(1024*1024)) + " MB");
mobileUsage.setText("Mobile Usage: " + String.valueOf(getIntent().getLongExtra("mobileUsage",0)/(1024*1024)) + " MB");
try {
icon.setImageDrawable(getPackageManager().getApplicationIcon(getIntent().getStringExtra("pname")));
}catch(PackageManager.NameNotFoundException e){
e.printStackTrace();
}
You should set OnClickListener for every iteration of the getView method, not just when convertView == null.
In your case, the current data position is not synced with the current view (holder) position for every getView iteration
Furthermore, it is a very bad practice to start the activity from the adapter itself.
The adapter role is to bind view to data, and that's all.
I want to update in and ArrayAdapter a TextView when user will click + or - button. I cannot figure out how can I change data of individual in ArrayApapter.
Here is a image for better explanation what I want to do :
public View getView(int position, View countView, ViewGroup parent) {
View listView = countView;
if (listView == null) {
listView = LayoutInflater.from(getContext()).inflate(R.layout.listview, parent, false);
}
word currentWord = getItem(position);
TextView foodName = (TextView) listView.findViewById(R.id.food);
Button minus = (Button) listView.findViewById(R.id.minus);
Button add = (Button) listView.findViewById(R.id.add);
TextView total = (TextView) listView.findViewById(R.id.total);
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
minus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
return listView;
}
Whenever you are making a change through onclicklistener then just only change your data. like if your are using ArrayList> then just change the any value whatever you want like if your press 4th element with add+ then get the 4th item from the list and set it to +1. and then refresh your adapter with notifydatasetChanged().
In the above code you are only showing the UI part in the list whenever you will use data with you can do it then very easily.
Inside onClickListener, Increase or decrease the quantity and call onDataSetChanged().
public class SongListAdapter_AddMode extends ArrayAdapter{
Context context;
ArrayList<Song> songs;
public SongListAdapter_AddMode(Context context, ArrayList<Song> songs) {
super(context, R.layout.list_item1_addmode, songs );
this.songs = songs;
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
ViewHolder holder;
final int pos = position;
Log.d("TAG", "position :" + position);
Song currentSong = songs.get(position);
Log.d("TAG", "position : " + position );
if( convertView == null ){
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate( R.layout.list_item1_addmode, parent, false );
holder.titleLabel = (TextView) convertView.findViewById( R.id.topLabel );
holder.artistLabel = (TextView) convertView.findViewById( R.id.bottomLabel );
holder.cover = (ImageView) convertView.findViewById( R.id.list_image );
holder.button = (ImageView) convertView.findViewById(R.id.addButton);
holder.button.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("TAG", "pos" + pos );
}
});
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
Uri coverPath = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), currentSong.getID() );
Picasso.with(context).load( coverPath ).error( R.drawable.untitled ).into( holder.cover );
holder.titleLabel.setText( currentSong.getName() );
holder.artistLabel.setText( currentSong.getArtistName() );
return convertView;
}
private class ViewHolder {
TextView titleLabel;
TextView artistLabel;
ImageView cover;
ImageView button;
}
}
Well this is a small example of what an ArrayAdapter might look like. As you can see in the getView() method, I get the currentSong from a list which is actually very huge. It contains 1600~ Songs. When I print the position to the LogCat, it says 0 up to 7. How am I getting the correct position in the song list but when I print it, it is totally different?
I ALWAYS get the correct song. Even in the 900~ and above. But the position(LogCat) is always from 0 up to 7...
I need to get the current row. I want to add an onClickListener() to a button from the current View in this method and when I click it, I want to do something which I cannot do with an onItemClickListener() on the ListView.
It seems that you set the button click listener to print the position of the recyclable views thus you get 0-7.
Instead set the listener outside the if (convertview == null) check.
I ALWAYS get the correct song
of course since the code is correct
But the position(LogCat) is always from 0 up to 7...
This is due to how the ListView recycles its rows. 7 items are visible on the screen of your test device. See this post
You should override getCount:
#Override
public int getCount(){
return songs.size();
}
Because it is essential for correct displaying the list, but returns 0 b default.
When your view has wrong position ids or displays wrong items in your autocomplete dropdown view, then instantiate your view like this (Kotlin):
val view: View = convertView ?: LayoutInflater.from(context).inflate(resourceId, parent, false)
I'm trying to set an onClick listener on my ImageView in the Adapter of my GridView. However, weird thing happens: The content of the onClick function affects also some other Views in my GridView.
There is a good reason that I don't do the click listener on my GridView, so I need a solution for this via the ImageAdapter.
The logcat is called only once I click, but for some reason, other ImageViews are affected by this function.
Here's relevant code:
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.row_multiphoto_item, null);
holder.tickImageView = (ImageView) convertView.findViewById(R.id.tickImageView);
holder.imageView = (ImageView) convertView.findViewById(R.id.imageView1);
holder.imageViewLayout = (LinearLayout)convertView.findViewById(R.id.imageViewLayout);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
holder.imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ImageView imageView = (ImageView)v;
int id = imageView.getId();
imageView.setVisibility(View.GONE);/*
if(!thumbnailsselection[id]){
Log.d(Global.TAG, "CLICK");
holder.tickImageView.setVisibility(View.VISIBLE);
holder.imageViewLayout.setBackgroundResource(R.drawable.imageview_selected);
thumbnailsselection[id] = true;
}
else{
holder.tickImageView.setVisibility(View.GONE);
holder.imageViewLayout.setBackgroundResource(R.drawable.imageview_unselected);
thumbnailsselection[id] = false;
}
*/
}
});
holder.imageView.setId(position);
holder.imageViewLayout.setId(position);
holder.tickImageView.setId(position);
holder.imageView.setImageBitmap(thumbnails[position]);
return convertView;
}
class ViewHolder {
ImageView imageView;
LinearLayout imageViewLayout;
ImageView tickImageView;
int id;
}
In baseAdapters, view are recycled. This means that if you set a view to invisible, you will add some other view invisible when you will scroll.
To avoid that, be sure to set again the visibility of yout view in the getView method:
holder.imageView.setVisibility(View.VISIBLE)
holder.imageView.setOnClickListener(new OnCl...
Also you will have to store each visibility state, in order to reassing to visible or invisible.
I have a GridView with custom View in it, which is a Button and a TextView. I defined the setOnItemClickListener but it looks like it never invoked, please see peaces of code below.
gridview = (GridView) findViewById(R.id.main_gridview);
gridview.setAdapter(new GridAdapter(this));
gridview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(), "gadsfadsf",
Toast.LENGTH_SHORT).show();
Log.d("Main", "onItemClick");
}
});
The marked answer is kind of a hack. Instead of setting an onclicklistener to the button just ensure, that the ButtonView and the TextView has the following property:
android:clickable="false"
I had the same issue. While I've not yet figured out why it never gets invoked, I can propose a workaround.
Instead of setting the onClickListener on your GridView, set it on the Button itself inside your GridAdapter, inside your getView() method.
That worked for me!
It could be that some items in your GridView are stealing focus. Try adding these attributes to any elements you have inside the grid:
android:focusable="false"
android:focusableInTouchMode="false"
Instead of setting the onClickListener on your GridView,
set it on the Button itself inside your GridAdapter, inside your getView() method.
That worked for me!
I had the same problem, the event grid.itemClickListener was never launched.
In my case I had two listeners: grid.itemClickListener and another clickListener attached to a Button within the item's layout.
After fiddling with the layout for a while, I realized that if there was a widget, within the item's layout, with focusable=true, then itemClickListener was never launched. The clickListener attached to the Button worked well though.
Maybe that was your case. Anyway, I think this information might be useful to other users running into the same problem.
Thanx to CodingUser
what we were doing is directly accessing the Layout inside the GridView, so the onItemClickListener finds it confusing to access the item.
So the solution is to apply the onClickListener inside the Adapter (i.e. normally ArrayAdapter)
so what i m trying to say is:
public View getView(int position, View convertView, ViewGroup parent) {
//Here row is a view and we can set OnClickListener on this
final View row;
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
//Here we inflate the layout to view (linear in my case)
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.imageTitle = (TextView) row.findViewById(R.id.text);
holder.image = (ImageView) row.findViewById(R.id.image);
row.setTag(holder);
} else {
row = convertView;
holder = (ViewHolder) row.getTag();
}
ImageItem item = data.get(position);
holder.imageTitle.setText(item.getTitle());
holder.image.setImageBitmap(item.getImage());
//Now get the id or whatever needed
row.setId(position);
// Now set the onClickListener
row.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(context, "Clicked" + row.getId() + "!!",
Toast.LENGTH_SHORT).show();
}
});
return row;
}
You can set OnClick for view in Adapter of GridView .It work for me .
public View getView(final int position, View convertView, ViewGroup parent) {
ObjMenuVideo objListVideo = mListMenuVideo.get(position);
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.item_video_of_kind, null);
holder.tv_number_views = (TextView) convertView
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tv_number_views.setText(String.valueOf(objListVideo.getViews()));
convertView.setId(position);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent menuVideoIntent = new Intent(mContext,
ActivityDetailVideo.class);
mContext.startActivity(menuVideoIntent);
}
});
return convertView;
}