Маркер строки ListView в стиле GMail

Я заинтересован в создании ListView, где каждая строка помечена так, как это делается в GMail для 3.0+. Это создает хорошее разделение левого и правого ListFragment.

Другие примеры включают также Календарь Google на 2.3.4, например, где цветной маркер находится слева от ListView.

GMail ListView

Обратите внимание на серый вертикальный разделитель между двумя списками. Как добиться чего-то подобного? Бонусом будет также переменная ширина, но я думаю, что это всего лишь меньшее изменение макета.

Я знаю, что, вероятно, мог бы сделать что-то вроде вставки туда ImageView, а затем заполнить его цветом, который я хотел бы, но мне кажется, что это уродливый хак.

Еще один вопрос будет заключаться в том, существует ли общий способ объединения двух фрагментов ListView каким-либо образом, как это делают приложения GMail или Mail.

GMail ListView в двух фрагментах


person Joa Ebert    schedule 20.05.2011    source источник


Ответы (3)


Если вам нужна скорость, то вариант, который я бы выбрал, - это использовать пользовательский класс View (например, расширить RelativeLayout) для представления контейнера строк и переопределить метод dispatchDraw(Canvas canvas).

Метод dispatchDraw вызывается после, когда представление отрисовывает свое собственное содержимое, и до отрисовки своих дочерних элементов — дочерние элементы рисуются при вызове super.dispatchDraw.

Используйте это, чтобы сделать что-то вроде

private boolean mDrawMarker = false;

public void setShouldDrawMarker(boolean drawMarker) {
    mDrawMarker = drawMarker;
}
public boolean getShouldDrawMarker() {
    return mDrawMarker;
}

@Override
public void dispatchDraw(Canvas canvas) {
    // draw the children of our view 
    super.dispatchDraw(canvas);

    // draw our marker on top of the children if needed
    if (mDrawMarker) {
        // e.g. canvas.drawRect(...) or canvas.drawBitmap(...)
    }
}

Таким образом, вы избежите добавления каких-либо дополнительных представлений в иерархию, что означает, что вы не понесете никаких штрафов на этапах макета или измерения. Не забудьте повторно использовать объекты Paint и Rect при рисовании прямоугольника, а не создавать каждый раз новый. Точно так же, если вы используете растровое изображение, вы должны использовать один и тот же экземпляр Bitmap для всех экземпляров вашего представления, а не каждый раз загружать новый из ваших ресурсов (это не означает размещение их в static полях).

Для отступов элементов, поскольку в этом случае списки, кажется, не перекрываются, вы могли бы (с моей головы):

  • Установите левое поле в контейнере строки (не совсем уверен, что это сработает)
  • Оберните контейнер строки в LinearLayout и установите для него левый отступ (если вышеприведенное не работает)
  • Используйте пользовательский класс представления (если установка левого поля не работает)
  • Следуйте предложению @commonsware и используйте два вида — один слева с серым цветом фона, а другой справа от него с цветом маркера — затем просто установите вид слева как видимый/исчезнувший, если вы хотите, чтобы отступы/нет- отступ

Что касается перекрытия представлений во втором примере, я отложу ответ @commonsware.

person Joseph Earl    schedule 20.05.2011

Скажите, правильно ли я понял проблему.... Вы хотите пометить определенные строки как выбранные, а выбранные строки визуально отображаются с отступом (с другим цветом и полями)?

Вот 2 техники:

1 — Использование StateListDrawable для фона строки:

Если это так, я бы создал файл макета строки списка и установил для свойства «фон» значение StateListDrawable (может быть XML). Это позволит строке переключать визуальные состояния для выбранного и не выбранного.

Атрибут "drawable" StateListDrawable будет представлять собой PNG с 9 патчами, один для невыбранного состояния, которое не включает поля, и один для выбранного... выбранный будет определять поле внутри самого PNG, указав область содержимого/нижняя черная линия не должна полностью простираться до левой стороны PNG, оставляя область, которая является вашим немасштабированным полем.

Ради тех, кто найдет это, Рэдли Маркс только что опубликовал отличный пост о патче 9: http://radleymarx.com/blog/simple-guide-to-9-patch/

С ListViews иногда бывает так, что вы хотите отключить «listSelector» (который представляет собой отдельный объект, отображаемый либо над списком, либо позади) и вместо этого использовать атрибут «duplicateParentState», чтобы позволить самой строке отображать выбор (без списка нужен селектор). Это может предоставить немного больше свободы для творчества, особенно если вы хотите иметь поля переменной ширины для определенных строк или несколько типов строк, которые все выглядят по-разному. Хотя полностью зависит от дизайна.

2 - Использование поля для каждой строки:

Если вы решили, что вам нужно несколько типов цветовых индикаторов и т. д., вам, возможно, придется использовать другой подход, предоставив атрибут поля (который, вероятно, не будет работать сразу)... Это относится к тому, как LayoutParams используются система компоновки. Я пытаюсь вспомнить точные детали, но я думаю, что это связано с различными типами подкласса LayoutParam и свойствами MarginLayoutParam (или его подкласса), например. marginLeft может игнорироваться кодом макета ListView. Вы должны использовать экземпляр AbsListView.LayoutParams, который не включает параметры полей. Один из способов — вложить вашу строку в представление контейнера (подкласс), который допускает поля в своих LayoutParams*. Я уверен, что не стал делать это постороннее вложение, но мне придется покопаться в каком-то коде, чтобы вспомнить лучшее решение.

Вы упоминаете о размещении ImageView и заполнении его цветом. Есть несколько альтернатив, на которые вы могли бы обратить внимание... Наиболее эффективным, вероятно, было бы определить свой собственный класс ListRow и использовать onDraw() для фактического рисования содержимого строки самостоятельно, canvas.draw_xyz() для рисования маленькой цветной вкладки, и рисовать текст и т. д. для остальных, а не строить строку в составном макете. Второй метод с использованием макетов заключается в том, чтобы, например, использовать более легкий ‹View layout_height="match_parent" layout_width="4dip" background="#ffff0000" /›.

* Золотое правило в макетах Android: сложная иерархия пользовательского интерфейса губительна для производительности, особенно с такими вещами, как ListView. Часто этого можно избежать, используя другие вещи: RelativeLayout, drawableTop(и т. д.), изображения с 9 патчами, а не добавляя больше представлений.

Если я неправильно понял, и вышеизложенное слишком просто, пожалуйста, не могли бы вы предоставить более подробную информацию, возможно, диаграмму, показывающую точную часть, которую вам нужно воспроизвести.

person richardleggett    schedule 20.05.2011

Как добиться чего-то подобного?

Внезапно я бы использовал View с желаемым цветом фона, видимым, когда вы этого хотите, и невидимым, когда вы этого не делаете.

Бонусом будет также переменная ширина, но я думаю, что это всего лишь меньшее изменение макета.

Я не знаю, что вы подразумеваете под «переменной шириной». Я думаю, что есть два Views с желаемым цветом фона в горизонтальном LinearLayout, из которых виден только один.

Я знаю, что, вероятно, мог бы сделать что-то вроде вставки туда ImageView, а затем заполнить его цветом, который я хотел бы, но мне кажется, что это уродливый хак.

Ну, ImageView немного тяжелее, чем нужно. В противном случае я не понимаю, почему это взлом. Думайте о них как о значках, которые просто оказались высокими, тонкими, серыми и не всегда нужными.

Еще один вопрос будет заключаться в том, существует ли общий способ объединения двух фрагментов ListView каким-либо образом, как это делают приложения GMail или Mail.

Используйте RelativeLayout, чтобы правый фрагмент мог плавать над левым фрагментом.

person CommonsWare    schedule 20.05.2011
comment
То есть этот белый маркер является частью правого фрагмента и просто рисует поверх левого? Это также означает, что в этом случае я должен синхронизировать рисование правого с положением прокрутки левого. - person Joa Ebert; 21.05.2011
comment
Спасибо за подсказку. На самом деле, если вы выберете самый верхний элемент и прокрутите его вверх, вы увидите, как белый маркер исчезает. Он является частью левого фрагмента и нарисован поверх правого. - person Joa Ebert; 21.05.2011
comment
@ Джоа Эберт: Значит, этот белый маркер является частью правого фрагмента и просто рисуется поверх левого? -- это один из подходов. Я запустил свой XOOM и внимательно посмотрел на пользовательский интерфейс вживую, а не на ваш фрагмент скриншота. Если вертикальная серая линия, разделяющая два фрагмента, является частью ListFragment слева, то наконечник стрелки также может быть частью этого фрагмента, как части выбранной строки. - person CommonsWare; 21.05.2011