ListView in widget adds randomly items on scrolling and resizing (nested remoteviews)

前端 未结 3 1904
北恋
北恋 2020-12-17 15:52

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

相关标签:
3条回答
  • 2020-12-17 16:32

    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 and getViewAt(int position) is called once if getCount() returns 1. I request to check the return value of the getCount() of the List adapter.
    2) You scrolled down: Nothing happens to the ListView 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 the ListView 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
    }
    
    0 讨论(0)
  • 2020-12-17 16:36

    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:

    0 讨论(0)
  • 2020-12-17 16:45

    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));
    
    0 讨论(0)
提交回复
热议问题