* Графичен калкулатор с NetBeans
Публикувано на 17 ноември 2015 в раздел УКИ.
Тук ще разгледаме възможността бързо и лесно да създаваме графични приложения чрез средата за програмиране NetBeans. Идеята при използването на такива среди е много лесно и бързо да "подреждаме" графичните контроли и да пишем минимално количество код. Това е особено подходящо за училищни курсове по програмиране, където се цели бързо и лесно достигане до практически значими резултати с минимални усилия.
След като стартирате NetBeans, отидете на File > New Project. Изберете Java Application, след което продължете с Next:
На следващият екран се уверете, че НЕ е отбелязано "Create Main Class". Напишете име на проекта - например OutCalculator - и натиснете Finish:
С това създадохте празен проект. Неговата файлова структура се изобразява в лявата част на екрана, а в центъра е работното поле, в което ще се отварят файловете. Сега натиснете с десния бутон на мишката върху името на проекта и изберете New > JFrame Form:
Задайте име на класа и име на пакета, в който ще се намира, след което натиснете Finish:
На следващата картинка са показани 5-те основни области на екрана:
Те са както следва:
- Йерархична файлова структура на проекта;
- Списък с обекти за бърз достъп;
- Основен екран за дизайн на Swing приложението;
- Списък с графични контроли, които можем да преместваме в 3;
- Стандартен изход (системни съобщения от компилатора и System.out).
Ще забележите, че в 3 може да сменяте режима между Design и Source изглед. Засега ще се фокусираме само върху дизайнера. Започнете да изтегляте контроли от 4 в 3 и ги преоразмерявайте (чрез натискане в левия ъгъл и влачене). Първо ни трябват два JLabel в горния ляв и горния десен ъгъл:
Под тях поставете тестово поле (JTextField):
Накрая добавете поредица от бутони и ги преоразмерете така, че да получите следния екран (за удобство когато добавите един ред с бутони можете после да ги маркирате, копирате и вмъквате):
Сега е добре да променим текста на графичните контроли, за да може да отразява това, което търсим - калкулатор. Натискайте с десния бутон на мишката последователно върху всяка контрола, след което избирайте "Edit text":
В крайния си вариант екрана трябва да изглежда по следния начин (JLabel1 и JLabel2 не са изтрити, а просто текста им е сменен с обикновен интервал, с което са станали невидими на екрана):
Ще забележите, че при всяко добавяне на нов графичен контрол в поле 2 (списъка с обекти) се появява нов запис. Те приемат имена по подразбиране (JButton1, JButton2, и т.н.). Добре е след като сме направили основния екран да ги променим. Маркирайте обектите един по един и натискайте бутон F2 (rename), след което променяйте имената им с някои по-удобни за вас. Например:
Това ще са имената на обектите и именно с тези имена ще се обръщаме към тях. Сега ще направим още няколко промени по дизайна. Маркирайте текстовото поле (currentNumber) с десен бутон и изберете Properties. Това са основните свойства на графичния компонент - всеки компонент има свои, като различните компоненти могат да имат различни. Ние искаме текста да е малко по-голям и да е подравнен вдясно. Направете промените както е показано на следващата картинка:
Аналогично извикайте Properties на основния JFrame (изберете с десния бутон Properties) от списъка с обекти. Задайте му Title като OurCalc:
И по-надолу премахнете опцията Resizable (така прозореца на основната програма ще бъде забранено да се разпъва):
Аналогично на currentNumber (текстовото поле) променете Editable да бъде изключено:
Дизайнът на нашата програма е готов. Сега трябва да добавим функционалностите на всеки един от контролите ни. С десен бутон натиснете върху бутон 1 и изберете Events > Action > actionPerformed. Това ще създаде метод, който ще бъде извикван при натискане на бутона:
Ще видите, че автоматично ще бъдете прехвърлени в Source изгледа, при това автоматично при метода, който току що сте създали:
Сега трябва да направим основното действие на бутон 1. То естествено е да добави цифрата "1" в края на съществуващото число. Да, но ако съществуващото число е 0 (каквото е по подразбиране), би се получило "01", а ние искаме да има само 1. Затова при натискане на бутон бихме изкали първо да се премахнат водещите нули и след това да се добавя числото. Ще направим това чрез следния код, който трябва да добавите на мястото на коментара в новосъздадения метод:
currentNumber.setText(currentNumber.getText().replaceFirst("^0*[^.]", "")+"1");
Да го разгледаме внимателно. На currentNumber извикваме метод "setText" (промени текста). В скобите имаме извикване на текущия текст на същия обект (метод getText()), на резултата от който прилагаме метод "replaceFirst" (замени първите букви). Като първи параметър на replaceFirst сме подали регулярен израз (^ - започва с, 0 - цифрата 0, * - нула или повече пъти), а като втори параметър подаваме празен текст. Към така получения резултат (оригиналния текст с премахнати нули в началото) добавяме текст с цифрата 1. Този краен резултат е и входен параметър на метод SetText.
Нека видим какво сме свършили. От главното меню на NetBeans изберете Run > Rub Project:
NetBeans ще се "оплаче", че няма избран Main метод за проекта, т.е. входна точка на програмата. Ще ви предложи следния екран за избор:
Натиснете OK и вашия калкулатор ще се появи на екрана:
Ще видите, че бутон 1 работи както беше предвидено. Сега загасете програмата и пристъпете към работа с бутони 2, 3, 4, 5, 6, 7, 8, 9 и 0. Тяхното действие е същото като на бутон 1, но само ще трябва да смените цифрата в скобичките:
Ето и кода на направеното дотук в текстови вид:
private void btn1ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"1"); } private void btn2ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"2"); } private void btn3ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"3"); } private void btn4ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"4"); } private void btn5ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"5"); } private void btn6ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"6"); } private void btn7ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"7"); } private void btn8ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"8"); } private void btn9ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"9"); } private void btn0ActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText(currentNumber.getText().replaceFirst("^0*", "")+"0"); }
Бутон "." не е толкова тривиален. При него не се притесняваме от водещи 0-ли (можем да имаме число 0.123 например). Тази точка обаче може да присъства максимум веднъж в числото. Затова ще направим следното - първо ще потърсим дали в съществуващия текст има точка и ще конкатенираме нашата точка само ако такава няма. Кодът за това ще бъде следния:
private void btnDotActionPerformed(java.awt.event.ActionEvent evt) { if(!currentNumber.getText().contains(".")){ currentNumber.setText(currentNumber.getText()+"."); } }
Удивителната е отрицание. Тоест ако в оператор if текущото число няма "." само тогава ще бъде добавена точка в края му. Пуснете програмата и пробвайте. Първоначално изглежда, че всичко работи добре - ако натискате много пъти върху точката, няма да се появява нова:
Ще установите обаче, че има и неприятна грешка (бъг). Ако натиснете 0. и след това кое да е друго число, нулата ще изчезне. Това разбира се е така, защото метода на цифровите бутони изтрива водещите нули. Ще се получи нещо, което не желаем:
Начините за справяне с този и други подобни проблеми са най-разнообразни. Ние ще подходим с "груба сила". В методите на бутоните от 0 до 9 ще добавим следния ред код:
if(currentNumber.getText().startsWith(".")){ currentNumber.setText("0"+currentNumber.getText()); }
Със сигурност това не е най-добрия начин за справяне с проблема - първо махаме нулите, после добавяме нула. В случая този начин и бърз и лесен - би бил подходящ за демонстрация пред ученици, които не са се занимавали с програмиране до този момент. Ако изпробваме това, ще видим, че работи:
Бутонът "C" е много лесен. Неговата функция е да изтрие всичко и да го върне в първоначалния му вид:
private void btnCActionPerformed(java.awt.event.ActionEvent evt) { currentNumber.setText("0"); savedNumber.setText(" "); operation.setText(" "); }
Бутон +/- също е що годе тривиален. При него правим проверка - ако текущото число започва с "-", трябва да го премахнем. Ако няма "-", трябва да му добавим. За удобство в началото ще си запишем текущото число в локална променлива:
private void btnInvertActionPerformed(java.awt.event.ActionEvent evt) { String cn = currentNumber.getText(); if(cn.startsWith("-")){ cn = cn.substring(1, cn.length()); } еlse{ cn = "-"+cn; } currentNumber.setText(cn); }
Сега искаме да извършим действията с бутоните с операциите. При натискане на +,-, * и / искаме текущото число да се съхрани в левия label (savedNumber), а операцията в десния (operation). След това текущото число (currentNumber) трябва да се нулира. Създайте събитие за натискане на бутон + и добавете следния код
private void btnPlusActionPerformed(java.awt.event.ActionEvent evt) { // Ако вече има въведено число, само променяме операцията if(!savedNumber.getText().equals(" ")){ operation.setText("+"); return; } // Във временна променлива съхраняваме текущото число String sn = currentNumber.getText(); // Ако в числото има десетична точка if(sn.contains(".")){ // Премахваме всички нули в края му sn = sn.replaceAll("0*$", ""); // Ако след премахването на нулите завършва на точка if(sn.endsWith(".")){ // Премахваме точката sn = sn.substring(0,sn.length()-1); } } savedNumber.setText(sn); operation.setText("+"); currentNumber.setText("0"); }
Виждате, че извършихме в началото няколко допълнителни операции. Ако числото има десетична точка и завършва на една или повече нули, премахваме тези нули (това е така, защото числото 123.45600000 всъщност ще си е 123.456). След като сме премахнали нулите, проверяваме дали случайно числото не завършва на точка (например 123.00000 да се е превърнало в 123.). Ако това е така, премахваме точката.
Аналогично трябва да направим същите операции за бутоните изваждане, умножение и деление - при тях единствено се различава знака:
private void btnMinusActionPerformed(java.awt.event.ActionEvent evt) { if(!savedNumber.getText().equals(" ")){ operation.setText("-"); return; } String sn = currentNumber.getText(); if(sn.contains(".")){ sn = sn.replaceAll("0*$", ""); if(sn.endsWith(".")){ sn = sn.substring(0,sn.length()-1); } } savedNumber.setText(sn); operation.setText("-"); currentNumber.setText("0"); } private void btnMultiplyActionPerformed(java.awt.event.ActionEvent evt) { if(!savedNumber.getText().equals(" ")){ operation.setText("*"); return; } String sn = currentNumber.getText(); if(sn.contains(".")){ sn = sn.replaceAll("0*$", ""); if(sn.endsWith(".")){ sn = sn.substring(0,sn.length()-1); } } savedNumber.setText(sn); operation.setText("*"); currentNumber.setText("0"); } private void btnDivideActionPerformed(java.awt.event.ActionEvent evt) { if(!savedNumber.getText().equals(" ")){ operation.setText("/"); return; } String sn = currentNumber.getText(); if(sn.contains(".")){ sn = sn.replaceAll("0*$", ""); if(sn.endsWith(".")){ sn = sn.substring(0,sn.length()-1); } } savedNumber.setText(sn); operation.setText("/"); currentNumber.setText("0"); }
Остава последния и най-труден бутон - извършване на операцията (=). При него логиката ще е сложна.
- Първо трябва да проверим дали има записано число. Ако няма, не трябва да правим нищо - Край. Ако има, продължаваме в 2;
- Превръщаме съхраненото число и текущото число от текст в числов тип double. Преминаваме на 3;
- Проверяваме каква е операцията и я извършваме. Продължаваме на 4;
- Записваме получения резултат в текущото число, а старото съхранено число и операцията ги премахваме. Край.
Освен това ще се погрижим ако крайния резултат е точен, т.е. число завършващо на .0, да премахнем окончанието .0.
private void btnCalculateActionPerformed(java.awt.event.ActionEvent evt) { if(savedNumber.getText().equals(" ")){ return; } double sn = Double.parseDouble(savedNumber.getText()); double cn = Double.parseDouble(currentNumber.getText()); double result = 0.0; switch(operation.getText()){ case "+": result = sn+cn; break; case "-": result = sn-cn; break; case "*": result = sn*cn; break; case "/": result = sn/cn; break; } savedNumber.setText(" "); operation.setText(" "); currentNumber.setText(""+result); if(currentNumber.getText().endsWith(".0")){ String tmp = currentNumber.getText(); currentNumber.setText(tmp.substring(0, tmp.length()-2)); } }
Дотук направения калкулатор е почти готов. Има още няколко неща, за довършване, едното от които обаче е много съществено - какво правим при деление на нула? Проверете, ще се получи следното:
В Java e прието число разделено на нула да се приема за безкрайност. При това текстът Infinity превърнат в число е валидна конструкция. По същият начин ако разделите 0/0 ще получите резултат NaN (not a number - не е число). Всяка операция с NaN ще дава NaN. Тоест бихме могли да оставим нещата и по този начин.
По лошото в случая е, че в нашата програма можем да продължим да работим и да добавяме разни числа в края на този текст:
Това вече категорично не е валидно число и ако се опитате да извършите операция с него, програмата ще се счупи. Опитайте.
За да се справим с този проблем трябва да забраним на цифрите да се добавят ако currentNumber е със стойност Infinity или NaN. Добавете следния код в началото на метода на всеки един от бутоните от 0 до 9:
if( currentNumber.getText().endsWith("Infinity") || currentNumber.getText().endsWith("NaN")){ return; }
Защо endsWith, а не equals? Причината е, че можем спокойно с бутон +/- да правим -Infinity или -NaN. Така си спестяваме още две проверки.
С това нашият калкулатор е готов и е относително стабилен. Вече можем да го разпространим. Натиснете Clean and Build бутона:
След това отидете с Windows Explorer в директорията, в която сте записали проекта. В моя случай това е C:\Users\Philip\Documents\NetBeansProjects\OurCalculator. В поддиректория "dist" ще видите файл OurCalculator.jar. Именно това е крайния вариант на вашата програма. Може да го копирате и разпространявате. Ако го отворите на компютър с инсталиран JRE, програмата ще се стартира.
Допълнителна задача 1: Създаденият има проблем с прекалено дългите числа - ако започнете да въвеждате число с 20 или повечи цифри, то ще излезе от екрана, а при операция етикетите на savedNumber и operation ще се припокрият.
Допълнителна задача 2: Отново при смятане с прекалено дълги числа, те ще се преобразуват като експоненти - например може да започнат да се появяват числа като 4.99E14 и други подобни. Програмата ни няма да се чупи, но ще е възможно да продължим да пишем с цифри в края на тези числа. Опитайте се да направите така, че да не може.
Допълнителна задача 3: Потърсете други пропуски в създадената програма и предложете решения.
Добави коментар