3 - Фигури

Интерфейс в Java

Продължаваме със създаването на нов интерфейс (но не графичен :)

nov-interfeis

За добро или лошо Java не поддържа множествено наследяване, т.е. един клас не може да наследи други два. Един интерфейс обаче може да наследи два или повече интерфейса. Също така един клас може да реализира (имплементира) два интерфейса.

Нашият интерфейс с име „Primitive“ ще бъде реализиран от всичките ни фигури - правоъгълници, елипси и други. За сега той ще има един метод „draw“, който ще изрисува конкретната фигура.

podskazka-klas

Методът ще приема като параметър обект от тип „Graphics“. Започвайки писането му, NetBeans ни го подсказва. Като щракнем на него, автоматично горе се добавя пакетът „java.awt.Graphics“.

dobaviane-paket

В тази версия на NetBeans, когато текущият файл не е запазен името му е удебелено.

zapazvane

С Ctrl+Shift+S запазваме всички отворени файлове.

Класове за фигури

Създаваме нов клас „MyRectanlge“, който реализира интерфейса „Primitive“ и наследява класа „Rectanlge“. Преди да сме имплементирали метода „draw“ NetBeans ни съобщава за грешка. Можем да щракнем на подсказката (лампичката в ляво) и да изберем „Implement all abstract methods“.

implementirane

Това ще се създаде „празен“ метод „draw“.

implementirane

 Добавяме поле от тип „Color“. Забележете, че NetBeans ни подсказва дори как може да се казва нашата променлива.

predlojenie-promenliva

 Щракаме с десния бутон някъде в класа и избираме „Insert Code“.

vmukvane-kod

Искаме да вмъкнем код за достъп и промяна на полето „color“.

getter-setter

Затова избираме него.

getter-setter

И така NetBeans генерира код за нас. Можехме да го напишем и ръчно разбира се.

По същия начин добавяме и конструктор.

konstruktor

Избираме конструктор от наследения клас и кои полета искаме да се инициализират от нашия.

konstruktor

Допълваме автоматично създаденият конструктор, като присвояваме съответните стойности на полетата x, y, width, height. Те са наследени от класа Rectangle.
След това можем да щракнем на „Format“, за да ни се подреди автоматично кодът.

formatirane

Класът ни „MyRectangle“ започва да изглежда така:

myrectangle

Създаваме клас „MyEllipse“ и повтаряме горните стъпки.

Глобални променливи

След това правим нов клас „GlobalVars“. В него ще съхраняваме данни и код общ за цялото ни приложение. Като начало това са:

  • списъкът с фигурите;
  • точката, в която е натисната мишката върху панела за рисуване;
  • обектът от тип „Graphics2D“, по който ще рисуваме;
  • началните координати, ширината и дължината на последно нарисуваната при влачене на мишката фигура.

Всички те са глобални за класа променливи (с ключовата дума static пред тях). Добавяме и метод (clearLastRect), който да изчиства платното за рисуване, като изрисува правоъгълник върху последно нарисуваната фигура. Внимаваме да я покрием добре, като добавим по един пиксел към нейните размери.

globalni-promenlivi

По нататък, за удобство, може да наричаме „figures“ и масив, но реално това е списък.

Обработка на събития

Връщаме се на графичния интерфейс, където за удобство променяме името на панела, в който ще рисуваме на „jPanelCanvas“.

ime-panel

Щракаме с дясното копче върху него и избираме събитието „натискане на мишката“.

natiskane-mishka

Обработваме събитието (evt), като вземаме координатите, в които е натисната мишката и ги запазваме, като точка, при глобалните променливи.

natiskane-mishka-kod

Добавяме събитието влачене, което представлява местене на мишката при натиснат бутон.

mishka-vlachene

При него има доста код.
Първо изчистваме последно нарисуваната фигура. При първото извикване на метода няма такава, но при следващите ще има.
След това пресмятаме ширината и дължината на фигурата, която ще рисуваме.
Правим проверка накъде се влачи мишката - надолу и надясно или нагоре и наляво и определяме началните точки - x, y.
Запазваме при глобалните променливи координатите и размера на фигурата.
И накрая правим проверка кой от трите ни бутона е натиснат.
Ако е бутонът „Избиране“ настройваме очертанието на рисуваната фигура да бъде прекъсната линия. Масивът float[]{6, 3} би трябвало да означава, че имаме 6 пиксела линия следвана от 3 пиксела празно място. Експериментирайте.
След това, както и при другите два бутона рисуваме фигурата.
drawRect(x, y, width, height) рисува правоъгълник с горен ляв ъгъл x, y и ширина и височина съответно width и height.
drawOval(x, y, width, height) рисува елипса, която се помества в правоъгълник с горен ляв ъгъл x,y и ширина и височина съответно width и height.

mishka-vlachene-kod

Добавяме събитието „пускане на мишката“, при което изчистваме последно нарисуваната при влаченето фигура, правим очертанието да е стандартното и прерисуваме панелът, за да избегнем възможни остатъци от фигури по него.

mishka-puskane

Преди да пробваме какво сме направили добавяме и събитието „преоразмеряване на панела“, при което обновяваме полето „graphics2D“. Това събитие настъпва и при самото стартиране на програмата, по-точно при изобразяването на панела на екрана. Ако изпуснем тази стъпка е възможно при увеличаване на прозореца да продължим да рисуваме на по-малка площ или обратното.

preorazmeriavane

Ето го и третият изглед от програмата ни.

izgled

Това е кодът за сега.

Всичко направено до тук изрисува фигури при влачене на мишката. Те обаче са временни - когато пуснем бутона на мишката изчезват.
Във времето до публикуване на следващата статия се опитайте сами да направите така, че фигурите да се запазват в паметта.

Това ще стане като се върна (пак от планината :) Ако на някой му се гледат снимки от там да ми пише.

Коментари

Грешка :)

Смени си фона на панела в който рисуваш. Получава се бъг, поне при мен. Ако е сив както е по дефолт не се вижда защото цветовете съвпадат.

Wikipedia: По същество бъгът

Wikipedia:

По същество бъгът представлява неочаквано поведение на програмата в определена ситуация.

В случая аз не виждам неочаквано поведение. Ти си променил фона си, но не си променил изчистването му, следователно е нормално да се получава този резултат.

Ако вече си го оправил може да споделиш как да го направят и другите :)

Ето решението ми :)

public static void clearLastRect() {
        graphics2D.setBackground(Color.WHITE);
        graphics2D.clearRect(lastX-1, lastY-1, lastWidth+2, lastHeiht+2);
 }

Пробвах да го оправя и само за това се сетих. За момента забих на изчертаването на обектите, та не знам дали няма да стане препокриване някъде, но ще видим като стигнеш до там :)

^

Проблем

Аз съм малко по-напред с проекта, но имам един проблем. Когато минимизирам прозореца и след това го покажа отново, всичко което съм чертал изчезва. Как мога да го накарам да се преначертава наново? Имам метод който преначертава всичко, но не знам какво събитие да ползвам?

Аз също не знаех, но преди

Аз също не знаех, но преди малко го налучках :)

За нашия проект важи правилото: защо да помня нещо, след като мога чрез логични разсъждения да стигна до него.

RE

Сетих се за това, което сте написали в следващия урок, но и то не работи на 100%. Когато отворя някакъв друг диалог (например палитрата с цветовете) и след това го затворя, на неговото място се губи графиката и не се преначертава наново.

Все пак събитието е

Все пак събитието е правилното.

Вероятно проблемът се получава, защото най-напред се обработва събитието (рисуват се фигурите), след което се прерисува панелът или прозорецът и нарисуваните фигури изчезват.

За да избегнем това можем да забавим изрисуването на фигурите, така че то да се извършва след прерисуването на панела. Това ще бъде добавено към следващата статия.

RE

Благодаря!

клас MyEllipse

Здравейте! Имам чувството, че тази секция в страницата отдавна не се посещава, но все пак ще задам въпрос.
Декларирах класа MyEllipse по следния начин: public class MyEllipse extends Ellipse implements Primitive {} (вместо extends Rectangle), при което IDE-то ми подсказа да се добави: import javafx.scene.shape.Ellipse. След това при добавяне на конструктор опцията отново беше Ellipse (а не Rectangle) и конструктора ми изглежда така:
public MyEllipse(Color color, double centerX, double centerY, double radiusX,
double radiusY) {
super(centerX, centerY, radiusX, radiusY);
this.color = color;
}
Всичко изглежда ок и IDE-то не дава съобщение за грешка. Прегледах сорс кода, който си предоставил, и видях че при теб MyEllipse наследява Rectangle. Интересно ми е каква е разликата и дали да го оставя така?

Имам един проблем който не знам как да реша

При jPanelCanvasMouseDragged когато напиша кода и стигна до if(jToggleButtonSelect.isSelected()){
MyGlobalVars.graphics2D.setStroke(new BasicStroke(1,BasicStroke.CAP_BUTT;
BasicStroke.JOIN_BEVEL;0 new float[]{6, 3},0))
Ми дава грешка компилатора която не знам как да разреша "No suitable constructor found for BasicStroke" някакви идеи защо се получава така ?

„полетата x, y, width,

полетата x, y, width, height. Те са наследени от класа java.awt.Rectangle.
На нас ни трябват тези полета от 
java.awt.Rectangle. Ползваме тях и при MyEllipse и при всички наши фигури, ако не се лъжа.
В javafx.scene.shape.Ellipse ги няма. Там им други от тип DoubleProperty.
Нищо не ти пречи и javafx.scene.shape.Ellipse да наследиш, но ще трябва да промениш целия код да работи с него.
Идеята е, че всяка от нашите фигури има тези 4 свойства на праводъгълника: координати x и y и ширина и височина. Всяка фигура се съдържа в този правоъгълник, и затова наследяваме Rectangle.

Oк. Благодаря за отговора!

Oк. Благодаря за отговора!

Попита ли чичо

lastHeiht

В класа GlovalVars, по-добре ще бъде lastHeight!!! Да се не обърка някой!

Ей, това го бях видял, но съм

Ей, това го бях видял, но съм го забравил.

В статия 6 ще бъде показано как най-лесно и без неприятни последствия може да се смени името на променлива.

Къде се създава класа

Къде се създава класа „Rectanlge“

Класът Rectangle...

Класът Rectangle е част от Java Standard Edition и не е нужно да го създаваме допълнително. Ползваме го наготово.

Ти за изпит ли се готвиш или си решил/а да си направиш проекта по-рано?

Класа MyRectangle

Когато декларирам класа MyRectangle се получава грешка-" Class myRectangle is public,should be declared in a file named MyRectangle.java". Не разбирам какъв е проблема и как да го отстраня.

Тази грешка копирана ли е?

Тази грешка копирана ли е или си я преписал/а? Защото myRectangle веднъж е изписано с малка буква, а втори път с голяма и запетаята между public,should трябва да бъде следвана от интервал.

Виж дали не си объркал/а името на класа, като внимаваш за малките и големи букви. После виж java файлът, в който декларираш класа как се казва. Името му трябва да съвпада с класа, който декларираш в него.

Пиши какво е станало.

MyRectangle.java

Декларирах класа в нов файл MyRectangle.java и всичко е наред. Благодаря!

Как го декларира, че и аз

Как го декларира, че и аз имам същия проблем. Смисъл какво писа в новия файл MyRecrangle.java =]

Погледни по-горе

Колега или колежке, защо не погледнеш изображенията или изходния код, който съм предоставил по-горе? Там класът е деклариран.

Уроците

Трябва май и един стартов урок по джава че май колегите не са чули на упражнения как се декларира Клас айде за интерфейса им е простено ама стига с тоя фейсбук бе колеги :D

Публикувай нов коментар

  • Адресите на уеб-страници и e-mail адресите автоматично се конвертират в хипервръзки.
  • Разрешени HTML tag-ове: <a> <p> <span> <div> <h1> <h2> <h3> <h4> <h5> <h6> <img> <map> <area> <hr> <br> <br /> <ul> <ol> <li> <dl> <dt> <dd> <table> <tr> <td> <em> <b> <u> <i> <strong><font> <del> <ins> <sub> <sup> <quote> <blockquote> <pre> <address> <code> <cite> <embed> <object> <param> <strike> <caption>
  • Линиите и параграфите се прекъсват автоматично.
  • Mark language-dependent sections with == lc == where lc (or lc-xx) is a language code, other or all.

Повече информация за опциите на форматиране

CAPTCHA
11 + 6 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.