views:

55

answers:

2

I am making a time sheet program where a user inputs his in- and out-punches. I have a ListView that I am populating from an array of calendar objects. I would like each row to show the day and date then on a new line the time, but I only want to display the day and date if it is different from the previous element.

Currently, I am setting visibility in the BaseAdapter based on comparisons using position vs position-1 (which are used as indices to the array). This only works if the whole list fits on the screen. If it extends beyond the screen and the user scrolls around the results are unpredictable.

To further confuse things, I am setting the color of the times, based on the position, to alternate between green and red (in/out) and it works as expected, scrolling or not.

How does Android handle the ListView position when scrolling or what could I do differently to show/hide the day and date?

public class TimeSheetActivity extends Activity {
SQLiteDatabase timesDatabase;
Cursor punchCursor;

private static Calendar[] allPunches;



@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.timesheet);
} //end onCreate()


@Override
public void onResume() {
    super.onResume();
    //Open database
    timesDatabase = openOrCreateDatabase(
            "times_database.db",
            SQLiteDatabase.CREATE_IF_NECESSARY,
            null);
    timesDatabase.setLocale(Locale.getDefault());
    timesDatabase.setLockingEnabled(true);
    timesDatabase.setVersion(1);
    punchCursor = timesDatabase.query("Timepunches", null, null, null, null, null, "punch ASC;");
    updateTimeSheet();
} //end onResume()


@Override
public void onPause() {
    super.onPause();
    timesDatabase.close();
} //end onResume()


private static class  EfficientAdapter extends BaseAdapter {
    private LayoutInflater mInflater;

    public EfficientAdapter(Context context) {
        mInflater = LayoutInflater.from(context);
    }

    public int getCount() {
        return allPunches.length;
    }

    public Object getItem(int position) {
        return position;
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.time_list_row, null);
            holder = new ViewHolder();
            holder.text1 = (TextView) convertView.findViewById(R.id.day_textview);
            holder.text2 = (TextView) convertView.findViewById(R.id.date_textview);
            holder.text3 = (TextView) convertView.findViewById(R.id.times_this_day_textview);

            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }

        String dayNames[] = new DateFormatSymbols().getWeekdays();

        //Initialize first list element
        if (position < 1) {
            holder.text1.setText(dayNames[allPunches[position].get(Calendar.DAY_OF_WEEK)]);
            holder.text2.setText(formatDate(allPunches[position]));

        }
        else {
            holder.text1.setText(dayNames[allPunches[position].get(Calendar.DAY_OF_WEEK)]);
            holder.text2.setText(formatDate(allPunches[position]));
            holder.text1.setVisibility(View.VISIBLE);
            holder.text2.setVisibility(View.VISIBLE);

            //Hide day and date if same as last
            if (formatDate(allPunches[position]).contentEquals(formatDate(allPunches[position-1]))) {
                holder.text1.setVisibility(View.GONE);
                holder.text2.setVisibility(View.GONE);
            }
        }

        holder.text3.setText(formatTime(allPunches[position], true) + " " + position);

        //Color in/out punches
        if (position%2 == 0) {
            holder.text3.setTextColor(Color.GREEN);
        }
        else {
            holder.text3.setTextColor(Color.RED);
        }

        return convertView;
    } //end getView()

    static class ViewHolder {
        public TextView text1;
        TextView text2;
        TextView text3;
    }
} //end EfficientAdapter


public void updateTimeSheet() {
    punchCursor = timesDatabase.query("Timepunches", null, null, null, null, null, "punch ASC;");
    allPunches = new Calendar[punchCursor.getCount()];

    int i = 0;                  //for indexing allPunches
    Calendar nextDay = Calendar.getInstance();
    nextDay.setLenient(true);

    //populate allPunches
    for (punchCursor.moveToFirst(); !punchCursor.isAfterLast(); punchCursor.moveToNext()) {
        allPunches[i] = Calendar.getInstance();
        allPunches[i].setTimeInMillis(punchCursor.getLong(0));
        ++i;
    } //end for

    final ListView timeSheetListView = (ListView)findViewById(R.id.timesheet_listview);
    timeSheetListView.setAdapter(new EfficientAdapter(this));
    timeSheetListView.setOnItemClickListener(new OnItemClickListener() {...}); //end click listener for list item
} //end updateTimeSheet()


public static String formatTime(Calendar thisTime, boolean showAMPM) {...}


public static String formatDate(Calendar thisDate) {
    String formattedDate = "";
    formattedDate += thisDate.get(Calendar.MONTH) +"-"+ thisDate.get(Calendar.DAY_OF_MONTH) +"-"+ thisDate.get(Calendar.YEAR);
    return formattedDate;
} //end formatDate()

} //end TimeSheet Activity

A: 

The views in the ListView are resused as you scroll. This likely causes the odd behavior you see. The important thing to remember when overriding getView is to set the behavior explicitly every time. Don't depend on a a view being in a default state, since you may be reusing a view that has already been changed.

In your particular case, make sure that you always set the visiblity explicitly to true or gone.

Also, did you copy paste this code directly? I believe you are missing a closing bracket for your second else statement.

Mayra
I am explicitly setting view to VISIBLE and GONE in getView(). I knew that the views were reused (thanks though) but I think there is something else going on.I did copy and paste but I removed unrelated code. The bracket that was missing here is present in my code. Thanks for the catch just the same.
hermy
I don't see anything obviously wrong in whatyou are doing then. Can you explain further what the unpredicatable behavior is?
Mayra
I might have fixed it. Thank your for your help, Mayra.
hermy
A: 

I seem to have been setting VISIBLE in the wrong place. Here is the code for getView() that seems to have it fixed!

    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.time_list_row, null);
            holder = new ViewHolder();
            holder.text1 = (TextView) convertView.findViewById(R.id.day_textview);
            holder.text2 = (TextView) convertView.findViewById(R.id.date_textview);
            holder.text3 = (TextView) convertView.findViewById(R.id.times_this_day_textview);

            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }

        String dayNames[] = new DateFormatSymbols().getWeekdays();
        holder.text1.setVisibility(View.VISIBLE);
        holder.text2.setVisibility(View.VISIBLE);

        //Initialize list
        if (position < 1) {
            holder.text1.setText(dayNames[allPunches[position].get(Calendar.DAY_OF_WEEK)]);
            holder.text2.setText(formatDate(allPunches[position]));
        }
        else {
            //Show day and date if not same as last
            holder.text1.setText(dayNames[allPunches[position].get(Calendar.DAY_OF_WEEK)]);
            holder.text2.setText(formatDate(allPunches[position]));

            if (formatDate(allPunches[position]).contentEquals(formatDate(allPunches[position-1]))) {
                holder.text1.setVisibility(View.GONE);
                holder.text2.setVisibility(View.GONE);
            }
        }

        holder.text3.setText(formatTime(allPunches[position], true));

        //Color in/out punches
        if (position%2 == 0) {
            holder.text3.setTextColor(Color.GREEN);
        }
        else {
            holder.text3.setTextColor(Color.RED);
        }

        return convertView;
    } //end getView()

    static class ViewHolder {
        public TextView text1;
        TextView text2;
        TextView text3;
    }
} //end EfficientAdapter
hermy