tags:

views:

78

answers:

1

Please help! I tried everything! :(

I've got a schedule Class, which is simply a custom ViewGroup (with custom onMeasure() and onLayout()), which enables me to place childs(=events) with LayoutParams for column/row start and column/row end. The number of childs and their LayoutParams depend on database entries.

Now I'm trying to add childs (events) from my database. I'd have to use a Cursor Adapter, so my schedule Class has to extend ListView, right? I tried that but the newView() method of the adapter is never called. Why not?? My custom ListView doesn't ask the adapter for childs, no childs are added. I also can't add the childs by hand calling schedule.addView() if I extend from AdapterView.

I'd be really (really) happy if someone could help!

Regards, cody

This is my custom ViewGroup:

public class Schedule extends ViewGroup {
private int columns;
private int rows;
private float preferredCellWidth;
private float preferredCellHeight;
private String[] rowTimes;
private Paint paint;

public Schedule(Context context, int columns, int rows, float preferredCellWidth, float preferredCellHeight, String[] rowTimes) {
    super(context);
    this.columns = columns;
    this.rows = rows;
    this.preferredCellWidth = preferredCellWidth;
    this.preferredCellHeight = preferredCellHeight;
    this.rowTimes = rowTimes;
    init(context);
}

private void init(Context context) {
    Log.i("Schedule", "initSchedule...");
    setPaint();
    setWillNotDraw(false);
}

private void setPaint() {
    paint = new Paint();
    paint.setTextSize(preferredCellHeight*2/3);
    paint.setStyle(Paint.Style.STROKE);
    paint.setColor(getResources().getColor(R.color.white));
}

public Schedule(Context context, AttributeSet attrs) {
    super(context, attrs);
    readAttr(context, attrs);
    init(context);
}

public Schedule(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    readAttr(context, attrs);
    init(context);
}

private void readAttr(Context c, AttributeSet attrs) {
    android.content.res.TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ScheduleLayout);
    this.columns = a.getInt(R.styleable.ScheduleLayout_columns, 1);
    this.rows = a.getInt(R.styleable.ScheduleLayout_rows, 1);
    this.preferredCellWidth = a.getDimension(R.styleable.ScheduleLayout_preferredCellWidth, 1);
    this.preferredCellHeight = a.getDimension(R.styleable.ScheduleLayout_preferredCellHeight, 1);
    a.recycle();
}

@Override
protected void onDraw(Canvas canvas) {
    //Log.i(this.toString(),"onDraw ..."+" this.getLeft()="+this.getLeft()+", this.getWidth()="+this.getWidth());
    super.onDraw(canvas);
    for (int i = 0; i < rows; i++) {
        int line = (int) Math.round(this.getTop()+ (i+1) * preferredCellHeight);
        canvas.drawText(this.rowtimes[i], this.getLeft()+5, line-3, paint);
        canvas.drawLine(this.getLeft(), line, this.getWidth(), line, paint);
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    Log.i("Schedule", "onMeasure...");
    float width = (MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight()) / columns;
    float height = (MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom()) / rows;
    float cellWidth = preferredCellWidth;
    float cellHeight = preferredCellHeight;

    if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
        cellWidth = width;
    } else if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
        cellWidth = Math.min(preferredCellWidth, width);
    }

    if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
        cellHeight = height;
    } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
        cellHeight = Math.min(preferredCellHeight, height);
    }

    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            int cwidth = (int) Math.round(cellWidth * lp.getWidth());
            int cheight = (int) Math.round(cellHeight * lp.getHeight());
            child.measure(
                    MeasureSpec.makeMeasureSpec(cwidth, MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(cheight, MeasureSpec.EXACTLY)
            );
        }
    }
    setMeasuredDimension(
            (int) Math.round(cellWidth * columns + getPaddingLeft() + getPaddingRight()),
            (int) Math.round(cellHeight    * rows + getPaddingTop() + getPaddingBottom())
    );
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (!changed)
        return;

    int cellWidth = ((r-l) - getPaddingLeft() - getPaddingRight()) / columns;
    int cellHeight = ((b-t) - getPaddingTop() - getPaddingBottom()) / rows;

    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);

        if (child.getVisibility() != GONE) {
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            int cl = (int) Math.round(getPaddingLeft() + lp.columnStart    * cellWidth);
            int cr = (int) Math.round(getPaddingLeft() + lp.columnEnd * cellWidth);
            int ct = (int) Math.round(getPaddingTop() + lp.rowStart    * cellHeight);
            int cb = (int) Math.round(getPaddingTop() + lp.rowEnd * cellHeight);
            child.layout(cl, ct, cr, cb);
        }
    }
}

protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams p) {
    Log.i("Schedule", "checkLayoutParams...");
    if (p instanceof LayoutParams) {
        LayoutParams lp = (LayoutParams) p;
        if (lp.columnEnd > columns || lp.columnStart < 0)
            return false;
        if (lp.rowEnd > rows || lp.rowStart < 0)
            return false;
        return lp.columnEnd > lp.columnStart && lp.rowEnd > lp.rowStart;
    } else
        return false;
}

public android.widget.AbsListView.LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new android.widget.AbsListView.LayoutParams(getContext(), attrs);
}

public static class LayoutParams extends android.view.ViewGroup.LayoutParams {
    public int columnStart;
    public int columnEnd;
    public int rowStart;
    public int rowEnd;

    public LayoutParams(int columnStart, int rowStart, int columnEnd, int rowEnd) {
        super(WRAP_CONTENT, WRAP_CONTENT);
        this.columnStart = columnStart;
        this.columnEnd = columnEnd;
        this.rowStart = rowStart;
        this.rowEnd = rowEnd;
    }

    public LayoutParams(Context c, AttributeSet attrs) {
        super(WRAP_CONTENT, WRAP_CONTENT);
        android.content.res.TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.EventLayout);
        this.columnStart = a.getInt(R.styleable.EventLayout_event_columnStart, 0);
        this.columnEnd = a.getInt(R.styleable.EventLayout_event_columnEnd,    this.columnStart + 1);
        this.rowStart = a.getInt(R.styleable.EventLayout_event_rowStart, 0);
        this.rowEnd = a.getInt(R.styleable.EventLayout_event_rowEnd, this.rowStart + 1);
        a.recycle();
    }

    public int getWidth() {
        return columnEnd - columnStart;
    }

    public int getHeight() {
        return rowEnd - rowStart;
    }
}

And this is the event-layout - event.xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
android:gravity="center" >

    <TextView android:id="@+id/text_event_name"
    style="@style/Event_TextView1" />

    <TextView android:id="@+id/text_event_name2"
    style="@style/Event_TextView2" />

</LinearLayout>

<TextView android:id="@+id/text_event_weeks"
style="@style/Event_TextView2"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" />

<TextView android:id="@+id/text_event_room"
style="@style/Event_TextView2"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true" />

In my Activity I've got that code:

Schedule schedule = new Schedule(this, 4, rowTimes.length, 15, 15, rowTimes);

Cursor cursor = dbManager.getEvents(day); MySimpleCurserAdapter adapter = ... ?? // schedule.setAdapter not working...

How can I add events to the schedule with the data from the cursor?

+1  A: 

You should not need to be extending ListView. You just want to add an instance of ListView to your layout.

It sounds like you might want to be using a SimpleCursorAdaptor, where you can map items in your custom view to the data model objects you want them to display.

See Binding to Data with Adapter and Hello ListView for some examples of the right ways to use adapters and ListViews.

Mayra
Thanks for the reply. But how do I set adapter for a ViewGroup that doesn't extend ListView?? To call setAdapter() is not allowed.
cody
I'm not sure why you have a custom ViewGroup. Did you read through the ListView tutorial? You want a layout file that describes the layout of each of your items. Then, in your main layout you have a ListView. You get that listView by ID in onCreate, and set the adapter there.
Mayra
I added the code. I can't figure out how to combine the custom ViewGroup and a ListView for the items (=events). I don't understand why my ViewGroup can't directly be that ListView.
cody
I'm confused about what you are trying to accomplish with the ViewGroup. Is that suppposed to be a single item in a ListView? Or are you trying to handle multiple list view items yourself in the ViewGroup?
Mayra
Yes...I'm just trying to handle multiple list view items in the ViewGoup... I though I could simply let it extend from ListView and the rest could do an adapter, but that doesn't seem to work. Do you have an idea?
cody
Don't try to handle multiple list view items in your own ViewGroup. That is not the way that ListView is meant to be used. Instead, create a layout that only displays the contents of a single list view item. When you create your adapter, you can provide it with the layout you want the adapter to use to display each row. Then let the ListView handle displaying one for each row.
Mayra
Ok.. so I could use the code from event.xml for my SimpleCursorAdapter to display each row and in my main layout file I just embed a ListView. That's clear. But how do I make use of my custom ViewGroup? I need the event views (=list items) all look different (column/row start/end) ...?
cody
You can override the getView method in the adapter to alter the item view as it is created. Keep in mind that the ListView reuses item view instances throughout the list, so for every change you make on one view, you need to do the opposite on all other views.
Mayra
So you mean I can create the Schedule layout in the newView() method of the adapter (perhaps put it in a ViewHolder) and attach the whole schedule to the parent (the listview) there? I've got only one schedule for the list, does that make sense..? Sorry that I need to take your time, I'd be glad if this could finally work...
cody
Sorry, I don't understand what you are trying to do. What is the end result that you want? A ListView with x number of schedules? Or a single Schedule with x number of events? It might help if you showed a sketch of what you expect it to look like. It doesn't make sense to have one item in a ListView.
Mayra
I've got a TabActivity where there are tabs for each day of the week. For each tab I want to create one separate Schedule (which I thought could each be a ListView). I can already place events into that Schedule with begin and end time (=row), but the data is not loaded from the database. I'm looking for a possibility to show (not more than about 5) events for each day which are saved in the db.
cody
Ok, so it sounds like you want a list of events where each event would be a ListView item. If you wanted to customize the look of particular events, then you could do so in the getView method of the adapter. There would not be a "Schedule" view. Alternatively, you can use your own Schedule view and not use ListView at all. But then you need to handle adding of events yourself.
Mayra
Alright, I think you'd agree when I say it's better to not make use of a ListView here. The events all have different positions and dimensions. I'll use only the Schedule ViewGroup instead which already supplies the required measure and layout methods. Now for this reason I only have to consider a different connection to the database. Something like a data providing service which runs in a background thread.Thank you so far, and have a nice weekend!
cody