Подтвердить что ты не робот

ListView в виджетах добавляет случайные элементы при прокрутке и изменении размера (вложенные удаленные просмотры)

Обновление: Я создал репозиторий с меньшим количеством кода, чтобы сделать его немного понятным.

Я пытаюсь создать виджет. Я сделал это, как описано здесь: qaru.site/info/257021/...

Это работает частично, но у меня действительно странная ошибка. Я сделал screencast, поэтому легче понять, что я имею в виду: http://c.maysi.de/c6H9

Скриншот: введите описание изображения здесь

Как вы можете видеть, есть некоторые элементы, которые были добавлены случайным образом. (RemoteViews, которые были добавлены к другому объекту RemoteViews) То же самое происходит при изменении размера виджета.

То, что я напечатал в журнале, похоже на ожидаемое. нет неверных данных. Кроме того, при прокрутке нет новых записей в журнале.

Это мой код:

RemoteViewsFactory:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class MyWidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private static ArrayList<Item> items = new ArrayList<>();
    private static int itemnr = 0;
    private static int subitemnr = 0;
    private int appWidgetId;
    private Context context;

    public MyWidgetViewsFactory(Context context, Intent intent) {
        this.context = context;
        appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);


        //Some random data to display
        for (int i = 0; i < 10; i++) {
            Item item = new Item(String.valueOf(itemnr++));

            for (int j = 0; j < 3; j++) {
                String[] subitem = {String.valueOf(subitemnr++), String.valueOf(subitemnr++), String.valueOf(subitemnr++)};
                item.addSubitem(subitem);
            }

            items.add(item);
        }
    }

    @Override
    public void onCreate() {
        // no-op
    }

    @Override
    public void onDestroy() {
        // no-op
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public RemoteViews getViewAt(int position) {
        Log.d("MyWidgetViewsFactory", "getViewAt(" + position + "):" + items.get(position));
        Item item = items.get(position);

        RemoteViews itemView = new RemoteViews(context.getPackageName(), R.layout.widget_listview_item);

        itemView.setTextViewText(R.id.textView_itemnr, item.getItemNr());

        for (String[] s : item.getSubitems()) {
            Log.d("MyWidgetViewsFactory", "subitem:" + s[0] + "|" + s[1] + "|" + s[2]);
            RemoteViews subitem = new RemoteViews(context.getPackageName(), R.layout.widget_listview_subitem);

            subitem.setTextViewText(R.id.textView_1, s[0]);
            subitem.setTextViewText(R.id.textView_2, s[1]);
            subitem.setTextViewText(R.id.textView_3, s[2]);

            itemView.addView(R.id.linearLayout_item_body, subitem);
        }
        return itemView;
    }

    @Override
    public RemoteViews getLoadingView() {
        return (null);
    }

    @Override
    public int getViewTypeCount() {
        return (1);
    }

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

    @Override
    public boolean hasStableIds() {
        return (true);
    }

    @Override
    public void onDataSetChanged() {
        // no-op
    }

    class Item {
        private ArrayList<String[]> subitems = new ArrayList<>();
        private String itemnr = "";

        Item(String itemnr) {
            this.itemnr = itemnr;
        }

        Item() {
        }

        public void addSubitem(String[] subitem) {
            this.subitems.add(subitem);
        }

        public ArrayList<String[]> getSubitems() {
            return subitems;
        }

        public String getItemNr() {
            return itemnr;
        }

        public void setItemNr(String itemnr) {
            this.itemnr = itemnr;
        }
    }
}

AppWidgetProvider

    public class MyWidgetProvider extends AppWidgetProvider {

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        Log.d("MyWidgetProvider", "appWidgetIds.lenght:" + appWidgetIds.length);
        for (int appWidgetId : appWidgetIds) {
            Intent svcIntent = new Intent(context, MyWidgetService.class);
            svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

            RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.widget_root);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
                widget.setRemoteAdapter(R.id.listView_widget, svcIntent);
            else
                widget.setRemoteAdapter(appWidgetId, R.id.listView_widget, svcIntent);

            /*
            Intent clickIntent = new Intent(context, MainActivity.class);
            PendingIntent clickPI = PendingIntent.getActivity(context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            widget.setPendingIntentTemplate(R.id.listView_widget, clickPI);*/

            appWidgetManager.updateAppWidget(appWidgetId, widget);
        }
    }

    @Override
    public void onEnabled(Context context) {
        // Enter relevant functionality for when the first widget is created
    }

    @Override
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled
    }
}

RemoteViewsService

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class MyWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return (new MyWidgetViewsFactory(this.getApplicationContext(), intent));
    }
}

все остальные ресурсы можете найти в репо в GitHub.


Выход Logcat:

08-08 02:11:10.858  32427-32444/? D/MyWidgetViewsFactory﹕ getViewAt(0):[email protected]179c9
08-08 02:11:10.860  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:0|1|2
08-08 02:11:10.864  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:3|4|5
08-08 02:11:10.866  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:6|7|8
08-08 02:11:10.927  32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(0):[email protected]179c9
08-08 02:11:10.927  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:0|1|2
08-08 02:11:10.927  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:3|4|5
08-08 02:11:10.927  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:6|7|8
08-08 02:11:10.931  32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(1):[email protected]248ce
08-08 02:11:10.931  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:9|10|11
08-08 02:11:10.931  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:12|13|14
08-08 02:11:10.931  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:15|16|17
08-08 02:11:10.933  32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(2):[email protected]bf3ef
08-08 02:11:10.933  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:18|19|20
08-08 02:11:10.933  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:21|22|23
08-08 02:11:10.933  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:24|25|26
08-08 02:11:10.936  32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(3):[email protected]3defc
08-08 02:11:10.936  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:27|28|29
08-08 02:11:10.936  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:30|31|32
08-08 02:11:10.936  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:33|34|35
08-08 02:11:10.938  32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(4):[email protected]85
08-08 02:11:10.938  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:36|37|38
08-08 02:11:10.938  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:39|40|41
08-08 02:11:10.938  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:42|43|44
08-08 02:11:10.941  32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(8):[email protected]e23da
08-08 02:11:10.941  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:72|73|74
08-08 02:11:10.941  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:75|76|77
08-08 02:11:10.941  32427-32443/? D/MyWidgetViewsFactory﹕ subitem:78|79|80
08-08 02:11:10.943  32427-32447/? D/MyWidgetViewsFactory﹕ getViewAt(9):[email protected]de00b
08-08 02:11:10.943  32427-32447/? D/MyWidgetViewsFactory﹕ subitem:81|82|83
08-08 02:11:10.943  32427-32447/? D/MyWidgetViewsFactory﹕ subitem:84|85|86
08-08 02:11:10.943  32427-32447/? D/MyWidgetViewsFactory﹕ subitem:87|88|89
08-08 02:11:10.945  32427-32444/? D/MyWidgetViewsFactory﹕ getViewAt(5):[email protected]deee8
08-08 02:11:10.945  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:45|46|47
08-08 02:11:10.945  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:48|49|50
08-08 02:11:10.945  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:51|52|53
08-08 02:11:10.948  32427-32444/? D/MyWidgetViewsFactory﹕ getViewAt(7):[email protected]99901
08-08 02:11:10.948  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:63|64|65
08-08 02:11:10.948  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:66|67|68
08-08 02:11:10.948  32427-32444/? D/MyWidgetViewsFactory﹕ subitem:69|70|71
08-08 02:11:10.951  32427-32447/? D/MyWidgetViewsFactory﹕ getViewAt(6):[email protected]aa3a6
08-08 02:11:10.951  32427-32447/? D/MyWidgetViewsFactory﹕ subitem:54|55|56
08-08 02:11:10.951  32427-32447/? D/MyWidgetViewsFactory﹕ subitem:57|58|59
08-08 02:11:10.951  32427-32447/? D/MyWidgetViewsFactory﹕ subitem:60|61|62

Итак, данные передаются правильно. он просто не отображается правильно...

BTW: вот что должно выглядеть: http://c.maysi.de/cB8K

Может быть, проблема связана с вложенными удаленными просмотрами? потому что все внешние удаленные просмотры отображаются правильно...

4b9b3361

Ответ 1

Я сам нашел ответ.

Чтобы устранить проблему с странным добавлением просмотров при прокрутке и изменении размера, вы должны вызвать removeAllViews в макете, где были добавлены дополнительные объекты:

@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;
    }

И проблема, что представления не отображаются из-за цвета: После добавления

    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));

отображаются все представления:

введите описание изображения здесь

Ответ 2

Ваша мысль о повторной переработке ListView правильная. Вам нужно понять, как все работает на уровне земли.

Шаблон MVC

Графики Android работают на шаблоне MVC, то есть на модели-View и Controller. Модель - это ваши данные, база данных в вашем случае. Вид - это ваш макет или графическая часть, например ListView или RecyclerView или RemoteView. Контроллер меняет ваше представление после обновления данных, родительского представления или ViewGroup в вашем случае RemoteViewsService.RemoteViewsFactory является контроллером. Я предлагаю читать дальше, перейдя по модели MVC.

Как реализуется шаблон?

Каждый раз, когда данные изменяются, просмотр должен обновляться контроллером. Рамка Android дает вам возможность отображать ваше представление в данной позиции с вашими данными, переопределяя getViewAt(int position). Контроллер вызывает getViewAt(int position), чтобы получить представление в данной позиции в ListView или RecyclerView. ListView или RecyclerView отображает на экране только видимые строки. Например, если у вас есть 100 элементов в ListView, и на экране отображается только 7, то это вызовет getItemAt (int) 7 раз. Каждый раз, когда вы прокручиваете, getItemAt (int) вызывается для видимых строк. В ListView и RemoteView есть свобода для повторного использования/повторного использования ранее переданного представления, возвращаемого getItemAt (int position). Это гарантирует, что память, потребляемая графической частью вашего приложения, ограничена.

Почему существует странное поведение?

Прежде всего, все видимые объекты на экране - это View, такие как TextView, ImageView и ListView и т.д. Если это не отображается на экране. RemoteView не является видом. Вы передаете макет и данные, которые будут отображаться с помощью RemoteView (View + Data).

Здесь я имею в виду ваш Screencast для объяснения.

1) Инициализация: ListView в вашем случае изначально создает, скажем, 6 строк на основе видимого пространства на экране, а getViewAt(int position) вызывается один раз, если getCount() возвращает 1. Я запрашиваю для проверки возвращаемого значения getCount() адаптера List.
2) Прокрутка вниз: ничего не происходит с ListView и рендерингом новых строк.
3) Вы прокручиваете: getPositionAt (int position) вызывается снова, а RemoteView передается обратно. Теперь две строки видны. Я прошу проверить возвращаемое значение getCount(). Это должно быть 2, если не причина, которая может быть кешированием строк ListView.
4) Прокрутка вниз: ничего не происходит с ListView и рендерингом новых строк.
5) Вы прокручиваете: см. 3. getCount() должен быть 3 и т.д.

Что вы должны делать?

В соответствии с вашей реализацией вы создали RemoteView только один раз и попытались повторно использовать один и тот же вид в getItemAt(int), возможно, нужно сохранить время разметки компоновки.
Чтобы устранить проблему, вы ДОЛЖНЫ предоставлять FRESH RemoteView каждый раз, когда вызывается getItemAt(int).

@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
}

Ответ 3

Ваша проблема, кажется, в:

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));

Вы добавляете его дважды.., но только тогда, когда первый элемент не игнорируется, поэтому он кажется случайным.

stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget));