Calculadora Científica Con Java FX

Hola , he estado practicando con Java cómo crear una calculadora científica; he probado Swing y FX. Me he decantado por FX no sólo porque se pueda pseudo-utilizar CSS, sino también porque: Scene-Builder es mucho más utilizable que el editor de NetBeans, y no te pone montón de código inútil. En general; FX es mucho mejor que Swing porque no es tan intrusivo en tu código; además de poder configurar tu UI mucho más fácil si te defiendes con CSS.

La calculadora opera tanto con operaciones elementales como en científicas. Me faltan 2 cosas a implementar:

Trabajar con fracciones->En esto ya tengo la clase Fracciones hecha con Java, debo añadir los botones con sus acciones y listo. No lo he terminado porque hace mucho calor.

Trabajar con paréntesis->Esto es ya más peliagudo, necesito un parseador, he encontrado mXparser; debo leer su documentación y practicar por consola antes de llevarlo a la aplicación.

Hacer la app usable en Android-> Debo averiguar cómo se establece el puente entre FX y Android, sospecho que me va a dar muchos dolores de cabeza.

Con todo eso que quiero averiguar / aprender, la calculadora es totalmente funcional y si tiene algún bug no creo que sea muy grave. Me gustaría saber cómo hacer para que su Look & Feel se parezca a una Casio, es decir; más realista. En fin, poco a poco.

Me ha dado muchos problemas para calcular algunos ángulos fundamentales con las funciones trigonométricas, ya que Java como la mayoría de lenguajes de programación dan resultados aproximados si usas double. Para el cálculo del factorial, combinaciones y variaciones he tenido que cambiar el tipo entero a Big Integer para que calculase algo decente.

La configuración de la interfaz es muy cómodo con SceneBuilder, he podido arrastrar/soltar un GridPane para ponerle dentro todos los botones, esto con Swing es una pesadilla (si lo quieres fácil debes pagar mucho). Como SceneBuilder te crea un fichero .fxml que sólo debes llamar desde tu código, hace que casi todo lo que se relaciona con la IU esté totalmente separada de la lógica de la app. FX te permite trabajar fácilmente con el modelo MVC (sobre todo con Netbeans):

1 Fichero fxml-> lo puedes crear con SceneBuilder

2 Fichero clase ppal. -> Contiene la clase ppal. con el método main y la incialización de la Stage y la Scene. Con llamadas para cargar los ficheros fxml y css.

3 Fichero Controlador -> Te crea el esqueleto Netbeans y tú lo vas rellenando con el código necesario para que el usuario pueda interactuar con la IU.

4 Fichero CSS -> El fichero para poder cambiar colores, tamaños etc de la app.

En fin, al principio cuesta un poco hacer las cosas con FX pero luego de unos días googleando y preguntando obtienes mejores resultados y más rápidos que con Swing.

Aquí dejo el código del fichero controlador:

  1. package fxcalculator;
  2.  
  3. import java.math.BigInteger;
  4. import java.net.URL;
  5. import java.util.ResourceBundle;
  6. import javafx.event.ActionEvent;
  7. import javafx.fxml.FXML;
  8. import javafx.fxml.Initializable;
  9. import javafx.scene.control.TextField;
  10.  
  11. /**
  12. *
  13. * @author lopez
  14. */
  15. public class fxmlCalculatorController implements Initializable {
  16. double number1 = 0.;
  17. double number2 = 0.;
  18. double solution;
  19. BigInteger soltn;
  20. long n1 = 0;
  21. long n2 = 0;
  22. String operation = "";
  23.  
  24. @FXML
  25. private TextField txtDisplay;
  26.  
  27. @FXML
  28. private void Btn7OnAction(ActionEvent event) {
  29. txtDisplay.setText(txtDisplay.getText() + "7");
  30. }
  31. @FXML
  32. private void Btn8OnAction(ActionEvent event) {
  33. txtDisplay.setText(txtDisplay.getText() + "8");
  34. }
  35. @FXML
  36. private void Btn9OnAction(ActionEvent event) {
  37. txtDisplay.setText(txtDisplay.getText() + "9");
  38. }
  39. @FXML
  40. private void Btn4OnAction(ActionEvent event) {
  41. txtDisplay.setText(txtDisplay.getText() + "4");
  42. }
  43. @FXML
  44. private void Btn5OnAction(ActionEvent event) {
  45. txtDisplay.setText(txtDisplay.getText() + "5");
  46. }
  47. @FXML
  48. private void Btn6OnAction(ActionEvent event) {
  49. txtDisplay.setText(txtDisplay.getText() + "6");
  50. }
  51. @FXML
  52. private void Btn3OnAction(ActionEvent event) {
  53. txtDisplay.setText(txtDisplay.getText() + "3");
  54. }
  55. @FXML
  56. private void Btn2OnAction(ActionEvent event) {
  57. txtDisplay.setText(txtDisplay.getText() + "2");
  58. }
  59. @FXML
  60. private void Btn1OnAction(ActionEvent event) {
  61. txtDisplay.setText(txtDisplay.getText() + "1");
  62. }
  63. @FXML
  64. private void Btn0OnAction(ActionEvent event) {
  65. txtDisplay.setText(txtDisplay.getText() + "0");
  66. }
  67. @FXML
  68. private void BtnDecimalPointOnAction(ActionEvent event) {
  69. if(!txtDisplay.getText().contains("."))
  70. txtDisplay.setText(txtDisplay.getText() + ".");
  71. }
  72. @FXML
  73. private void BtnPlusMinusOnAction(ActionEvent event) {
  74. String n = txtDisplay.getText();
  75. if(!n.contains("-"))
  76. txtDisplay.setText("-" + n);
  77. }
  78. @FXML
  79. private void BtnClearOnAction(ActionEvent event) {
  80. txtDisplay.setPromptText("0");
  81. txtDisplay.setText("");
  82. }
  83. @FXML
  84. private void BtnClearCharOnAction(ActionEvent event) {
  85. String n = txtDisplay.getText();
  86. String nn;
  87. nn = n.substring(0, n.length()-1);
  88. txtDisplay.setText(nn);
  89. }
  90. @FXML
  91. private void BtnPIOnAction(ActionEvent event) {
  92. txtDisplay.setText(String.valueOf(Math.PI));
  93. }
  94. @FXML
  95. private void BtnEOnAction(ActionEvent event) {
  96. txtDisplay.setText(String.valueOf(Math.E));
  97. }
  98. @FXML
  99. private void BtnSumOnAction(ActionEvent event) {
  100. operation = "suma";
  101. if(txtDisplay.getText().length() == 0){
  102. txtDisplay.setText("Please, insert digits");
  103. }
  104. else {
  105. number1 = Double.parseDouble(txtDisplay.getText());
  106. txtDisplay.setText("");
  107. txtDisplay.setPromptText("");
  108. }
  109. }
  110. @FXML
  111. private void BtnSubtractOnAction(ActionEvent event) {
  112. operation = "resta";
  113. if(txtDisplay.getText().length() == 0){
  114. txtDisplay.setText("Please, insert digits");
  115. }
  116. else {
  117. number1 = Double.parseDouble(txtDisplay.getText());
  118. txtDisplay.setText("");
  119. txtDisplay.setPromptText("");
  120. }
  121. }
  122. @FXML
  123. private void BtnProductOnAction(ActionEvent event) {
  124. operation = "producto";
  125. if(txtDisplay.getText().length() == 0){
  126. txtDisplay.setText("Please, insert digits");
  127. }
  128. else {
  129. number1 = Double.parseDouble(txtDisplay.getText());
  130. txtDisplay.setText("");
  131. txtDisplay.setPromptText("");
  132. }
  133. }
  134. @FXML
  135. private void BtnDivOnAction(ActionEvent event) {
  136. operation = "division";
  137. if(txtDisplay.getText().length() == 0){
  138. txtDisplay.setText("Please, insert digits");
  139. }
  140. else {
  141. number1 = Double.parseDouble(txtDisplay.getText());
  142. txtDisplay.setText("");
  143. txtDisplay.setPromptText("");
  144. }
  145. }
  146. @FXML
  147. private void BtnSquareRootOnAction(ActionEvent event) {
  148. String n = txtDisplay.getText();
  149. if(n.length() == 0){
  150. txtDisplay.setText("Please, insert digits");
  151. }
  152. else {
  153. double nn = Double.parseDouble(n);
  154. nn = Math.sqrt(nn);
  155. n = String.valueOf(nn);
  156. txtDisplay.setText(n);
  157. }
  158. }
  159. @FXML
  160. private void BtnCubicRootOnAction(ActionEvent event) {
  161. String n = txtDisplay.getText();
  162. if(n.length() == 0){
  163. txtDisplay.setText("Please, insert digits");
  164. }
  165. else {
  166. double nn = Double.parseDouble(n);
  167. nn = Math.cbrt(nn);
  168. n = String.valueOf(nn);
  169. txtDisplay.setText(n);
  170. }
  171. }
  172. @FXML
  173. private void BtnModulusOnAction(ActionEvent event) {
  174. operation = "modulo";
  175. if(txtDisplay.getText().length() == 0){
  176. txtDisplay.setText("Please, insert digits");
  177. }
  178. else {
  179. number1 = Double.parseDouble(txtDisplay.getText());
  180. txtDisplay.setText("");
  181. txtDisplay.setPromptText("");
  182. }
  183. }
  184. @FXML
  185. private void BtnPower2OnAction(ActionEvent event) {
  186. String n = txtDisplay.getText();
  187. if(n.length() == 0){
  188. txtDisplay.setText("Please, insert digits");
  189. }
  190. else {
  191. double nn = Double.parseDouble(n);
  192. nn = Math.pow(nn, 2);
  193. n = String.valueOf(nn);
  194. txtDisplay.setText(n);
  195. }
  196. }
  197. @FXML
  198. private void BtnPower3OnAction(ActionEvent event) {
  199. String n = txtDisplay.getText();
  200. if(n.length() == 0){
  201. txtDisplay.setText("Please, insert digits");
  202. }
  203. else {
  204. double nn = Double.parseDouble(n);
  205. nn = Math.pow(nn, 3);
  206. n = String.valueOf(nn);
  207. txtDisplay.setText(n);
  208. }
  209. }
  210. @FXML
  211. private void BtnPowXnOnAction(ActionEvent event) {
  212. operation = "potencia";
  213. if(txtDisplay.getText().length() == 0){
  214. txtDisplay.setText("Please, insert digits");
  215. }
  216. else {
  217. number1 = Double.parseDouble(txtDisplay.getText());
  218. txtDisplay.setText("");
  219. txtDisplay.setPromptText("");
  220. }
  221. }
  222. @FXML
  223. private void BtnExp10OnAction(ActionEvent event) {
  224. String n = txtDisplay.getText();
  225. if(n.length() == 0){
  226. txtDisplay.setText("Please, insert digits");
  227. }
  228. else {
  229. double nn = Double.parseDouble(n);
  230. nn = Math.pow(10, nn);
  231. n = String.valueOf(nn);
  232. txtDisplay.setText(n);
  233. }
  234. }
  235. @FXML
  236. private void BtnExpEOnAction(ActionEvent event) {
  237. String n = txtDisplay.getText();
  238. if(n.length() == 0){
  239. txtDisplay.setText("Please, insert digits");
  240. }
  241. else {
  242. double nn = Double.parseDouble(n);
  243. nn = Math.pow(Math.E, nn);
  244. n = String.valueOf(nn);
  245. txtDisplay.setText(n);
  246. }
  247. }
  248. @FXML
  249. private void BtnLogOnAction(ActionEvent event) {
  250. String n = txtDisplay.getText();
  251. if(n.length() == 0){
  252. txtDisplay.setText("Please, insert digits");
  253. }
  254. else {
  255. double nn = Double.parseDouble(n);
  256. nn = Math.log10(nn);
  257. n = String.valueOf(nn);
  258. txtDisplay.setText(n);
  259. }
  260. }
  261. @FXML
  262. private void BtnLnOnAction(ActionEvent event) {
  263. String n = txtDisplay.getText();
  264. if(n.length() == 0){
  265. txtDisplay.setText("Please, insert digits");
  266. }
  267. else {
  268. double nn = Double.parseDouble(n);
  269. nn = Math.log(nn);
  270. n = String.valueOf(nn);
  271. txtDisplay.setText(n);
  272. }
  273. }
  274. @FXML
  275. private void BtnInverseOnAction(ActionEvent event) {
  276. String n = txtDisplay.getText();
  277. if(n.length() == 0){
  278. txtDisplay.setText("Please, insert digits");
  279. }
  280. else {
  281. double nn = Double.parseDouble(n);
  282. nn = 1 / nn;
  283. n = String.valueOf(nn);
  284. txtDisplay.setText(n);
  285. }
  286. }
  287. @FXML
  288. private void BtnFactorialOnAction(ActionEvent event) {
  289. String n = txtDisplay.getText();
  290. if(n.length() == 0){
  291. txtDisplay.setText("Please, insert digits");
  292. }
  293. else {
  294. long nn = Long.parseLong(n);
  295. BigInteger m;
  296. m = factorial(nn);
  297. n = String.valueOf(m);
  298. txtDisplay.setText(n);
  299. }
  300. }
  301. @FXML
  302. private void BtnCombinationOnAction(ActionEvent event) {
  303. operation = "combination";
  304. if(txtDisplay.getText().length() == 0){
  305. txtDisplay.setText("Please, insert digits");
  306. }
  307. else {
  308. n1 = Integer.parseInt(txtDisplay.getText());
  309. txtDisplay.setText("");
  310. txtDisplay.setPromptText("");
  311. }
  312. }
  313. @FXML
  314. private void BtnVariationOnAction(ActionEvent event) {
  315. operation = "variation";
  316. if(txtDisplay.getText().length() == 0){
  317. txtDisplay.setText("Please, insert digits");
  318. }
  319. else {
  320. n1 = Integer.parseInt(txtDisplay.getText());
  321. txtDisplay.setText("");
  322. txtDisplay.setPromptText("");
  323. }
  324. }
  325. @FXML
  326. private void BtnDegMinSecOnAction(ActionEvent event) {
  327. String n = txtDisplay.getText();
  328. if(n.length() == 0){
  329. txtDisplay.setText("Please, insert digits");
  330. }
  331. else {
  332. double nn = Double.parseDouble(n);
  333. n = degreesConversor(nn);
  334. txtDisplay.setText(n);
  335. }
  336. }
  337. @FXML
  338. private void BtnSineOnAction(ActionEvent event) {
  339. String n = txtDisplay.getText();
  340. if(n.length() == 0){
  341. txtDisplay.setText("Please, insert digits");
  342. }
  343. else {
  344. double nn = Double.parseDouble(n);
  345. if(nn >= 360 || nn < 0) nn %= 360;
  346. if(nn % 180 == 0) nn = 0;
  347. else nn = Math.sin(Math.toRadians(nn));
  348. n = String.valueOf(nn);
  349. txtDisplay.setText(n);
  350. }
  351. }
  352. @FXML
  353. private void BtnCosineOnAction(ActionEvent event) {
  354. String n = txtDisplay.getText();
  355. if(n.length() == 0){
  356. txtDisplay.setText("Please, insert digits");
  357. }
  358. else {
  359. double nn = Double.parseDouble(n);
  360. if(nn >= 360 || nn < 0) nn %= 360;
  361. if(nn == 90 || nn == 270) nn = 0;
  362. else nn = Math.cos(Math.toRadians(nn));
  363. n = String.valueOf(nn);
  364. txtDisplay.setText(n);
  365. }
  366. }
  367. @FXML
  368. private void BtnTangentOnAction(ActionEvent event) {
  369. String n = txtDisplay.getText();
  370. if(n.length() == 0){
  371. txtDisplay.setText("Please, insert digits");
  372. }
  373. else {
  374. double nn = Double.parseDouble(n);
  375. if(nn >= 360 || nn < 0) nn %= 360;
  376.  
  377. if(nn == 0 || Math.abs(nn) == 180) nn = 0;
  378. else if(Math.abs(nn) == 45 || Math.abs(nn) == 225) nn = 1;
  379. else if(Math.abs(nn) == 135 || Math.abs(nn) == 315) nn = -1;
  380. else if(Math.abs(nn) == 90 || Math.abs(nn) == 270) nn = Double.NaN;
  381. else nn = Math.tan(Math.toRadians(nn));
  382. n = String.valueOf(nn);
  383. txtDisplay.setText(n);
  384. }
  385. }
  386. @FXML
  387. private void BtnASineOnAction(ActionEvent event) {
  388. String n = txtDisplay.getText();
  389. if(n.length() == 0){
  390. txtDisplay.setText("Please, insert digits");
  391. }
  392. else {
  393. double nn = Double.parseDouble(n);
  394. nn = Math.toDegrees(Math.asin(nn));
  395. n = String.valueOf(nn);
  396. txtDisplay.setText(n);
  397. }
  398. }
  399. @FXML
  400. private void BtnACosineOnAction(ActionEvent event) {
  401. String n = txtDisplay.getText();
  402. if(n.length() == 0){
  403. txtDisplay.setText("Please, insert digits");
  404. }
  405. else {
  406. double nn = Double.parseDouble(n);
  407. nn = Math.toDegrees(Math.acos(nn));
  408. n = String.valueOf(nn);
  409. txtDisplay.setText(n);
  410. }
  411. }
  412. @FXML
  413. private void BtnATangentOnAction(ActionEvent event) {
  414. String n = txtDisplay.getText();
  415. if(n.length() == 0){
  416. txtDisplay.setText("Please, insert digits");
  417. }
  418. else {
  419. double nn = Double.parseDouble(n);
  420. nn = Math.toDegrees(Math.atan(nn));
  421. n = String.valueOf(nn);
  422. txtDisplay.setText(n);
  423. }
  424. }
  425. @FXML
  426. private void BtnHypSineOnAction(ActionEvent event) {
  427. String n = txtDisplay.getText();
  428. if(n.length() == 0){
  429. txtDisplay.setText("Please, insert digits");
  430. }
  431. else {
  432. double nn = Double.parseDouble(n);
  433. nn = Math.sinh(nn);
  434. n = String.valueOf(nn);
  435. txtDisplay.setText(n);
  436. }
  437. }
  438. @FXML
  439. private void BtnHypCosineOnAction(ActionEvent event) {
  440. String n = txtDisplay.getText();
  441. if(n.length() == 0){
  442. txtDisplay.setText("Please, insert digits");
  443. }
  444. else {
  445. double nn = Double.parseDouble(n);
  446. nn = Math.cosh(nn);
  447. n = String.valueOf(nn);
  448. txtDisplay.setText(n);
  449. }
  450. }
  451. @FXML
  452. private void BtnHypTangentOnAction(ActionEvent event) {
  453. String n = txtDisplay.getText();
  454. if(n.length() == 0){
  455. txtDisplay.setText("Please, insert digits");
  456. }
  457. else {
  458. double nn = Double.parseDouble(n);
  459. nn = Math.tanh(nn);
  460. n = String.valueOf(nn);
  461. txtDisplay.setText(n);
  462. }
  463. }
  464. @FXML
  465. private void BtnEqualsOnAction(ActionEvent event) {
  466. if(txtDisplay.getText().length() == 0){
  467. txtDisplay.setText("Please, insert digits");
  468. }
  469. else {
  470. number2 = Double.parseDouble(txtDisplay.getText());
  471. switch (operation) {
  472. case "suma":
  473. solution = number1 + number2;
  474. txtDisplay.setText(String.valueOf(solution));
  475. break;
  476. case "resta":
  477. solution = number1 - number2;
  478. System.out.println(solution);
  479. txtDisplay.setText(String.valueOf(solution));
  480. break;
  481. case "producto":
  482. solution = number1 * number2;
  483. txtDisplay.setText(String.valueOf(solution));
  484. break;
  485. case "division":
  486. solution = number1 / number2;
  487. txtDisplay.setText(String.valueOf(solution));
  488. break;
  489. case "potencia":
  490. solution = Math.pow(number1, number2);
  491. txtDisplay.setText(String.valueOf(solution));
  492. break;
  493. case "modulo":
  494. solution = number1 % number2;
  495. txtDisplay.setText(String.valueOf(solution));
  496. break;
  497. case "combination":
  498. n2 = Long.parseLong(txtDisplay.getText());
  499. soltn = combinatorial(n1, n2);
  500. txtDisplay.setText(String.valueOf(soltn));
  501. break;
  502. case "variation":
  503. n2 = Long.parseLong(txtDisplay.getText());
  504. soltn = variations(n1, n2);
  505. txtDisplay.setText(String.valueOf(soltn));
  506. break;
  507. default:
  508. break;
  509. }
  510. }
  511. }
  512. @Override
  513. public void initialize(URL url, ResourceBundle rb) {
  514. // TODO
  515. }
  516.  
  517. public static BigInteger factorial(long n) {
  518. if (n == 0) return BigInteger.valueOf(1);
  519. BigInteger fact = BigInteger.valueOf(1);
  520. for (int i = 1; i <= n; i++) {
  521. fact = fact.multiply(BigInteger.valueOf(i));
  522. }
  523.  
  524. return fact;
  525. }
  526. public static BigInteger combinatorial(long m, long n) {
  527. return factorial(m).divide(factorial(n).multiply(factorial(m - n)));
  528. }
  529. public static BigInteger variations(long m, long n) {
  530. return factorial(m).divide(factorial(m - n));
  531. }
  532. public static String degreesConversor(double x){
  533. int integer_part = (int) Math.floor(x);
  534. double decimal_part = x - Math.floor(x);
  535. int deg, min, secs;
  536. String expression;
  537.  
  538. min = Math.abs((int) (decimal_part * 60.));
  539. secs = Math.abs((int) ((decimal_part * 60. - Math.floor(decimal_part * 60.)) * 60.));
  540. deg = integer_part;
  541.  
  542. if(secs >= 60) {secs -= 60; min += 1;}
  543. if(min >= 60) {min -= 60; deg += 1;}
  544. if(deg >= 360) deg %= 360;
  545. if(deg < 0) deg += 360;
  546.  
  547. expression = String.valueOf(deg) + "º " + String.valueOf(min) +
  548. "' " + String.valueOf(secs) + "''";
  549. return expression;
  550. }
  551. }
  552.  
  553.  

Todo el código se puede descargar en mi GitHub:

Calculator

Deja un comentario

Proudly powered by WordPress   Premium Style Theme by www.gopiplus.com
A %d blogueros les gusta esto: