0

I am trying to do an Android ListView with my own, custom and so called "widgets".

This is my Widget parent-class:

public class Widget {

protected Context context;

protected int layout;
protected int headlineId;
protected int leftButtonId;
protected int rightButtonId;
protected int headlineTextId;
protected View fragment;
protected LayoutInflater inflater;

protected View widgetView;
protected TextView headline;
protected ImageButton leftButton, rightButton;

protected void init() {
    context = fragment.getContext();

    widgetView = inflater.inflate(layout, parentLayout, false);

    headline = (TextView) widgetView.findViewById(headlineId);
    leftButton = (ImageButton) widgetView.findViewById(leftButtonId);
    rightButton = (ImageButton) widgetView.findViewById(rightButtonId);

    headline.setText(headlineTextId);

    leftButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(fragment.getContext(), "[Move]", Toast.LENGTH_SHORT).show();
        }
    });


    rightButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(fragment.getContext(), "[Hide]", Toast.LENGTH_SHORT).show();
        }
    });
}

public View getLayout() {
    return widgetView;
}

public int getLayoutId() {
    return layout;
}

public void setLayout(View layout) {
    this.widgetView = layout;
}

public int getHeadlineTextId() {
    return headlineTextId;
}

public TextView getHeadline() {
    return headline;
}

public void setHeadline(String s) {
    headline.setText(s);
}

public void setHeadline(int resId) {
    setHeadline(context.getString(resId));
}

}

I am using this class in extension with my widget subclasses, like this (extract):

public class NowWidget extends Widget {

private TextView degree;

public NowWidget(View fragment, LayoutInflater inflater) {
    super();
    this.fragment = fragment;
    this.inflater = inflater;

    this.layout = R.layout.widget_now;
    this.headlineId = R.id.widget_now_card_headline;
    this.leftButtonId = R.id.widget_now_card_left_button;
    this.rightButtonId = R.id.widget_now_card_right_button;
    this.headlineTextId = R.string.now;

    init();
}

public void init() {
    super.init();

    degree = (TextView) widgetView.findViewById(R.id.widget_now_degree);
}

public void setDegree(String s) {
    degree.setText(s);
}

}

The idea behind the widget class: Everything (including the view with all data) is saved in this class and can be called via getLayout().

In my fragments, I create the widgets and fill them with data (Getter and Setter). In the beginning, I worked with normal LinearLayouts and just added the widgets to it. Everything was working properly.

But now I have to use ListViews (performance) and tried it with the following Adapter:

public class WidgetAdapter extends ArrayAdapter<Widget> {

private ArrayList<Widget> widgets;

public WidgetAdapter(Context context, ArrayList<Widget> widgets) {
    super(context, 0, widgets);

    this.widgets = widgets;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    return getItem(position).getLayout();

}

private static class WidgetHolder {
    public Widget widget;
}

}

I set the Adapter in my fragment like this:

    CustomWidgetListView widgetListView = (CustomWidgetListView) fragment.findViewById(R.id.now_list_view);

    ArrayList<Widget> widgetList = new ArrayList<Widget>();

    for (int i = 0; i < 20; i++) {

        NowWidget nowWidget = new NowWidget(fragment, inflater);

        nowWidget.setHeadline("Widget " + i);

        widgetList.add(nowWidget);

    }

    WidgetAdapter adapter = new WidgetAdapter(context, widgetList);
    widgetListView.setAdapter(adapter);

The widgets get created and I can see them in my ListView. This is not my problem.

BUT: The widgets are not in the right order: everything what is off-screen on loading, is not created and the "old" widgets are shown:

Top of my ListView: Top of my ListView

Scrolled down: Scrolled down

I have already noticed (via logging the widgets-ArrayList) that the ArrayList changes on scrolling. But why?

I have really no idea why this happens. I've already tried a lot of other getView-methods in my adapter, but sometimes there were no widgets in the ListView or they were still in the wrong order.

I've also tried this (now working):

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        Widget widget = getItem(position);
        convertView = widget.getLayout();
    }

    return convertView;
}

I hope someone of you can help me.

Thanks!

  • Please see this answer: http://stackoverflow.com/a/19289890/950427. You need to utilize the `ViewHolder` pattern. – Jared Burrows Feb 16 '15 at 21:30
  • Sorry, but it doesn't help me. My widget structure I do not have to inflate in the getView() method of the adapter. Everything is already inflated and saved in the Widget class. – Dominik Dassow Feb 16 '15 at 21:35
  • You need to convert your code to the `ViewHolder` pattern for `ListView`s. The `getView` recycles the view, the positions your are seeing are only visible list view items(eg. 0-5). – Jared Burrows Feb 16 '15 at 22:19
  • possible duplicate of [Android - Keep ListView's item highlighted once one has been clicked](http://stackoverflow.com/questions/9281000/android-keep-listviews-item-highlighted-once-one-has-been-clicked) – Jared Burrows Feb 16 '15 at 22:30

1 Answers1

0

The ListView is recycling your Views so it doesn't have to make a new one every time. You need logic for if a new view is created, and also logic for if the view is not null, but is to be recycled.

In your getView method you have the correct logic to make a new view (if convertView == null).

Now you must make convertView point to the correct widget if the view is not null, because if it's not null it's trying to recycle the view, and you return it without changing it giving you unexpected output.

MeetTitan
  • 3,016
  • 1
  • 10
  • 21
  • But in my first code snipped of the getView method, it doesn't even work if I just take the saved view (return getItem(position).getLayout();) and give a sh*t on performance.. Why is this not working? – Dominik Dassow Feb 17 '15 at 21:11
  • @Dominik, I'm not sure why. It sounds like your `widgetList` is changing. I can almost guarantee that if your views are out of order, it's because of view recycling, though. Perhaps look into the viewholder pattern as the others have suggested. – MeetTitan Feb 17 '15 at 21:22
  • Yes, my `widgetList` is changing. I did some tests and logged the list. I looked already in the ViewHolder Patter, but it doesn't solve my problem. – Dominik Dassow Feb 18 '15 at 17:52
  • I've thought on this a bit. I think your problem may be that you're inflating your view in `onCreate` (I assume), instead of in `getView` with the `parent` `ViewGroup`. – MeetTitan Feb 18 '15 at 20:36