Update: I created a repository with less code to make it a bit easier to understand.
I\'m trying to create a widget. I made it like describ
Your thought behind ListView recycling issue is correct. You need to understand how things works at ground level.
MVC Pattern
The Android graphics works on the MVC pattern i.e. Model-View and Controller pattern. Model is your data, database in your case, View is your layout or graphical portion,such as ListView or RecyclerView or RemoteView. Controller changes your view after data update, parent View or ViewGroup in your case the
RemoteViewsService.RemoteViewsFactory
is the controller. I suggest to read further by googling the MVC model.
How Pattern is Implemented?
Any time the data changes, the view should be updated by the controller. The Android framework gives you opportunity to display your view at the given position with your data by overriding the getViewAt(int position)
.The controller calls the getViewAt(int position)
to get the view at the given position in the ListView or RecyclerView. ListView or RecyclerView renders only visible rows on the screen. For example, If you have 100 items in the ListView and only 7 is visible on the screen than it will call getItemAt(int) 7 times. Every time you scroll the getItemAt(int) is called for the visible rows. The ListView and RemoteView take liberty to recycle/reuse the previously passed View returned by the getItemAt(int position). It ensures that memory consumed by graphical portion of your application is limited
Why there is strange behavior?
First of all every visible thing on the screen is a View
such as TextView, ImageView and ListView etc. If not it can not be displayed on the screen. RemoteView is not a View. You pass the layout and data to be displayed with the RemoteView (View + Data).
Here the I am referring to your Screencast for the explanation.
1) Initialization: The
ListView
in your case, initially creates say 6 rows based on the visible space on screen andgetViewAt(int position)
is called once ifgetCount()
returns 1. I request to check the return value of the getCount() of the List adapter.
2) You scrolled down: Nothing happens to theListView
and rendering of new Rows.
3) You Scrolled Up: getPositionAt(int position) is called again and RemoteView is passed back.Two rows are visible now. I request to check the getCount() return value. It should be 2 if not than the reason could be caching of rows by ListView.
4) You scrolled down: Nothing happens to theListView
and rendering of new Rows.
5) You Scrolled Up: Refer 3. The getCount() should be 3 and so on.
What you should do?
As per your implementation, you created the RemoteView only once and tried reusing the same view at the getItemAt(int)
, may be to save on the layout inflation time.
To fix the issue, you MUST provide the FRESH RemoteView
every time getItemAt(int)
is called.
@Override
public RemoteViews getViewAt(int position)
{
Log.d("VplanWidgetViewsFactory", "getViewAt("+position+"):"+stunden.get(position));
//TODO: Store context when constructor is called.
RemoteView rv = new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget);
rv.setTextViewText(R.id.textView_lesson_nr, "" + (position + 1) + "."); <= I have not tested this.
return rv;
//return stunden.get(position); <=COMMENT THIS
}
I found the answer myself.
To fix the problem with the weird adding of views on scrolling and resizing you have to call removeAllViews
on the Layout where the subviews were added:
@Override
public RemoteViews getViewAt(int position) {
...
RemoteViews itemView = new RemoteViews(context.getPackageName(), R.layout.widget_listview_item);
itemView.removeAllViews(R.id.linearLayout_item_body);
...
return itemView;
}
And the problem that the views are not displayed is because of the color: After adding
subitem.setTextColor(R.id.textView_1, context.getResources().getColor(R.color.abc_primary_text_material_light));
subitem.setTextColor(R.id.textView_2, context.getResources().getColor(R.color.abc_primary_text_material_light));
subitem.setTextColor(R.id.textView_3, context.getResources().getColor(R.color.abc_primary_text_material_light));
all views are displayed:
Your problem seems to be in:
if(stundenContainer[j]!=null)
Log.d("VplanWidgetViewsFactory", "stundenContainer["+j+"]:" + stundenContainer[j].toString());
else
Log.d("VplanWidgetViewsFactory", "stundenContainer[" + j + "]:null");
if (stundenContainer[j] == null) {
//Freistunde
Log.d("VplanWidgetViewsFactory", "Freistunde");
// HERE -----
stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget));
faecher.add(new RemoteViews(context.getPackageName(), R.layout.fragment_fach));
stunden.get(stunden.size() - 1).setTextViewText(R.id.textView_lesson_nr, "" + (j + 1) + ".");
} else if (!stundenContainer[j].get(0).getSubject().equals("ignore")) {
Log.d("VplanWidgetViewsFactory", "stundenContainer[j].get(0).getSubject(): " + stundenContainer[j].get(0).getSubject());
// HERE -----
stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget));
You are adding it twice.. , but only when the first item is not ignored, so it appears random.
stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget));