Заставить gridview нарисовать все плитки - PullRequest
5 голосов
/ 15 октября 2011

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

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

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

Спасибо.

Редактировать: Чтобы уточнить - В моем тайлеадаптере я установил количество дочерних элементов равным 225. В моем виде сетки вызов getChildCount () возвращает 165.

Редактировать еще раз: это происходит только тогда, когда высота сетки больше, чем у экрана - дочерние объекты, которые находятся вне экрана по оси Y, просто вычитаются из дочернего счета - устанавливая размердети в число, где все они плотно прилегают к экрану, устраняют проблему, но убивают цель прокрутки.

Код!

XML Схема деятельности:

  <LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:theme="@style/Theme.Custom"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <TextView android:id="@+id/logmessage"
  android:theme="@style/Theme.Custom"
  android:layout_width="fill_parent"
  android:layout_height="25dip"
  android:text="LogMessage"/>

  <RelativeLayout android:id="@+id/boardwrap"
  android:layout_weight="1"
  android:layout_height="fill_parent"
  android:layout_width="fill_parent"
  android:gravity="center_vertical">
  <com.MyProject.GameGrid 
    android:id="@+id/board"
    android:theme="@style/Theme.Custom"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:numColumns="15"
    android:stretchMode="none"
    android:verticalSpacing="0dip"
    android:horizontalSpacing="0dip"
    android:padding="0dip"
    android:columnWidth="20dip"
    android:scrollbars="none"/>
</RelativeLayout>
<RelativeLayout 
    android:id="@+id/toolbar"
    android:layout_width="fill_parent"
    android:layout_height="60dip"
    android:background="#FFFFFFFF"/>
</LinearLayout>

Упражнение:

public class GameBoardActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.gameboard);

        GameGrid Board = (GameGrid)findViewById(R.id.board);
        Board.setAdapter(new TileAdapter(this));
    }
}

GameGrid:

public GameGrid(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setNumColumns(15);

        DisplayMetrics metrics = new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
        scale = metrics.density;
        smallSize = Math.round(20 * scale);
        largeSize = Math.round(40 * scale);

        columnwidth = largeSize;
        this.setColumnWidth(columnwidth);
        Common.DebugMessage(Float.toString(columnwidth));

    }

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

Прокрутка (с чем вы мне помогли ранее)

if (myState == TOUCH_STATE_SCROLLING) {
                    final int deltaX = (int) (mLastX - x);
                    final int deltaY = (int) (mLastY - y);
                    mLastX = x;
                    mLastY = y;

                    int xpos = this.getScrollX();
                    int ypos = this.getScrollY();

                    int maxX = (columnwidth * 15) - super.getWidth();
                    int maxY = (columnwidth * 15) - super.getHeight();

                    if (xpos + deltaX >= 0 && xpos + deltaX <= maxX && ypos + deltaY >= 0 && ypos + deltaY <= maxY )
                    {
                        this.scrollBy(deltaX, deltaY);
                    }
                    else {
                        this.scrollTo(xpos + deltaX <= 0 ? 0 : xpos + deltaX >= maxX ? maxX : xpos + deltaX,
                                      ypos + deltaY <= 0 ? 0 : ypos + deltaY >= maxY ? maxY : ypos + deltaY);
                    }
                    Common.DebugMessage(this.getChildCount());

                }

Common.DebugMessage - это просто вспомогательный метод для печати отладочных сообщений в LogCat

TileAdapter:

public TileAdapter(Context c) {
        mContext = c;
    }

    @Override
    public int getCount() {
        return 225;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        int colWidth = ((GameGrid)parent).getColumnWidth();
        if (convertView == null) {
            imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(colWidth , colWidth));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(0, 0, 0, 0);
        }
        else {
            imageView = (ImageView)convertView;
        }
        imageView.setImageResource(R.drawable.tile);
        return imageView;
    }

Ответы [ 4 ]

5 голосов
/ 19 октября 2011

Andreas,

Если ваша проблема просто onDraw(). Вы можете сделать это довольно легко с помощью переопределенного Draw(canvas) в вашем GridView. Это имеет побочный эффект увеличения требований к процессору, пока ваша активность загружена, но может создать желаемый эффект. Такое переопределение будет следующим:

 //This may be used in your GridView or Activity -- whichever provides the best result.
    public void draw(Canvas canvas)
    {   int _num = myGridView.getChildCount();
        for (int _i = _num; --_i >= 0; )
        {   View _child = (View)myGridView.getChildAt(_i);
            if (_child != null)
                _child.draw(canvas);
        }
    }

Дополнительная техника (РЕДАКТИРОВАТЬ)

Иногда переопределение draw() может иметь неблагоприятные последствия. Что мы действительно пытаемся сделать, так это вызвать объекты для рисования, когда они доступны. Переопределение invalidate() подобным образом часто может иметь эффект в зависимости от того, где находится проблема. Поскольку мы установили, что мы получаем странные результаты с переопределением draw(), это, похоже, следующий курс действий.

//This definitely goes in your GridView
public void invalidate()
{   int _num = myGridView.getChildCount();
    for (int _i = _num; --_i >= 0; )
    {   View _child = (View)myGridView.getChildAt(_i);
        if (_child != null)
            _child.invalidate();
    }
}

Следует понимать, что этот метод может не вызывать отрисовку, когда вы хотите, а просто дает ОС знать, что она готова к перерисовке, когда она станет доступной. Поскольку аннулирование не распространяется автоматически вниз по дереву View, если ваши ImageView вложены глубже, чем GridView, вам придется настроить. Это можно использовать вместе с draw() или независимо. Кроме того, при использовании с соответствующим размещением оператора invalidate() может снизить отклик, но это поможет сохранить ваши изображения нарисованными.

Возможно, проблема связана с задержкой макета или рисования. Если это так, то решение Lazy Loading может быть лучшим. Ленивый загрузчик - это способ загрузки контента, когда он необходим, так что он обычно использует меньше памяти и обработки, а также показывает, что нужно, когда это необходимо. Теперь я не удивительный в Ленивой Загрузке, потому что у меня редко есть потребность. Но есть отличный пример кода на этом сайте . Он также предназначен для GridView.

Считается не применимо (но может быть полезно для других)

Единственная проблема другого типа, о которой я думаю, это проблема нехватки памяти, которая не вызывает принудительное закрытие (и да, они существуют). Они особенно неуловимы и болезненны в уходе. Единственный способ обнаружить это в LogCat во время загрузки или прокрутки с выбранным видом ошибки. Или вы можете пройти весь дамп LogCat (я рекомендую очистить журнал перед первым запуском приложения).

При возникновении такого рода проблем становится важным знать, как работает жизненный цикл изображения. Если ваши изображения уменьшены до размера миниатюр для отображения, я бы подумал о создании настоящих миниатюр наряду с полноразмерными изображениями. Поскольку сжатие изображения во время выполнения временно требует больше памяти, чем сохранение его исходного размера. Поскольку все это происходит одним махом, это может быть временной проблемой, проявляющейся постоянно. Это значительно снизит требования к памяти. (Например, для изображения 2x2 требуется 16 байтов плюс заголовок, тогда как для изображения 4x4 требуется 64 байта плюс заголовок [400% для двойного размера !!].)

Кроме того, добавление System.gc () в критические места в вашем коде вызовет сборку мусора, чаще освобождая память. (Это не гарантия освобождения памяти, но она работает чаще, чем не работает).

Лучшее решение, вероятно, было бы комбинацией всех трех, но потребовало бы немного больше информации, чем то, что у нас есть с этим вопросом. Например, нам нужно выяснить, переопределили ли вы draw() и onMeasure() и onLayout() и, возможно, некоторые другие детали.

4 голосов
/ 31 октября 2011

Честно говоря, я предлагаю прекратить использование API платформы таким образом, чтобы оно явно не предназначалось для использования. Даже если через некоторые трудности вам удалось сыграть достаточно игр с базовым кодом GridView, чтобы заставить его делать то, что вы хотите ... насколько вы уверены, что ваш код продолжит работать с небольшими изменениями в реализации GridView в качестве платформы эволюционирует.

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

Если поведение GridView не то, что вам нужно, решение состоит в том, чтобы написать свой собственный вид, который делает то, что вы хотите. А с Android это еще проще, потому что вы можете просто взять и взять код GridView с платформы с открытым исходным кодом в качестве основы, получить компиляцию в своем приложении (вам, вероятно, понадобится несколько настроек, потому что код в его нынешнем виде требует не нужно писать исключительно против SDK, так что, вероятно, нет ... но есть ничего , это делает то, что вы не можете сделать в обычной сборке приложения против SDK), а затем модифицируйте этот код в своем приложении так, как вам хочется, чтобы он делал то, что хочет. Без борьбы со встроенным виджетом, который на самом деле не делает то, что вы хотите. И не опасаясь, что ваш тщательно сконструированный карточный домик обрушится на вас, если в будущем изменится реализация GridView.

0 голосов
/ 16 февраля 2012

переопределяет getCount() вашего адаптера и возвращает длину базового массива для вашего адаптера. Затем используйте mAdapter.getCount вместо mGrid.getChildCount.getChildCount возвращает только видимых потомков, тогда как getCount выдаст вам всего потомков в вашем наборе данных.

0 голосов
/ 26 октября 2011

Вместо этого я отправил другой ответ, потому что после разъяснения проблема определенно отличается от того, во что я изначально верил. Тем не менее, приемы, описанные в предыдущем ответе, не следует забывать, и они являются отличным ресурсом для решения других связанных с этим вопросов, поэтому я храню их там.

Проблема:

GridView теперь рисует (вроде), но повторяет одни и те же изображения снова и снова, что приводит к ужасным артефактам. Получающиеся в результате артефакты настолько плохи, что на самом деле не дают хорошего указания относительно того, присутствуют ли другие виды. Эта проблема на самом деле вызвана проблемой "слишком большой прозрачности".

Android и прозрачность

Android прекрасно справляется с прозрачностью обработки, позволяя объектам ниже отображать текущий вид, пока фокус top находится в фокусе. Однако, если все представления прозрачны, Android ничего не сможет отследить, когда ему нужно будет обновить. Обычно это не проблема.

Как правило, разработчики используют предоставленные нам инструменты и просто стараются сделать с ними что-то интересное. (YAY!) И пока мы их используем, Android говорит (в значительной степени): «Я знаю, что делать!» Однако, когда мы начинаем с пользовательских видов, Android может немного испугаться, особенно когда дело доходит до рисования соответствующим образом.

Ваш GridView на самом деле не GridView. Это расширенный GridView. Как расширение представления Android, Android не делает никаких предположений о том, как его следует рисовать, оооочень .... В результате нормального непрозрачного фона для вашего GridView не существует. Можно подумать, «но у меня есть это в RelativeLayout. Разве этого не достаточно?» Ответ - нет. Объекты макета являются объектами макета. У них нет фона, если мы не укажем их.

Это усугубляется, когда вы выполняете прокрутку и другие подобные движения или анимацию. В этих случаях Android пытается кешировать. Кэширование происходит как бы ниже всех просмотров, но в результате получается, что, поскольку все прозрачно, вы видите весь процесс. И он не сбрасывается, пока не потребуется очистить кеш. Вот почему становится все хуже и хуже.

Другими словами, «окно» может выглядеть черным, но на самом деле не черным ...

Решение (часть I):

Таким образом, ответ заключается в том, чтобы установить непрозрачный фон либо в расширенный GridView, либо в один из его родительских видов. Это должно устранить артефакты, которые вы видите при прокрутке. И это должно позволить вам правильно увидеть, как и если другие виды рендеринга. В конечном счете, вы хотите попробовать применить фон к верхнему представлению, которое охватывает все представления. Задать фон можно так же просто, как установить цвет фона (так как он создает непрозрачный рисунок).

Следующие шаги:

Я заметил, что вы пропустили соответствующий код. Мне действительно нужно знать, что за Adapter ваш TileAdapter расширяет и как он получает свою базовую информацию. Это может повлиять на то, как оно получает и присваивает события розыгрыша. Как только мы узнаем это, мы сможем исправить все остальное.

Мне также нужно знать, как вы позиционируете тайлы (код позиционирования отсутствует). Поскольку код позиционирования отсутствует, нет способа узнать, действительно ли добавляются представления. Поведение по умолчанию для ImageViews состоит в том, что если хотя бы некоторая их часть не видна, они не добавляются в иерархию до тех пор, пока они не будут видны. Мы хотим вызвать эту проблему

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

Альтернативный результат

Что-то, я бы хотел, чтобы с играми с прокруткой чаще случалось, если бы они запустили с уменьшенной позицией, и мы могли бы "переместиться" в увеличенную позицию. Это может легко разрешить ваш ChildCount, так как вы сказали, что если он аккуратно умещается на экране, они все рисуют. И первоначальное увеличение может произойти после того, как все будет загружено. Тогда у вас есть хороший графический эффект, указывающий, что загрузка завершена. Уменьшение - это обычная анимация, которую легко реализовать. и вы знаете, что все ваши дети загружены. Кроме того, это может ограничить код, который вы должны ввести, чтобы заставить его работать правильно. Наконец, все это можно сделать с помощью одного и того же объекта GameGrid. :)

Я не знаю, думали ли вы об этом, но я подумал: «Эй, похоже, это простой способ убить более двух птиц одним камнем».

...