Nano Hash - криптовалюты, майнинг, программирование

В разработке Android связь между классом и макетом xml

Предыстория: ранее я спрашивал этот вопрос по аналогичной теме о том, как передать переменную экземпляра из класса Java в соответствующий файл макета в разработке для Android. Оказывается, нет простого способа сделать это, поэтому следующий вопрос — поиск хорошего способа, позволяющего xml-файлу и классу общаться друг с другом.

Вопрос. Я разрабатываю игру (почти закончил на Java Swing, теперь превращаю ее в приложение для Android). Он содержит несколько уровней, каждый с шахматной доской и шахматными фигурами разного размера - во всех смыслах и целях это приложение для шахматных головоломок, которое отображает новую головоломку на доске разного размера после того, как игрок решил предыдущую головоломку. Бизнес-логика завершена, но я работаю над графикой. В начале игры статический класс BoardFragment (содержащийся в BoardContainer.class) с соответствующим XML-файлом макета fragment_board.xml должен отображать размер доски (как GridLayout) и шахматные фигуры, соответствующие первому уровню, которые затем обновляются по мере завершения уровня. Для пояснения код выглядит примерно так (пропустите до конца, чтобы увидеть мою настоящую проблему):

//Here is BoardFragment.class, static class contained by BoardContainer:

public static class BoardFragment extends Fragment {

     public BoardFragment() {
     }

     public int level=0;

     public void buttonPressed(View view) {
     //method that will advance the game and update "level" 
     //depending on which button was clicked
     }


         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_board,
                container, false);
         return rootView;
         }
     }

//This is fragment_board.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.exampletest.MainGame$BoardFragment" >

    <android.support.v7.widget.GridLayout
        xmlns:app="schemas.android.com/apk/res-auto"
        android:id="@+id/gridView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        app:columnCount="4" >

        <ImageButton
            android:id="@+id/imageButton1"
            android:layout_width="49dp"
            android:layout_height="49dp"
            android:contentDescription="@null"
            android:onClick="buttonPressed"
            android:src="@drawable/grid11" />

        </android.support.v7.widget.GridLayout>

</RelativeLayout>

Обратите внимание, что для простоты и пространства в XML-файле сейчас находится только одна кнопка изображения, но если app:columnCount равен 4, их будет 16. На самом деле число должно зависеть от переменной экземпляра «уровень» в соответствующем классе ( так что если level==5, то, возможно, app:columnCount==6 и так далее), но я не уверен, как мне передать это из файла класса в xml. Это вообще возможно? На самом деле, когда уровень пройден, размер доски и количество фигур должны измениться. Следует ли это делать

  • Иметь один файл фрагмента xml для каждого уровня? Тогда предыдущий вопрос решен, но для многих уровней это приведет к большому количеству файлов фрагментов - это лучшая практика?

  • Иметь один XML-файл фрагмента, который обновляется по мере завершения уровня?

Я ценю любую помощь.


Ответы:


1

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

Например, ваш макет может быть адаптирован для разделения ресурсов макета для макетов доски и ячеек:

fragment_board_grid.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.exampletest.MainGame$BoardFragment" >

    <android.support.v7.widget.GridLayout
        android:id="@+id/gridView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

fragment_board_cell.xml

<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="49dp"
    android:layout_height="49dp"
    android:contentDescription="@null"
    android:onClick="buttonPressed"
    android:src="@drawable/grid11"
    tools:context="com.example.exampletest.MainGame$BoardFragment" />

Теперь их можно динамически надувать для создания доски:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_board_grid,
            container, false);
    GridLayout boardLayout = (GridLayout) rootView.findViewById(
            R.id.gridView);
    boardLayout.setColumnCount(columnsNum);
    boardLayout.setRowCount(rowsNum);
    for (int columnIndex = 0; columnIndex < columnsNum; columnIndex++) {
        for (int rowIndex = 0; rowIndex < rowsNum; rowIndex++) {
            ImageView cellView = (ImageView) inflater.inflate(
                    R.layout.fragment_board_cell, boardLayout, false);
            // Here you can customize it, add listeners, etc.
            boardLayout.addView(cellView);
        }
    }
    return rootView;
}

При запуске следующего уровня вам нужно будет заново надуть макет доски с нуля. Вы можете подойти к этому двумя способами:

  1. Сделайте так, чтобы каждый экземпляр вашего Fragment представлял собой определенную доску. Это сделало бы логику Fragment чрезвычайно простой и сфокусированной только на одном игровом уровне. Логика запуска нового уровня будет обрабатываться его Activity путем замены существующего Fragment новым экземпляром. Это также имело бы то преимущество, что анимацию для начала нового уровня было бы чрезвычайно легко реализовать, поскольку FragmentTransaction имеют встроенную поддержку анимации.

  2. Поручите Fragment обрабатывать все. Этого можно добиться путем создания универсального метода, который будет раздувать новый макет доски по запросу и делегировать ему функции обратного вызова onCreateView(), а затем реализовать метод, удаляющий текущий Fragment. макет от его родителя и заменить его новым макетом, сгенерированным методом наполнения доски. Корень макета можно получить из метода getView() (или, в случае библиотеки поддержки, это первый дочерний элемент, поскольку библиотека оборачивает исходный макет Fragment, чтобы блокировать сохранение состояния View методом Activity).

28.07.2014
  • Большое спасибо, это ответ высшего качества. Однако я не мог запустить это в Eclipse (вечные проблемы с android.support.v7.widget.GridLayout уже давно, и теперь он не может найти новые файлы .xml по причине, которая мне недоступна). Я перехожу на Android Studio, я попробую этот метод там, как только все заработает. 29.07.2014
  • Переменная ячейка не определена, вы имели в виду cellView? Хотя тогда я, кажется, получаю исключение нулевого указателя, не знаю почему. 29.07.2014
  • @Sid: Да, я имел в виду cellView - изначально я назвал его ячейкой и забыл обновить ссылку при переименовании (я писал код прямо в поле ввода публикации). Я исправил это сейчас. Если вы получаете NullPointerException в строке этой, это будет означать, что boardLayout равно нулю, что означает, что у вас нет View с идентификатором gridView в ресурсе макета сетки. 29.07.2014
  • В моем представлении GridLayout в fragment_board.xml действительно есть идентификатор GridView. Но root в root.findViewById возвращает ошибку (не может получить символ), вы уверены, что он там? Я удалил его и добавил setContentView(R.layout.fragment_board_grid) раньше, так что теперь код компилируется и работает правильно, хотя таинственным образом я все еще получаю фатальную ошибку (вероятно, потому что этот код находится во фрагменте?). Если вы знаете в чем проблема, то было бы неплохо, а иначе без проблем, потому что тогда я задам еще один вопрос в сети. 29.07.2014
  • @Sid: Извините, снова моя ошибка - вместо root должен быть rootView (исправлено). setContentView() используется для установки содержимого Activity, а не Fragment, поэтому вам следует вернуть исходный формат, если вы работаете внутри Fragment. 29.07.2014
  • Спасибо, теперь он работает (но для других после этого ответа: строка xmlns:app="schemas.android.com/apk/res-auto" нужна в GridView в файле xml, по крайней мере, на моем компьютере). Однако приложение ведет себя не так, как ожидалось, поскольку CellView выстроены в одну строку, а не заполняют доску для любых значений columnsNum и rowsNum больше единицы. Вы знаете, почему это происходит? Приложение также вылетает при переходе из портретного режима в ландшафтный (и наоборот), но я надеюсь, что этой проблемы не возникнет, если я вставлю копии файлов xml в каталоги land и port. 30.07.2014
  • @Sid: Да, вам нужно будет определить пространство имен пользовательского приложения, если вы собираетесь назначить атрибуты библиотеки поддержки GridLayout в ресурсе макета (хотя вам нужно будет добавить префикс схемы http://) - я просто скопировал XML из ваш вопрос. Я добавил динамическое распределение размеров строк/столбцов, что должно решить проблему однострочного макета (см. мое последнее редактирование кода). Я создал тестовое приложение в соответствии с кодом в моем ответе, и оно ведет себя правильно при изменении ориентации - вы должны опубликовать новый вопрос с подробностями о сбое и трассировке стека. 30.07.2014
  • Да, эта ошибка GridLayout определенно была моей ошибкой, я обновлю свой вопрос этой строкой. Вы были очень полезны. Я посмотрю, смогу ли я что-то сделать с ошибкой ориентации сегодня, и если нет, то я отправлю новый вопрос. 30.07.2014
  • Я понял ошибку; Я удалил ключевое слово static из класса BoardFragment, пытаясь заставить работать setContentView(), а затем забыл вернуть его (что сделало его зависимым от класса BoardContainer). Теперь все работает как положено, дело закрыто! Меня интересует несколько других вещей, например, как по-разному определить макет в книжной и альбомной ориентации, но сначала я поиграю со слушателями и так далее, прежде чем задавать новый вопрос в сети. Спасибо. 30.07.2014
  • @Sid: вам следует просмотреть документацию для получения основных руководств и ссылок. Например, вы можете узнать подробности о том, как предоставить внешние ресурсы для нескольких конфигураций здесь. Вам также следует подумать о приобретении хорошей книги по разработке для Android, если вы новичок в этом — здесь задействовано множество новых концепций, хотя вы можете не столкнуться с большинством из них. К сожалению, я не могу дать вам какие-либо конкретные текущие рекомендации, так как я давно не занимался разработкой Android. 30.07.2014
  • Я задал дополнительный вопрос здесь: gridl" title=" рисование шахматной доски в Android с пользовательской рамкой с использованием изображений и gridl"> stackoverflow.com/questions/25100161/ 03.08.2014

  • 2

    Хорошим способом динамической обработки макетов является их программная генерация. Это не так просто, как разместить все в файле XML, но это даст вам гораздо больше гибкости. Вот пример:

    Как программно создать RelativeLayout с двумя кнопками, расположенными одна над другой?

    27.07.2014
  • Спасибо, теперь я читаю об этом (это сложно!). Один вопрос: при использовании xml Android позволяет создавать несколько макетов для каждого размера экрана. Есть ли хороший способ сделать это с помощью вашего метода? 27.07.2014

  • 3

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

    Например...

    //the layout you will work with
    GridLayout layout = (GridLayout) findViewById(R.id.gridLayout);
    
    //properties for button
    Button btn = new Button(this);
    btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); //this will depend how you want the view to look
    btn.setText("My button created programmatically");
    btn.setId(someId);
    
    //add button to the layout
    layout.addView(btn);
    
    if(level == whateverlevel){ //pseudo code here obviously
        //make a new button/buttons in here like above and add it to your view depending on your level
    }
    
    27.07.2014
  • Можно ли тогда работать с разными размерами экрана? Этот фрагмент кода должен быть в моем методе onCreateView в BoardFragment? 27.07.2014
  • @Sid, да, вы все еще можете программно создавать представления, которые работают для разных размеров экрана, устанавливая соответствующие параметры макета и т. Д. Кроме того, да, этот код будет находиться в onCreateView ... однако я бы рекомендовал сделать это самостоятельно. вспомогательный метод createBoard(int level)... затем вызовите этот помощник в onCreateView и передайте номер уровня 28.07.2014
  • Отлично, вы были очень полезны. Для всех, кто читает это, я думаю, что это руководство хорошо для начала: techotopia.com/index. php/. О настройке разных параметров макета для разных размеров экранов, как программно узнать размер экрана? Есть ли какой-нибудь метод, который я могу поместить в оператор if, например isScreenSizeLarge() или что-то в этом роде? (С файлами xml у меня могут быть разные папки, например, большие или xlarge, и мобильный телефон выберет нужную в зависимости от размера экрана). 28.07.2014
  • @Sid: Вам было бы лучше определить свои отдельные View (в отличие от всего макета) - или, по крайней мере, размеры - в ресурсах макета XML и раздуть их, вместо того, чтобы создавать их непосредственно в вашем коде. Таким образом, вы сможете создать динамическую компоновку, сохранив при этом все преимущества внешних ресурсов. 28.07.2014
  • @corsair992 Было бы идеально, если бы вы взяли лучшее из обоих миров. Возможно ли, что вы можете проиллюстрировать это примером (может быть, в отдельном ответе?) или ссылкой на ресурс, где этот метод использовался? В любом случае, спасибо за это предложение. 28.07.2014
  • @Sid: Хорошо, я добавил ответ, демонстрирующий, как динамически генерировать макет путем раздувания из отдельных View, определенных в ресурсах макета XML. 28.07.2014
  • @Sid, дай мне знать, если у тебя остались вопросы. Извините, что не ответил, весь день был на работе. 29.07.2014
  • @JoshEngelsma Спасибо за это, я буду! 29.07.2014
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..