I am trying to display detailed Product information in an custom Listview with two TextViews per row for the key/value pair. The data is displayed correct. I also colored every second line different.
And there is my Problem. If I scroll up and down the different colored rows change their color and remain in this state. The data is not affected from this problem. Just the backroundcolor of the TextViews. I use the ViewHolder Pattern but this did not change anything. I added the code of the adapter. I think thats enough. Have you any idea?
Screenshot of the problem:
Code:
public class ProductDetailAdapter extends BaseAdapter {
private LinkedHashMap<String,String> list;
private Context context;
public ProductDetailAdapter(Context c, LinkedHashMap<String,String> list){
super();
this.context = c;
this.list=list;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ProductDetailAdapter.ViewHolder viewHolder;
if(convertView == null){
convertView=inflater.inflate(R.layout.product_detail_data_row,null);
viewHolder = new ViewHolder();
viewHolder.textViewKey = (TextView) convertView.findViewById(R.id.productDataKey);
viewHolder.textViewValue = (TextView) convertView.findViewById(R.id.productDataValue);
convertView.setTag(viewHolder);
}else {
viewHolder = (ProductDetailAdapter.ViewHolder) convertView.getTag();
}
viewHolder.textViewKey.setText((String)list.keySet().toArray()[position]);
viewHolder.textViewValue.setText(list.get(list.keySet().toArray()[position]));
if(position % 2 == 0){
viewHolder.textViewKey.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
viewHolder.textViewValue.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
}
return convertView;
}
private static class ViewHolder {
public TextView textViewKey;
public TextView textViewValue;
public ViewHolder(){};
}
}
That happens because the rows are being recycled. It's a common problem.
You can solve it by doing:
if(position % 2 == 0){
viewHolder.textViewKey.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
viewHolder.textViewValue.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite2));
} else {
viewHolder.textViewKey.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite1)); //Or the color that you want for odd rows
viewHolder.textViewValue.setBackgroundColor(context.getResources().getColor(R.color.colorParkerWhite1)); //Or the color that you want for odd rows
}
Try this:
super( c, 0, list );
instead of this:
super();
Once you pass the data source to the adapter you no longer need :
getCount
getItem
getItemId
please refer this link and link2 and link3 these will work for you
Related
I am trying to implement a customlistadapter for a small project of mine. I basically want ask java to use the appropriate class to inflate the view. I have here first:
public class slide {
public class video {
VideoView videoOfTheDay;
//Purpose of this constructor
public video(VideoView videoOfTheDay) {
this.videoOfTheDay = videoOfTheDay;
}
public VideoView getVideoOfTheDay() {
return videoOfTheDay;
}
}
public class blog {
ImageView imageOfTheDay;
TextView messageOfTheDay;
public blog(ImageView imageOfTheDay, TextView messageOfTheDay) {
this.imageOfTheDay = imageOfTheDay;
this.messageOfTheDay = messageOfTheDay;
}
public ImageView getImageOfTheDay() {
return imageOfTheDay;
}
public TextView getMessageOfTheDay() {
return messageOfTheDay;
}
}
public class advertisement {
ImageView ImageViewAd1;
ImageView ImageViewAd2;
public advertisement(ImageView imageViewAd1, ImageView imageViewAd2) {
this.ImageViewAd1 = imageViewAd1;
this.ImageViewAd2 = imageViewAd2;
}
public ImageView getImageViewAd1() {
return ImageViewAd1;
}
public ImageView getImageViewAd2() {
return ImageViewAd2;
}
}
}`
I have listed all the classes within a superclass slide because I wasn't able to accomplish no errors without them being grouped. From there I went to ask Java to look within itself and determine the appropriate class to use to populate the element:
class CustomListAdapter extends BaseAdapter {
private ArrayList<slide> customVariableDisplay;
private LayoutInflater layoutInflater;
public CustomListAdapter(Context context, ArrayList<slide>customVariableDisplay) {
this.customVariableDisplay = customVariableDisplay;
layoutInflater = LayoutInflater.from(context);
}
public int getCount() {
return customVariableDisplay.size();
}
public Object getItem(int position) {
return customVariableDisplay.get(position);
}
public long getItemId(int position) {
return position;
}
// If the element of the slide is a video -- then the getView will return...
if(slide==slide.video){
public View getView ( int position, View convertView, ViewGroup parent){
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.act_layout, null);
holder = new ViewHolder();
holder.slide.video = (VideoView) convertView.findViewById(R.id.videolayout);
}
else{
holder = (ViewHolder)convertView.getTag();
}
holder.video.setVideoResource(customVariableDisplay.get(position).getVideoOfTheDay());
}
return convertView;
}
// If the element is a 'blog' then --- then the getView will return...
else if(slide==slide.blog){
public View getView ( int position, View convertView, ViewGroup parent){
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.act_layout, null);
holder = new ViewHolder();
holder.slide.blog.message = (TextView) convertView.findViewById(R.id.messageInLayout);
holder.slide.blog.image = (ImageView) convertView.findViewById(R.id.imageInLayout);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
//Can write to getClass() for either?
//Ex: holder.(setImageResource(cVD) || setText(cVD)).getClass ??
holder.image.setImageResource(customVariableDisplay.get(position).getImageofTheDay());
holder.message.setText(customVariableDisplay.get(position).getMessageOfTheDay());
}
return convertView;
}
//Else if the element of the slide is an 'advertisement' then the getView will return...
else if (slide==slide.advertisement){
public View getView ( int position, View convertView, ViewGroup parent){
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.act_layout, null);
holder = new ViewHolder();
holder.slide.advertisement.imagead1 = (ImageView)convertView.findViewById(R.id.imageAdOneInAdvertisementLayout);
holder.slide.blog.image = (ImageView)convertView.findViewById(R.id.imageAdTwoInAdvertismentLayout);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
holder.imagead1.setImageResource(customVariableDisplay.get(position).getImageViewAd1());
holder.imagead2.setImageResource(customVariableDisplay.get(position).getImageViewAd2());
}
return convertView;
}
else{
//Throw a final View exception for unprecedented errors!!
}
}
}`
I am stuck with what to a way to ask Java what class is it inside the if statements. // If this slide comprises the class blog... etc. ANY HELP APPRECIATED! THANKS!
You can declare one variable slideType in your adapter and pass it to your adapter constructor and based on that value inflate your layout in onBindViewHolder(), onCreateViewHolder()
int slideType;
public CustomListAdapter(Context context, ArrayList<slide>customVariableDisplay, int slideType) {
this.slideType = slideType;
}
and also define three separate method in your adapter to pass your list so that you can bind your data.
Does this ListView show a mix of video, blog etc.. if yes then you need use below checking.
if(null != slide.video){
// add code for video
}
else if(null != slide.blog){
// add code for blog
}
else if (null != slide.advertisement){
// add code for advertisement
}
Also you need to set null for blog and advertisement in slide object or don't initialize them in case the item you want to show is video
I making a gridview that is made of a text and under it an image and everything is working fine but the problem is that when I scroll, the images gets repeated, so any tips on how I could solve this problem
here is my adapter:
public class CustomGridAdapter extends BaseAdapter {
private Context context;
private final String[] mobileValues;
public CustomGridAdapter(Context context, String[] mobileValues) {
this.context = context;
this.mobileValues = mobileValues;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(context);
// get layout from mobile.xml
gridView = inflater.inflate(R.layout.heartless_gridview_design, null);
// set value into textview
TextView textView = (TextView) gridView
.findViewById(R.id.heartless_name);
textView.setText(mobileValues[position]);
// set image based on selected text
ImageView imageView = (ImageView) gridView
.findViewById(R.id.heartless_image);
String mobile = mobileValues[position];
if (mobile.equals("Shadow")) {
imageView.setImageResource(R.drawable.shadow);
} else if (mobile.equals("Soldier")) {
imageView.setImageResource(R.drawable.soldier);
} else if (mobile.equals("Large Body")) {
imageView.setImageResource(R.drawable.large_body);
} else if (mobile.equals("Silver Rock")) {
imageView.setImageResource(R.drawable.silver_rock);
} else if (mobile.equals("Emerald Blues")) {
imageView.setImageResource(R.drawable.emerald_blues);
} else {
}
} else {
gridView = (View) convertView;
}
return gridView;
}
#Override
public int getCount() {
return mobileValues.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
}
please if you know how to fix this please tell me how.
so far I understood you should use viewholder pattern and
every time when getView method calls check if viewholder is null initialize it otherwise do nothing just return view..
http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder
and good example here - http://java.dzone.com/articles/android-listview-optimizations
I'm attempting to create a Gridview of icons with the Imageview set to one of two images like in my example below:
I'm retrieving two values from the sqlite content provider, one for the number of stamps and the other for the max number of stamps.
I use a custom adapter with a view holder pattern so I attempt to set the model onLoadFinished() in the Fragment. The model I've currently tried is an int[] with either value of 0 or 1 in the values to determine if green stamp is used or gray. This isn't working however as nothing is being displayed in the GridView.
Here's the code snippet from onLoadFinished() from the Fragment:
imageSourceModel = new int[data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_MAX_POINTS))];
int i = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_POINTS));
for (int j = 0; j < imageSourceModel.length; j++, i--) {
if (i > 0) {
imageSourceModel[j] = 1;
} else {
imageSourceModel[j] = 0;
}
}
stampGridAdapter.setModel(imageSourceModel);
and the code from the custom Adapter:
#Override
public View getView(int i, View view, ViewGroup parent) {
ViewHolder viewHolder;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.list_item_discover, parent, false);
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
if (model[i] > 0) {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp);
} else {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp_disabled);
}
return view;
}
And simple ViewHolder:
private class ViewHolder {
public ImageView stampView;
public ViewHolder(View view) {
stampView = (ImageView) view.findViewById(R.id.grid_stamp_image);
}
}
If I had to guess, I would say that it fails because of the creation order such that getView() is completed before onLoadFinished() can pass the model in. I'm just not sure how to work around this or what other methods I could use. Any help is appreciated.
For what you want to achieve, this seems unnecessary complicated to me, especially the double variable loop. To cut it simple I'd just do this:
in onLoadFinished()
int maxPoints = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_MAX_POINTS));
int currentPoints = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_POINTS));
mGridView.setAdapter(new RewardPointAdapter(getActivity(), currentPoints, maxPoints);
your adapter could look like this:
public class RewardPointAdapter extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private int mMaxRewardPoints;
private int mCurrentRewardPoints;
public RewardPointAdapter(Context context, int currentRewardPoints, int maxRewardPoints) {
mLayoutInflater = LayoutInflater.from(context);
mMaxRewardPoints = maxRewardPoints;
mCurrentRewardPoints = currentRewardPoints;
}
#Override
public int getCount() {
return mMaxRewardPoints;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.list_item_discover, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (position < mCurrentRewardPoints) {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp);
} else {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp_disabled);
}
return convertView;
}
private class ViewHolder {
public ImageView stampView;
public ViewHolder(View view) {
stampView = (ImageView) view.findViewById(R.id.grid_stamp_image);
}
}
}
I've some problems with my ListView custom adapter (and its newly implemented viewHolder). I have a ListView with a checkbox for each item (nothing new here). The problem is, if there is more than 9 items in my list, when I check the first checkbox, the tenth will be automatically checked (same for the second with the eleventh) just like if there were one listener for both item (and I beleive it's the case in some way).
I read about the position issue with listView, view recycling and the ViewHolder way to solve it here: How can I make my ArrayAdapter follow the ViewHolder pattern?
But I probably made something wrong because it's not working...
public class PresenceListAdapter extends SimpleAdapter {
private LayoutInflater inflater;
private List<Integer> ids;
private List<String> statuts;
public PresenceListAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to, List<Integer> ids, List<String> statuts)
{
super (context, data, resource, from, to);
inflater = LayoutInflater.from (context);
this.ids = ids;
this.statuts= statuts;
}
#Override
public Object getItem (int position)
{
return super.getItem (position);
}
#Override
public View getView (int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if (convertView == null)
{
convertView = inflater.inflate (R.layout.list_text_checkbox, null);
holder = new ViewHolder();
holder.btn = (Button) convertView.findViewById(R.id.btnRetard);
holder.check = (CheckBox) convertView.findViewById(R.id.checkPresent);
if (statuts.get(position).equals("P")) {
Drawable img = inflater.getContext().getResources().getDrawable(android.R.drawable.presence_online);
holder.btn.setCompoundDrawablesWithIntrinsicBounds( img, null, null, null );
holder.btn.setEnabled(true);
holder.check.setChecked(true);
}
else if(statuts.get(position).equals("R"))
{
Drawable img = inflater.getContext().getResources().getDrawable(android.R.drawable.presence_away);
holder.btn.setCompoundDrawablesWithIntrinsicBounds( img, null, null, null );
holder.btn.setEnabled(true);
holder.check.setChecked(true);
}
else
{
Drawable img = inflater.getContext().getResources().getDrawable(android.R.drawable.presence_invisible);
holder.btn.setCompoundDrawablesWithIntrinsicBounds( img, null, null, null );
holder.check.setChecked(false);
}
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
int id = ids.get(position);
if(id != 0)
{
holder.check.setTag(id);
holder.btn.setTag(id);
}
return super.getView (position, convertView, parent);
}
static class ViewHolder {
Button btn;
CheckBox check;
}
And my listener:
public void changerPresent(View v) {
CheckBox checkPresent = (CheckBox) v;
int idPersonne = (Integer) checkPresent.getTag();
View parent = (View)v.getParent();
Button btn = (Button) parent.findViewById(R.id.btnRetard);
if(checkPresent.isChecked()) {
gestion.updatePresence(idPersonne, idSeance, "P");
btn.setEnabled(true);
setBtnRetardPresent(btn);
}
else
{
gestion.updatePresence(idPersonne, idSeance, "A");
btn.setEnabled(false);
setBtnRetardAbsent(btn);
}
}
I would appreciate any help at this point, I'm working on this for hours now.
Thank you very much.
Here's how I made it work:
First, you need a separate array for your checked state. It has to be the same size as your adapter's getCount().
Then on your getView, your checkbox's setOnCheckedChangedListener MUST PRECEED your checkbox.setChecked statements.
example:
holder.checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
isChecked[position] = isChecked;
}
});
holder.checkBox.setChecked(isChecked[position]);
You should set CheckedBox state outside the initialization of ViewHolder, like the following code:
if (convertView == null) {
viewHolder = new ViewHolder();
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.checkedBox.setChecked();
BTW: use SparseBooleanArray instead of two list to store CheckedBox state.
The problem is because of the fact that listview recycles it views
so in getView() method
if (convertView == null)
{
........
}
else
{
holder = (ViewHolder) convertView.getTag();
}
// Uncheck needed boxes here... You need to implement your logic
if( 'position' is checked earlier)
holder.check.setChecked(true);
else
holder.check.setChecked(false);
You need to write the code to manage the state of view if the convert is not null, because it is a already used view which may be having checked check boxes.
Can someone tell me how should am i going to create my listview which look similar [here][1].
Problem: How am i going to fulfill the sort of look and feel in my codes which has an icons, file name, and file size yet at the same time looking clean and simple on each file object as shown in the example in the link [here][2]??
Can someone guide on this matter because i'm rather new in android/java... Thanks
Refer to following url for how to implement custom listview
Update
public static class VideoInfo {
public String name = "";
public String size= "";
public String path = "";//add any more variables of which you want info
}
then where you are creating arraylist i.e getVideoFiles() create object of this class
Declare ArrayList<VideoInfo> videoItems;
private void getVideoFiles(File[] videoList)
{
videoItems = new ArrayList<VideoInfo>();
for (int i = 0; i < videolist.length; i++)
{
File mfile = videoList[i];
String path = mfile.getPath();
path = path.substring(path.lastIndexOf("/", path.indexOf("."));
VideoInfo model = new VideoInfo();
model.name = path;
model.size = mfile.length();
videoItems.add(model);
}
setListAdapter(new ListViewAdapter(this, R.layout.row, videoItems));
}
now in your adapter pass this object of arraylist and how you set variables in listview is already given in link above
Problem 2: And why is my listview
having error when the directory is
empty despite having this to handle
the "empty" in my xml
did you remember to name your listview?:
#id/android:list
And if you could for further helper PLEASE clear up problem 1, so its more clear and concise what you want.
UPDATE
The ID of the listview should be: #id/android:list
The ID of the texview should be: #id/android:empty
You haven't create custom adapter to incorporate the layout into codes listview.
Here is something you can use
private class ListViewAdapter extends ArrayAdapter<Object> {
private ArrayList<Object> items;
public ListViewAdapter(Context context, int textViewResourceId,
ArrayList<Object> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
Object info = items.get(position);
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
if (info != null) {
ImageView imView = (ImageView) v.findViewById(R.id.icon);
TextView txtname =(TextView) v.findViewById(R.id.toptext);
TextView txtAddr = (TextView) v.findViewById(R.id.bottomtext);
//set image and set text as you like
}
return v;
}
}
Hope this can help.
First Create the Layout that you want to display for each row in your list then Extend the BaseAdapter class to Customize your Lists. This class contains getView method to replicate the rows in your lists as many times as you want to.
class HighScoreAdapter extends BaseAdapter
{
/* layout inflater to convert your XML layout to View to be rendered in your Each row of Lists.*/
private LayoutInflater minflator = LayoutInflater.from(HighScoreList.this);
ViewHolder holder;
#Override
public Object getItem(int position)
{
return position;
}
#Override
public long getItemId(int position)
{
return position;
}
boolean toRemove = false;
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
/* first time is null */
if (convertView == null )
{
convertView = minflator.inflate(R.layout.highscorelist, null);
holder = new ViewHolder();
holder.rank = (TextView) convertView.findViewById(R.id.user_id);
holder.username = (EditText) convertView.findViewById(R.id.user_nameedit);
holder.time = (TextView) convertView.findViewById(R.id.user_time);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
holder.rank.setText(String.valueOf(position+1)+".");
/* returns the view for next row as layout will be same i.e. this increases the your list's scrolling and working faster even though your list contains thousands of Entry*/
return convertView;
}
/* this class is to make reference to your child views of your layout by using this you can set your child views properties and Listeners acording to your need.*/
class ViewHolder
{
TextView rank;
EditText username;
TextView time;
}
}
I hope this solves your problem completely.. :)
Add this method to your code and if you get any error or Exception please let me know.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
Object info = items.get(position);
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
if (info != null) {
ImageView imView = (ImageView) v.findViewById(R.id.icon);
TextView txtname =(TextView) v.findViewById(R.id.toptext);
TextView txtAddr = (TextView) v.findViewById(R.id.bottomtext);
//set image and set text as you like
imview.setImageBitmap(IMAGE YOU WANT TO SET AAS ICON);
txtname.setText(FILENAME YOU WANT TO SET);
txtadr,setText(FILESIZE YOU WANT TO SET);
// Better you take each info filename,filesize and icon in a arraylist and set them
}
return v;
}