Good time!
My Android app has so feature that I use ListView in the one of the page of TabHost without layout for ListView. Like that:
ListView lv = new ListView(this);
lv.setAdapter(myAdapter);
So I'd like to change some row's properties like text size in the row. So, how can I get access to the properties of the explicit row of ListView to change text size, for example?
Use BaseAdapter and modify font size 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!!!
Related
I have a listfragment which I am populating with a adapter extending Base adapter.
I want a textview in one particular to be of a different color.
This is my target: (Forgive me for the "Title" spelling, I was in a hurry)
However when I scroll the list up and down, textviews from different rows also change color randomly,like this:
I have tried with and without viewholders, with the same result. I can't seem to find out what the problem is.
This is my getView method
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderItem viewHolder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.shedrow, null);
viewHolder = new ViewHolderItem();
viewHolder.txt=(TextView) convertView.findViewById(R.id.textview1);
viewHolder.txt2=(TextView) convertView.findViewById(R.id.textview2);
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolderItem) convertView.getTag();
}
viewHolder.txt.setTypeface(tf);
viewHolder.txt2.setTypeface(tf);
viewHolder.txt.setText(schedStorage.getProgramTitle(position));
if (position==0)
{
viewHolder.txt2.setTextColor(color);
viewHolder.txt2.setTypeface(tf, Typeface.BOLD);
}
viewHolder.txt2.setText(sTimes.get(position));
return convertView;
}
convertView is a reused View (Row). When you scroll up and down and the first row goes off screen, Android may choose to reuse it when new rows show on-screen. If it gets reused, the color of txt2 will still be set to green. To avoid this, you should set the color and Typeface of txt2 when position != 0.
if (position==0) {
viewHolder.txt2.setTextColor(color);
viewHolder.txt2.setTypeface(tf, Typeface.BOLD);
} else {
viewHolder.txt2.setTextColor(Color.BLACK);
viewHolder.txt2.setTypeface(tf, Typeface.NORMAL);
}
I have a list view with a custom listadapter. Inside the getView method of my adapter I intialize the layout of my rows. Inside each row there is a TextView.
I would like to pass specific values to the TextViews of specific rows. Is it possible?
The adapter code is here:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
final Model model = arrList.get(position);
if (row == null) {
row = inflater.inflate(R.layout.list_row, null, false);
holder = new ViewHolder();
holder.text = (TextView) row.findViewById(R.id.textview);
holder.button = (Button) row.findViewById(R.id.okbutton);
holder.numberPicker = (com.lol.helen.widgets.NumberPicker) row.findViewById(R.id.numb_pickr);
Yes, it is possible to pass custom data through holder. But be careful what you're holding for holder since a holder is tied to its view only and since views are recycled you'll end up using same holder for more views. If you need to keep some state info for a specific view position, store that data in model object.
I am having an unclear issue concerning the recycling of views in a getView method of a custom array adapter.
I understand that elements are reused, but how do I know exact what to implement in the first part of the if statement, and what in the second?
Right now I am having following code. I came to this question due to dropping the code in the second part of the statement which results in a list of the first 9 elements, which are repeated numberous times instead of all elements. I didn't really know what is causing this exactly...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
title = getItem(position).getTitle();
size = calculateFileSize(position);
txtTitle = (TextView) row.findViewById(R.id.txtTitle);
tvFileSize = (TextView) row.findViewById(R.id.tvFileSize);
txtTitle.setText(title);
tvFileSize.setText(size);
} else {
title = getItem(position).getTitle();
size = calculateFileSize(position);
txtTitle = (TextView) row.findViewById(R.id.txtTitle);
tvFileSize = (TextView) row.findViewById(R.id.tvFileSize);
txtTitle.setText(title);
tvFileSize.setText(size);
}
return row;
}
It's easy. The first time no row is created, so you have to inflate them. Afterwards, the Android os may decide to recycle the views that you already inflated and that are not visible anymore. Those are already inflated and passed into the convertView parameter, so all you have to do is to arrange it to show the new current item, for example placing the right values into the various text fields.
In short, in the first part you should perform the inflation AND fill the values, in the second if (if convertView != null) you should only overwrite the field because, given the view has been recycled, the textviews contain the values of the old item.
This post and this are good starting points
I understand that elements are reused, but how do I know exact what to implement in the first part of the if statement, and what in the second?
The organization is quite simple once you get the hang of it:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
/* This is where you initialize new rows, by:
* - Inflating the layout,
* - Instantiating the ViewHolder,
* - And defining any characteristics that are consistent for every row */
} else {
/* Fetch data already in the row layout,
* primarily you only use this to get a copy of the ViewHolder */
}
/* Set the data that changes in each row, like `title` and `size`
* This is where you give rows there unique values. */
return convertView;
}
For detailed explanations of how ListView's RecycleBin works and why ViewHolders are important watch Turbo Charge your UI, a Google I/O presentation by Android's lead ListView programmers.
You want to create a ViewHolder class in your MainActivity. Something like
static class ViewHolder
{
TextView tv1;
TextView tv2;
}
then in your getView, the first time you get your Views from your xml in the if and reuse them after that in the else
View rowView = convertView;
if (rowView == null)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.layout_name_to_inflate, parent, false);
holder = new ViewHolder();
holder.tv1= (TextView) rowView.findViewById(R.id.textView1);
holder.tv2 = (RadioGroup) rowView.findViewById(R.id.textView2);
rowView.setTag(holder);
}
else
{
holder = (ViewHolder) rowView.getTag();
}
I would recommend that you use the View holder and convertview pattern to create your listView as it will be more efficient.Here is a good explanation of how it works with a re-use strategy. This will answer your question on how re-cycling works. If you want to refer to a code sample, I have it on GitHub.
Hope this helps.
The last part of the question I really couldn't grasp without a picture of the effect but for the first part "what to implement in the first part of the if statement, and what in the second" I think I've found the this implementation very common.
You would find the view references first and store them to a static class ViewHolder which then you attach to the tag of the new inflated view. As the listview recycles the views and a convertView is passed getView you get the ViewHolder from the convertView's tag so you don't have to find the references again (which greatly improves performance) and update the view data with that of your object at the position given.
Technically you don't care what position the view was since all you care for is the references to the views you need to update which are held within it's ViewHolder.
#Override
public View getView(int position, View convertView, ViewGroup container) {
ViewHolder holder;
Store store = getItem(position);
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.item_store, null);
// create a holder to store references
holder = new ViewHolder();
// find references and store in holder
ViewGroup logoPhoneLayout = (ViewGroup) convertView
.findViewById(R.id.logophonelayout);
ViewGroup addressLayout = (ViewGroup) convertView
.findViewById(R.id.addresslayout);
holder.image = (ImageView) logoPhoneLayout
.findViewById(R.id.image1);
holder.phone = (TextView) logoPhoneLayout
.findViewById(R.id.textview1);
holder.address = (TextView) addressLayout
.findViewById(R.id.textview1);
// store holder in views tag
convertView.setTag(holder);
} else {
// Retrieve holder from view
holder = (ViewHolder) convertView.getTag();
}
// fill in view with our store (at this position)
holder.phone.setText(store.phone);
holder.address.setText(store.getFullAddress());
UrlImageViewHelper.setUrlDrawable(holder.image, store.storeLogoURL,
R.drawable.no_image);
return convertView;
}
private static class ViewHolder {
ImageView image;
TextView phone;
TextView address;
}
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 viewholder to display from a dynamic arrayadapter.it works but the data displayed changes irregularly when i scroll the List.i want my List View to be populated only once ,Not all the time when i scroll my list. Any suggestion?
Here is my Code
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.sample, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
if(_first==true)
{
if(id<myElements.size())
{
holder.name.setText(myElements.get(id));
holder.icon.setImageBitmap( mIcon1 );
id++;
}
else
{
_first=false;
}
}
//holder.icon.setImageBitmap(mIcon2);
/*try{
if(id<myElements.size())
id++;
else
{
id--;
}
}
catch(Exception e)
{
android.util.Log.i("callRestService",e.getMessage());
}*/
return convertView;
}
static class ViewHolder {
TextView name;
ImageView icon;
}
when the list is loaded it looks like this : http://i.stack.imgur.com/NrGhR.png after scrolling some data http://i.stack.imgur.com/sMbAD.png it looks like this, and again if i scroll to the beginning it looks http://i.stack.imgur.com/0KjMa.png
P.S : my list have to be in alphabetic order
Have you tried this?
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.sample, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
holder.name.setText(myElements.get(id));
holder.icon.setImageBitmap( mIcon1 );
return convertView;
}
static class ViewHolder {
TextView name;
ImageView icon;
}
If yes, what's wrong with it?
I don't think loading all the rows at once is a good idea. You will end up having plenty of useless Views in memory that are going to slow the application down for nothing.
Views and operations on views (like inflate, findViewById, getChild..) are expensive, you should try to reuse them as much as possible. That's why we use ViewHolders.
You would need to write you own version of ListView to do that (which is bad). If the ListView doesn't work properly, it probably means that you are doing something wrong.
Where does the id element come from? You are getting the position in your getView() method, so you don't need to worry about exceeding list bounds. The position is linked to the element position in your list, so you can get the correct element like this:
myElements.get(position);
When the data in your list changes, you can call this:
yourAdapter.notifyDataSetChanged()
That will rebuild your list with new data (while keeping your scrolling and stuff).