Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: GUI и Java FX приложения > совет по правильной архитектуре gui


Автор: pycha 6.3.2017, 15:14
Приветствую. Хочу получить совет как правильно устроить архитектуру gui. 
У меня есть главный фрейм, к нему цепляю кучу панелек. Есть дефаултные панели, которые наследуются и дорисовуются дочерними, и потом дочерние добавляются во фрейм.  На каждую панель сделал отдельный файлик класса, чтобы не впихивать все в один файл исходников, да и в конце концов это же обьектно ориентированное программирование. 
Все было хорошо пока рисовал и размещал панели, теперь взялся за обработку событий и тут начался гемор.  Вот для примера изменение введенной строки в jTextFild в дефаултной панели, должен вызвать перерисовку фрейма с новыми данными.  Как правильно организовать общение между этими разными классами? 
Я более склоняюсь к мысли что надо сделать элементы , которые взаимодействуют статическими и так перебрасываться между собой сообщениями, на для примера myGui это главный клас, он создает фрейм  и хранит его в себе.  Тогда чтобы вызвать перерисовку из дефаултного класса  нужно будет просто сделать вот так myGui.frame.myRepaint(); это удобно и требует минимум кода , но считается ли это хорошим стилем?  
Есть еще вариант перебрасывать события от родительского к дочерним  окнам, но как это сделать я не знаю, да и это потребует дополнительных обработчиков событий, что мне кажется не красиво.
Еще вариант передавать от дочернего к родителю ссылки на фрейм , и прочие элементы , которые нужно задействовать.  это мне тоже очень режет глаза - нужно будет передавать много параметров, при чем почти в каждую новую созданную панель.

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


Автор: vpf 6.3.2017, 18:12
Если к хранилищу фреймов обращаются все, то надо выделять его в отдельный класс, чтобы все имели равные права.
Если обращается только один класс, то спрятать надо в него, и не париться. Ну я так думаю.

Автор: LSD 6.3.2017, 18:14
Цитата(pycha @  6.3.2017,  16:14 Найти цитируемый пост)
Вот для примера изменение введенной строки в jTextFild в дефаултной панели, должен вызвать перерисовку фрейма с новыми данными.

Ну там вообще-то должна быть модель, которая оповестит вью что надо перерисоваться.

Автор: pycha 7.3.2017, 13:52
vpf, там много классов. в основном все межклассовые обращения идут к фрейму, но в будущем еще возможно нужно будет добавить и общение между собой

Цитата

Ну там вообще-то должна быть модель, которая оповестит вью что надо перерисоваться. 


а можно об этом поподробней? гугл при запросе "java модель" выдает "Модель памяти Java" и почитав описание показалось что это не то, что ты мне хотел посоветовать

Автор: LSD 7.3.2017, 18:25
Цитата(pycha @  7.3.2017,  14:52 Найти цитируемый пост)
а можно об этом поподробней?

http://www.oracle.com/technetwork/java/architecture-142923.html

Автор: pycha 10.3.2017, 17:41
спасибо, это походу какраз то что мне нужно. и (надеюсь) последний вопрос.

решил разделить по образу MVC на gui, controller, data( по смыслу модель. класс , который хранит данные).   после изменения текста в jTextFild мне нужно чтобы перерисовались несколько комбобоксов на разных панелях и изменились данные в data.  решил сделать это так:  в ответ на изменение jTextFild вызываю определенный метод в конролере. передаю ему новый текст и индекс для комбобоксов, которые нужно изменить.  в этом методе в контроллере генерирую событие и все подписанные на это событие комбобоксы переписывают ячейку. таким же образом переписываются данные в data.

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

ps (не по теме) еще подскажите пожалуйста как можно поменять в ячейке комбобокса текст по индексу , ибо я в притул не не разглядел в документации метода, который это делает. Пришлось написать костыли такого типа
Код

               JCombobox tableCB = new JCombobox("1","2","3");
               int i = 1; 
               tableCB.insertItemAt("new text" ,i);
               tableCB.removeItemAt((i+1));


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

Автор: pycha 11.3.2017, 23:15
с событиями сам разобрался. Вопрос насчет изменения текста ячейки комбобокса  актуален

Автор: LSD 13.3.2017, 15:58
Ты про прежнему неправильно работаешь с моделью. Ты должен менять модель, а модель (правильно написанная) сама оповестит об изменении всех кому это надо:
Код

package com.hp.test;

import javax.swing.*;
import java.io.Serializable;
import java.util.Vector;

public class Main {
    public static void main(String[] args) throws Exception {
        MyComboBoxModel<String> comboBoxModel = new MyComboBoxModel<>(new String[]{"1", "2", "3"});
        JComboBox<String> tableCB = new JComboBox<>(new String[]{"1", "2", "3"});
        comboBoxModel.setElementAt("B", 1);
    }

    public static class MyComboBoxModel<T> extends AbstractListModel<T> implements MutableComboBoxModel<T>, Serializable {
        protected Vector<T> objects;
        protected Object    selectedObject;

        public MyComboBoxModel() {
            objects = new Vector<T>();
        }

        public MyComboBoxModel(T[] items) {
            objects = new Vector<T>(items.length);

            int i, c;
            for (i = 0, c = items.length; i < c; i++) {
                objects.addElement(items[i]);
            }

            if (getSize() > 0) {
                selectedObject = getElementAt(0);
            }
        }

        public MyComboBoxModel(Vector<T> v) {
            objects = v;

            if (getSize() > 0) {
                selectedObject = getElementAt(0);
            }
        }

        public void setSelectedItem(Object anObject) {
            if ((selectedObject != null && !selectedObject.equals(anObject)) ||
                selectedObject == null && anObject != null) {
                selectedObject = anObject;
                fireContentsChanged(this, -1, -1);
            }
        }

        public Object getSelectedItem() {
            return selectedObject;
        }

        public int getSize() {
            return objects.size();
        }

        public T getElementAt(int index) {
            if (index >= 0 && index < objects.size()) {
                return objects.elementAt(index);
            } else {
                return null;
            }
        }

        public int getIndexOf(Object anObject) {
            return objects.indexOf(anObject);
        }

        public void addElement(T anObject) {
            objects.addElement(anObject);
            fireIntervalAdded(this, objects.size() - 1, objects.size() - 1);
            if (objects.size() == 1 && selectedObject == null && anObject != null) {
                setSelectedItem(anObject);
            }
        }

        public void insertElementAt(T anObject, int index) {
            objects.insertElementAt(anObject, index);
            fireIntervalAdded(this, index, index);
        }

        public void setElementAt(T newValue, int index) {
            objects.insertElementAt(newValue, index);
            fireContentsChanged(this, index, index);
        }

        public void removeElementAt(int index) {
            if (getElementAt(index) == selectedObject) {
                if (index == 0) {
                    setSelectedItem(getSize() == 1 ? null : getElementAt(index + 1));
                } else {
                    setSelectedItem(getElementAt(index - 1));
                }
            }

            objects.removeElementAt(index);

            fireIntervalRemoved(this, index, index);
        }

        public void removeElement(Object anObject) {
            int index = objects.indexOf(anObject);
            if (index != -1) {
                removeElementAt(index);
            }
        }

        public void removeAllElements() {
            if (objects.size() > 0) {
                int firstIndex = 0;
                int lastIndex = objects.size() - 1;
                objects.removeAllElements();
                selectedObject = null;
                fireIntervalRemoved(this, firstIndex, lastIndex);
            } else {
                selectedObject = null;
            }
        }
    }
}

Основное внимание на метод setElementAt()
там вначале меняются данные, а затем вызывается fireContentsChanged() вот именно он и скажет комбобоксу, что надо перерисовать строчку 2.

Автор: pycha 14.3.2017, 01:39
завтра разберусь с кодом но еще раз прочитал о принципе mvc  и понял что  действительно не так у себя все устроил.

Цитата

    Модель (Model) предоставляет данные и реагирует на команды контроллера, изменяя своё состояние[1];
    Представление (View) отвечает за отображение данных модели пользователю, реагируя на изменения модели[1];
    Контроллер (Controller) интерпретирует действия пользователя, оповещая модель о необходимости изменений[1].


я было так : все слушатели на изменение регистрируются в контроллере. введенный текст передается контроллеру и оттуда пересылается всем слушателям ( и в модель и в представление). 
Теперь если я правильно понял, то мне нужно сделать иначе : все слушатели(комбобоксы) регистрируются в модели. введенный текст передается  контроллеру , контроллер перенаправляет его в модель .  модель изменяет свои данные и оповещает  всех слушателей ( тоесть комбобоксы в view) что изменились данные и нужно переписать ячейку. верно?



 ну и еще тогда уж уточню о точной реализации. как лучше правильно инициализировать контроллер и модель? в классе  MyGui создать по одному статическому обьекту класса ( и тогда к ним можно будет обращаться MyGui.model.tableListeners.addTableListener(...) ) ,  или полностью статические методы сделать чтобы можно было напрямую Model.tableListeners.addTableListener(...) ? 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)