Mostrar Mensajes

Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.


Mensajes - Kabuto

Páginas: 1 ... 20 21 22 23 24 [25] 26 27 28 29 30 ... 50
481
En este tipo de tareas no puedo ayudar, desconozco el tema de sockets y conexiones.  :(

A ver si pasa alguien que sí pueda aportar algo.
Suerte y un saludo.

482
Necesitas dos arrays con nombres distintos.
Lo mismo con las variables hh y mm, cambiar nombres para tener las horas y minutos de inicio separadas de las de fin
El método, ya calculando y comparando los tiempos, podría quedar así:
Código: [Seleccionar]
      public static boolean comprobarTiempos(String inicio, String fin) {
        //Inicio
        String[] partesInicio = inicio.split(":");
        int hhIni = Integer.parseInt(partesInicio[0]);
        int mmIni = Integer.parseInt(partesInicio[1]);
        int segundosInicio = hhIni * 3600; //Horas a segundos
        segundosInicio = segundosInicio + (mmIni * 60); //Sumamos los minutos convertidos en segundos
        //Fin
        String[] partesFin = fin.split(":");
        int hhFin = Integer.parseInt(partesFin[0]);
        int mmFin = Integer.parseInt(partesFin[1]);
        int segundosFin = hhFin * 3600;
        segundosFin = segundosFin + (mmFin * 60);
        //Comparamos
        if (segundosInicio < segundosFin)
            return true;
        else
            return false;
    }

Y el programa ya completo, quedaría así:
Código: [Seleccionar]
import java.util.Scanner;

public class Main
{
    public static void main(String[] args) {
        Scanner scn = new Scanner(System.in);
        String cadena ="";
 
        while (!cadena.equals("salir")){
            if (!cadena.equals("salir")) {
                System.out.println("\n\t Ingresa la cadena de texto con el formato indicado");
                System.out.println("\t Dia de la semana,hora de inicio, hora final.");
                System.out.println("\t Escriba \"salir\" para terminar programa");
                System.out.print("\n\t Cadena: ");
                cadena = scn.nextLine();
                String[] partes = cadena.split(",");
           
                if (partes.length == 3){
                    if (comprobarSemana(partes[0])) {//if 1 comprobar el dia de la semana
                        if (comprobarHoras(partes[1]) && comprobarHoras(partes[2])) {
                            //Ahora hay que comprobar que la hora de partes[1] es anterior a la de partes[2]
                            if (comprobarTiempos(partes[1], partes[2]))
                                System.out.println("La cadena es valida");
                            else
                                System.out.println("La hora de inicio ha de ser anterior a la de fin");
                        }
                        else
                            System.out.println("Formato de horas incorrecto. El fomato ha de ser hh:mm");
             
                    }// cierre if 1
                    else
                        System.out.println("\n \t"+partes[0] + " no es un dia de la semana.");
                }
            }
            else //No hemos obtenido 3 partes
                System.out.println("La cadena ingresada es incorrecta favor de verificar el formato \n");
        }
       
        System.out.println("Fin de programa");
    }
   
      //Declaracion metodo comprobarSemana
        public static boolean comprobarSemana(String dia) {
            boolean comprobacionSemana = false;
            if(dia.equals("lunes") || dia.equals("martes") || dia.equals("miercoles") || dia.equals("jueves") || dia.equals("viernes")
                || dia.equals("sabado") || dia.equals("domingo"))
                return true;
            else
            return false;
    }
   
     public static boolean comprobarHoras(String hora) {
   
        String[] partes = hora.split(":");
        if (partes.length != 2)
            return false; //No hemos obtenido un array de dos elementos
        else {
            //Sí tenemos dos elementos, pero ambos elementos han de tener longitud de 2 caracteres
            if (partes[0].length() != 2 || partes[1].length() != 2)
                return false;//Uno, o ambos elementos, no tienen dos caracteres
            else {
                //Ambos elementos tienen dos caracteres.
                //Los parseamos a enteros para comprobar si son horas y minutos válidos
                //Usamos try catch por si los caracteres no son números parseables
                try {
                    int hh = Integer.parseInt(partes[0]);
                    int mm = Integer.parseInt(partes[1]);
                    //Comprobamos horas
                    if (hh < 0 || hh >= 24)
                        return false; //Horas incorrectas
                    if (mm < 0 || mm >= 60)
                        return false; //Minutos incorrectos
                }
                catch(NumberFormatException ex) {
                    return false;//Usuario no ha tecleado números
                }
            }
        }
       
        return true; //Todas las comprobaciones han sido correctas
    }
   
      public static boolean comprobarTiempos(String inicio, String fin) {
        //Inicio
        String[] partesInicio = inicio.split(":");
        int hhIni = Integer.parseInt(partesInicio[0]);
        int mmIni = Integer.parseInt(partesInicio[1]);
        int segundosInicio = hhIni * 3600; //Horas a segundos
        segundosInicio = segundosInicio + (mmIni * 60); //Sumamos los minutos convertidos en segundos
        //Fin
        String[] partesFin = fin.split(":");
        int hhFin = Integer.parseInt(partesFin[0]);
        int mmFin = Integer.parseInt(partesFin[1]);
        int segundosFin = hhFin * 3600;
        segundosFin = segundosFin + (mmFin * 60);
        //Comparamos
        if (segundosInicio < segundosFin)
            return true;
        else
            return false;
    }
}


Y creo que con esto se termina.  :)
Si sigues teniendo dudas o preguntas, no te las guardes y publícalas.

Y espero que cuando otros visitantes lean este tema, vean la importancia de intentar uno mismo hacer el código y fallar, y atascarse, y equivocarse con la sintaxis, etc...

Yo podría haberte dado el programa terminado. De hecho, lo escribí (con una solución algo distinta a la que hemos hecho aquí) antes de tu primera respuesta.
Podría habértelo dado y explicado que hago aquí, y que hago allá.

Y tu habrías entendido las explicaciones, sí...pero habrías aprendido menos.
No es lo mismo que si lo intentas tú y te vas encontrando con todas las dudas que has ido teniendo:
- no se puede declarar un método dentro de otro.
- dos elementos (variable, array, objeto, lo que sea...) aunque se llamen igual dentro del programa, son cosas distintas si están declarados en métodos distintos. El array partes del método main principal es distinto del array partes del método comprobarHoras()
- en los métodos que retornan un valor, ya sea boolean, int, String... lo que sea..., hay que tener cuidado con las instrucciones condicionales (if, switch..) porque el código que escribamos ha de garantizar que se va a retornar dicho valor.

Y más cosas...

Insisto mucho en esto porque en muchos foros veo gente pidiendo ayuda con los ejercicios pero solo buscan que les den la solución terminada.
Y hacerles el trabajo, pues no estamos para eso la verdad..., pero sobre todo porque entonces no se les ayuda realmente.

A mi me resultaría más rápido y cómodo escribir el código y "regalarlo", además tengo un tiempo algo limitado para dedicarlo a esto.

El código de este ejercicio ya lo escribí hace un par de días, podría habértelo dado y olvidarme para dedicarme a otra cosa.
Pero prefiero ayudar a que lo intentes, aunque nos llevase varios días, e ir resolviendo dudas conforme van surgiendo.

Espero que haya sido útil. Un saludo.

483
Esta línea que señalo en rojo debería ir en el método de comprobarHoras

Citar
if (partes.length == 3){
     if (comprobarSemana(partes[0])) {//if 1 comprobar el dia de la semana
       if (partes.length != 2){ //Formato mal if 2 comprobar el formato de la hora 00:00
                 System.out.println("\n \t"+partes[0] + " no esta escrito en el formato establecido 00:00.");

Porque ahí, ese "partes" es el array que tiene las tres partes del horario: dia,inicio,fin

Lo de validar longitud igual 2, es para el array "partes" que está en el método comprobar horas.
Aunque los arrays se llamen igual, no tienen ninguna relación entre ellos, porque están en métodos/ámbitos distintos.
Quizás habría sido mejor idea no llamarlos igual para evitar estas confusiones, pero bueno.


Otra cosa que va a fallar en el método, son los if que mostraré a continuación, seguramente el compilador se quejará de que falta un return.
Cuando hacemos return mediante if, tenemos que garantizar dos return, para cuando se cumple la condición que evaluamos y para cuando no se cumple.

El primer if, el que señalo en verde, tiene ambos return garantizados. Pero el segundo if, el que marco en rojo, solo tiene return para cuando SÍ se cumple la condición, pero NO para cuando no la tiene, y esto el compilador no lo puede aceptar.

Citar
if (hh >= 0 && hh <= 23){ //Las horas son correctas
            if (mm >= 0 && mm <= 59) //Los minutos son correctos
                return true;

        } else
           return false ;

En este método hay muchas comprobaciones y es fácil que nos equivoquemos y no proporcionemos un return para cada posibilidad.
Para facilitarlo, podemos cambiar la estrategia.

En los if solo preguntaremos si "está mal", en cuyo caso haremos un return false;
Luego al final del método, la última línea, será un return true; sin ninguna condición.

De esta manera, si se encuentra "algo mal", algún return false habrá puesto fin al método.
Si no ha ocurrido ningún return false, es porque TODO está bien, por lo tanto podemos hacer tranquilamente un retun true.

Así no nos liamos y el compilador quedará contento, pues como hay un return al final que no depende de ninguna condición, queda garantizado que el método podrá finalizar en cualquier caso.

Podría quedar del siguiente modo.
Fíjate que añado algo que no comenté antes, un try  catch para capturar posibles excepciones al parsear horas y minutos.
Esto es importante, porque imagina que el usuario introduce esto: h5:9m
Son dos elementos, correcto.
Cada elemento tiene dos caracteres, correcto.
Pero al intentar parsear eso a números, no va a ser posible porque hay letras. Y se producirá una excepción y el programa se interrumpirá lanzando mensaje de error, a no ser que controlemos esa posible excepción con try catch

Código: [Seleccionar]
     public static boolean comprobarHoras(String hora) {
   
        String[] partes = hora.split(":");
        if (partes.length != 2)
            return false; //No hemos obtenido un array de dos elementos
        else {
            //Sí tenemos dos elementos, pero ambos elementos han de tener longitud de 2 caracteres
            if (partes[0].length() != 2 || partes[1].length() != 2)
                return false;//Uno, o ambos elementos, no tienen dos caracteres
            else {
                //Ambos elementos tienen dos caracteres.
                //Los parseamos a enteros para comprobar si son horas y minutos válidos
                //Usamos try catch por si los caracteres no son números parseables
                try {
                    int hh = Integer.parseInt(partes[0]);
                    int mm = Integer.parseInt(partes[1]);
                    //Comprobamos horas
                    if (hh < 0 || hh >= 24)
                        return false; //Horas incorrectas
                    if (mm < 0 || mm >= 60)
                        return false; //Minutos incorrectos
                }
                catch(NumberFormatException ex) {
                    return false;//Usuario no ha tecleado números
                }
            }
        }
       
        return true; //Todas las comprobaciones han sido correctas
    }


Con este método ya podríamos comprobar ambas horas.
Por cierto, lo de repetir "mientras flag == 3", no lo veo claro, porque solo repetiría si damos tres elementos.
Si damos más o menos, ha de indicar el error, pero estaría bien que siguiera pidiendo cadenas.

Quizás podríamos hacer que se repita hasta que el usuario introduzca la cadena "salir"

Código: [Seleccionar]
public class Main
{
    public static void main(String[] args) {
        Scanner scn = new Scanner(System.in);
        String cadena ="";
 
        while (!cadena.equals("salir")){
            if (!cadena.equals("salir")) {
                System.out.println("\n\t Ingresa la cadena de texto con el formato indicado");
                System.out.println("\t Dia de la semana,hora de inicio, hora final.");
                System.out.println("\t Escriba \"salir\" para terminar programa");
                System.out.print("\n\t Cadena: ");
                cadena = scn.nextLine();
                String[] partes = cadena.split(",");
           
                if (partes.length == 3){
                    if (comprobarSemana(partes[0])) {//if 1 comprobar el dia de la semana
                        if (comprobarHoras(partes[1]) && comprobarHoras(partes[2])) {
                            //Ahora hay que comprobar que la hora de partes[1] es anterior a la de partes[2]
                        }
                        else
                            System.out.println("Formato de horas incorrecto. El fomato ha de ser hh:mm");
             
                    }// cierre if 1
                    else
                        System.out.println("\n \t"+partes[0] + " no es un dia de la semana.");
                }
            }
            else //No hemos obtenido 3 partes
                System.out.println("La cadena ingresada es incorrecta favor de verificar el formato \n");
        }
       
        System.out.println("Fin de programa");
    }
   
      //Declaracion metodo comprobarSemana
        public static boolean comprobarSemana(String dia) {
            boolean comprobacionSemana = false;
            if(dia.equals("lunes") || dia.equals("martes") || dia.equals("miercoles") || dia.equals("jueves") || dia.equals("viernes")
                || dia.equals("sabado") || dia.equals("domingo"))
                return true;
            else
            return false;
    }
   
     public static boolean comprobarHoras(String hora) {
   
        String[] partes = hora.split(":");
        if (partes.length != 2)
            return false; //No hemos obtenido un array de dos elementos
        else {
            //Sí tenemos dos elementos, pero ambos elementos han de tener longitud de 2 caracteres
            if (partes[0].length() != 2 || partes[1].length() != 2)
                return false;//Uno, o ambos elementos, no tienen dos caracteres
            else {
                //Ambos elementos tienen dos caracteres.
                //Los parseamos a enteros para comprobar si son horas y minutos válidos
                //Usamos try catch por si los caracteres no son números parseables
                try {
                    int hh = Integer.parseInt(partes[0]);
                    int mm = Integer.parseInt(partes[1]);
                    //Comprobamos horas
                    if (hh < 0 || hh >= 24)
                        return false; //Horas incorrectas
                    if (mm < 0 || mm >= 60)
                        return false; //Minutos incorrectos
                }
                catch(NumberFormatException ex) {
                    return false;//Usuario no ha tecleado números
                }
            }
        }
       
        return true; //Todas las comprobaciones han sido correctas
    }
}


Este código se puede probar online en este enlace.

Pero no está completo, falta una última comprobación.
La hora de inicio que tenemos en partes[1] ha de ser anterior a la hora de fin que tenemos en partes[2]

Es decir, esta cadena es correcta: martes,15:30,17:00

Pero esta, no lo es: martes,17:00,15:30

Para comprobar esto, necesitamos nuevo método y podemos hacer que reciba los dos String donde tenemos las horas:

Código: [Seleccionar]
public static boolean comprobarTiempos(String inicio, String fin) {


}

Inicio y fin son horas con formato hh:mm
Y como ya dije anteriormente, una forma fácil de comprobar si una es anterior a la otra es transformar esos tiempos en segundos.
Así que de nuevo habría que hacer split(":") para separar las hh de las mm, tanto de inicio, como de fin
De nuevo hay que parsear a enteros, esta vez podemos ahorrarnos el try catch porque llegados a este punto de programa, ya hemos comprobado que las horas son caracteres numéricos.

Primero convertimos las hh de inicio a segundos, multiplicando por 3600.
Luego los mm, multiplicando por 60 y se los sumamos a los segundos que nos han dado las hh.

Ya tenemos los segundos de inicio. Ahora habría que hacer lo mismo para conseguir los segundos de fin.

Cuando tengamos ambas cantidades de segundos, las comparamos. Si los segundos de inicio son menores que los de fin, todo es correcto, devolvemos TRUE y ya por fin podemos decirle al usuario que la cadena introducida es VÁLIDA.

Si los segundos son iguales, o los de inicio son mayores que los de fin, entones retornamos FALSE y la cadena no es correcta, porque el tiempo de inicio no puede ser posterior al de fin.

Venga, último esfuerzo, ya casi lo tenemos  ;)

484
Asi fue como lo modifique solo que me da errores con el inicio del metodo boolean no se si sea por donde esta


Claro,no puedes declarar un método, dentro de otro método.

El código principal lo estás escribiendo dentro del método main, eso es correcto. Pero dentro de este método, no podemos declarar y escribir otro.
Hay que escribirlo fuera.
Dentro del main, simplemente lo invocaremos para que haga su tarea, pero su código ha de estar declarado fuera.

Código: [Seleccionar]
//Declaracion metodo main
  public static void main(String[] args) {
  Scanner scn = new Scanner(System.in);
  String cadena;
  int flag=3;
 
  do{
  System.out.println("\t Ingresa la cadena de texto con el formato indicado");
  System.out.println("\t Dia de la semana,hora de inicio, hora final.");
  cadena = scn.nextLine();
  String[] partes = cadena.split(",");


 if (partes.length == 3){
     if (comprobarSemana(partes[0])) {
         //Dia de la semana comprobado, ahora hay que comprobar las dos horas de inicio y fin
     }
     else
        System.out.println(partes[0] + " no es un dia de la semana.");
  }else
      System.out.println("La cadena ingresada es incorrecta favor de verificar el formato \n");
 
                                                     
  }while(flag == 3);
                             
  }
  //Fin del metodo main

  //Declaracion metodo comprobarSemana
   public boolean comprobarSemana() {
        boolean comprobacionSemana = false;
        if(partes[0]==("lunes" || "martes" || "miercoles" || "jueves" || "viernes" || "sabado" || "domingo"))
             {
             flag=1;
             ComprobarSemana= true;
        }else{
          ComprobarSemana= false;
        }
            return ComprobarSemana;
    }

Bien, ya está declarado fuera. Pero el código de ese método no funcionará
Este método, ha de recibir entre paréntesis, la cadena String que ha de comprobar ya que no va a saber qué es partes[0]

El array partes está declarado dentro del método main, y solo va a existir dentro de este método.
El resto de métodos no pueden saber que es partes, porque están en ámbitos distintos.

Por eso, lo que se hace es definir este método de manera que reciba el String que ha de comprobar entre los paréntesis.
Además el método ha de ser declarado como static, porque lo vamos a llamar desde el main, que también es estático.
No te preocupes ahora en saber que diferencia hay entre métodos static y "no estáticos", ya llegarás a eso en otra ocasión.

Otra cosa que no funcionará es esta comparación:
Código: [Seleccionar]
if(partes[0]==("lunes" || "martes" || "miercoles" || "jueves" || "viernes" || "sabado" || "domingo"))
Ojalá funcionase, porque resultaría muy cómodo poder escribirlo así, pero la sintaxis de Java no funciona de ese modo. No va a servir separar los String con el operador OR ( || )
Además cuando se comparan objetos (un String es un objeto) no es recomendable usar el ==
No va a funcionar como esperamos, ese operador es para tipos primitivos: int, double, boolean, byte,...

Para objetos de clase, hay que usar el método equals().
Así que, un posible método con estas correcciones podría quedar así:
Código: [Seleccionar]
  //Declaracion metodo comprobarSemana
   public static boolean comprobarSemana(String dia) {
        if(dia.equals("lunes") || dia.equals("martes") || dia.equals("miercoles") || dia.equals("jueves") || dia.equals("viernes")
        || dia.equals("sabado") || dia.equals("domingo"))
             return true;
        else
          return false;
    }
Otra alternativa, quizás un poco menos engorrosa de escribir, es utilizar un switch.
El resultado a la hora de computar es el mismo, pero es algo más cómodo de escribir:
Código: [Seleccionar]
  //Declaracion metodo comprobarSemana
   public static boolean comprobarSemana(String dia) {
        switch(dia) {
            case "lunes":
            case "martes":
            case "miercoles":
            case "jueves":
            case "viernes":
            case "sabado":
            case "domingo":
                return true;
            default:
                return false;
        }
    }

Ambas formas son buenas.
Luego ya en el main, llamamos a este método, le pasamos lo que tenemos en partes[0] y  a ver que dice.
Si resulta true, pues habrá que seguir con las siguientes comprobaciones.
Si no, (false) pues informamos que no nos ha dado un día de la semana correcto.

Código: [Seleccionar]
if (partes.length == 3){
     if (comprobarSemana(partes[0])) {
         //Dia de la semana comprobado, ahora hay que comprobar las dos horas de inicio y fin
     }
     else
        System.out.println(partes[0] + " no es un dia de la semana.");
  }else
      System.out.println("La cadena ingresada es incorrecta favor de verificar el formato \n");

La siguiente comprobación es revisar el formato de la hora de inicio y la hora de fin.

Para esto puedes declarar otro método, recuerda, ha de ser static y estar fuera del main.
Entre paréntesis indicas que va a recibir un String.
Este String ha de tener el formato --> 00:00

Es decir dos números de dos cifras, separados por dos puntos.
No sería aceptable por ejemplo: 001:45, ni tampoco 1:45, ni 01,45,...

Así que de nuevo podríamos cortar ese String con split(":").
Código: [Seleccionar]
String[] partes = hora.split(":");Esto nos ha de retornar un array de dos elementos.
Si este array no tiene dos elementos, formato mal...
Código: [Seleccionar]
if (partes.length != 2) //Formato malY si estos elemenos, no son Strings de dos caracteres , formato mal...
Código: [Seleccionar]
if (partes[0].length() != 2 || partes[1].length() != 2) //Formato mal
Y una vez comprobado todo esto, además, tenemos que averiguar si nos han dado unas horas acordes con el sistema horario real.
Es decir, la hora 34:78, pues es incorrecta....
Para esto, hay que parsear a números enteros las dos partes que hemos obtenido y ver si están en el rango adecuado.

Código: [Seleccionar]
int hh = Integer.parseInt(partes[0]);
int mm = Integer.parseInt(partes[1]);

Código: [Seleccionar]
if (hh >= 0 && hh <= 23) //Las horas son correctas
Código: [Seleccionar]
if (mm >= 0 && mm <= 59) //Los minutos son correctos
Como ves este método va a tener que hacer muchas comprobaciones y solo podrá retornar true si se cumplen todas ellas.
Intenta escribirlo tú. Insisto, no desesperes ni te frustres si no te sale a la primera, pues que no te salga a la primera es lo normal.

Pero esos errores, confusiones, atascarte al querer trasladar la lógica de tu cerebro a la lógica del programa,...eso es lo que hace que aprendas.

Un saludo.





485
Primero, no es necesario que importes las librerías sql. Estas se utilizan para hacer consultas a bases de datos, y no es el caso.

Segundo. El método split() te devuelve un array resultante de "cortar" la cadena allá donde le hayas dicho.
Supongamos que la cadena tecleada es esta cadena compuesta de tres partes separadas por dos comas.
lunes,00:45,02.30

Si le hacemos split(",") a esta cadena, obtendremos un array de 3 elementos como este:
[lunes]  [00:45]  [02:30]

Si la cadena tecleada, solo tuviera 2 partes, el array sería de dos elementos.
Si tuviera 4, pues cuatro elementos.. etc..

Por eso, tras hacer split(), lo primero que vamos a querer pregunta es si el tamaño del array obtenido, es igual a 3. Porque la cadena correcta ha de tener 3 partes, si tiene más o menos, no será valida.
Para saber lo que mide el array, hay que comprobar su atributo length


Código: [Seleccionar]
  if (partes.length == datos){
      //Por ahora tenemos 3 partes, hay que seguir haciendo más comprobaciones.
  }else{
      System.out.println("La cadena ingresada es incorrecta favor de verificar el formato");
  }



Tercero, la variable datos a la que das un valor de 3. Puedes usarla si quieres, pero no la necesitas. Para comprobar el tamaño del array, puedes comparar directamente con el valor 3

Código: [Seleccionar]
  if (partes.length == 3){
      //Por ahora tenemos 3 partes, hay que seguir haciendo más comprobaciones.
  }else{
      System.out.println("La cadena ingresada es incorrecta favor de verificar el formato");
  }

Bien, la siguiente comprobación sería ver si la primera parte de esa cadena, es un día de la semana: lunes, martes, etc...
Esa primera parte la tienes en el primer elemento del array partes, es decir, en partes[0].

Lo más cómodo es pasarle ese primer elemento a un método de tipo boolean que se encargue de comprobar si es el nombre de un día o no.
Ese es tu siguiente paso.

486
 :o Uooo, nunca había oído hablar de los números "felices".

Creo que el principal fallo de tu lógica está en la descomposición del número para sumar el cuadrado de sus cifras.
En cada iteración, solo descompones una vez, es decir, solo extraes una cifra.
Pero tendrías que descomponer tantas veces como cifras tenga el número que se está evaluando.

Para esto se requiere un segundo bucle while anidado dentro del while principal.
En este bucle vamos descomponiendo la variable respaldo hasta que obtenga valor 0, es decir, ya no quedan cifras.
En cada "descomposición", la cifra obtenida hacemos el cuadrado y vamos acumulando las sumas con otros cuadrados en otra variable, que podemos llamara sumaCuadrados

Tras terminar de descomponer, comprobamos si sumaCuadrados es igual a 1, en cuyo caso el número evaluado es feliz.
Si no es 1, repetimos proceso, pasando el valor de sumaCuadrados a respaldo para que en la siguiente iteración, volvamos de nuevo a descomponerlo hasta que valga 0.

Todo esto se repite un máximo de 8 veces y mientras no se certifique que el número es feliz.

He escrito el siguiente código siguiendo estos pasos, y parece funcionar. He buscado una lista de números felices y los que he comprobado son reconocidos como tal.

Espero que te sirva. Cualquier duda pregunta lo que sea.
Un saludo.

Código: [Seleccionar]
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;

int main() {
    int numero, respaldo, iteraciones = 0;
    bool esFeliz = false;
    cout << "Ingrese el numero que desea corroborar si es feliz:" << endl;
    cin >> numero;
    respaldo = numero;

    while (iteraciones <= 8 && !esFeliz)
    {
        int sumaCuadrados = 0;
        //Descomponemos número para separar sus cifras y sumar sus cuadrados
        while (respaldo > 0) //Esto se repite hasta que ya no queden cifras para descomponer
        {
            int cifra = respaldo % 10;
            sumaCuadrados += cifra * cifra;
            respaldo = respaldo / 10;
        }
        //Comprobamos suma de cuadrados
        if (sumaCuadrados == 1)
            esFeliz = true; //Bucle terminará
        else //Hay que intentar de nuevo con la suma de cuadrados obtenida
        {
            respaldo = sumaCuadrados;
            iteraciones++;
        }
    }

    if (esFeliz)
        cout << numero << " es un numero feliz :)" << endl;
    else
        cout << numero << " es un numero triste :( " << endl;

    system("pause");
    return 0;
}


487
Con el método split() de la clase String, puedes dividir la cadena en un array de String "cortando" allá donde encuentre una coma
Código: [Seleccionar]
String[] partes = cadena.split(",");
Ahí ya podrías preguntar cuánto mide ese array. Si el tamaño es 3, entonces es que al menos te han dado tres datos.
Si no mide 3, es que faltan o sobran datos.

Si tienes los tres datos, sigues adelante con las comprobaciones.
Puedes hacer un método de tipo boolean que reciba el primer dato y compruebe si es un día de la semana, por ejemplo con un switch, que devuelva true si es "lunes", o "martes", o "miercoles"... y false si no coincide con ningún día.

Los otros dos datos, las horas de inicio y fin, se los pasas por separado a otro método para comprobar que son horas.
Por ejemplo, de nuevo puedes usar split() para cortar entre los dos puntos split(":") y así obtienes otro array con las horas en el primer elemento y los minutos en el segundo elemento.
Luego los parseas a valores int y compruebas si están en los límites correctos: 0 y 23 para las horas, 0 y 59 para los minutos.

Si todos es correcto, lo último que falta comprobar es si la primera hora es anterior a la segunda.
Una forma de hacerlo es transformar esas horas al segundo del día que representan y comprobar si la primera es menor que la segunda

Es decir, por ejemplo las 00:30 representa el segundo 1800 del día:
hora 00 -> 0
minuto 30 --> 30x60 = 1800
0 + 1800 = 1800 segundos desde que ha comenzado el día


Y las 02:45 sería el segundo 9900 del día
hora 02 --> 02x60x60 = 7200
minuto 45 --> 45x60 = 2700
7200 + 2700 = 9900 segundos desde que ha comenzado el día


Como 1800 es menor que 9900, no hay duda de que las 00:30 es anterior a las 02:45

Todo esto son unas posibles formas de resolverlo. Inténtalo y consigas lo que consigas, compártelo por aquí y te ayudamos a corregir y/o completar lo que sea.



488
Hola y bienvenido.
Ya que planteas una duda, movemos el tema a la sección adecuada.

Si quieres rechazar los .exe, supongo que bastará con comprobar si el nombre del archivo finaliza con ese texto.
Algo parecido a esto:
Citar
while(true) {
            try {
            // Creamos el socket que atendera el servidor
            cliente = servidor.accept();
            // Creamos flujo de entrada para leer los datos que envia el cliente
            DataInputStream dis = new DataInputStream(cliente.getInputStream());
            // Obtenemos el nombre del archivo
            nombreArchivo = dis.readUTF();
            // Obtenemos el tamaño del archivo
            int tam = dis.readInt();
            System.out.println("VERIFICANDO TIPO DE ARCHIVO ");
            if (nombreArchivo.endsWith(".exe"))
                System.out.println("Archivo rechazado: " + nombreArchivo);
            else {

                // Creamos flujo de salida, este flujo nos sirve para
                // indicar donde guardaremos el archivo       
                //para ello en C: cree la carpeta ARCHIVO   
                FileOutputStream fos = new FileOutputStream(nombreArchivo);
                try (BufferedOutputStream out = new BufferedOutputStream(fos);
                BufferedInputStream in = new BufferedInputStream(cliente.getInputStream())) {
               etc....
               etc....
               etc....
            }

489
Aprender a programar desde cero / Re: Analisis lexico
« en: 04 de Marzo 2021, 01:07 »
Mmmhh, en Java si se me ocurren formas sencillas de hacerlo.
Pero claro, Java incorpora librerías y clases que bien usadas, te hacen la vida muy fácil.

Por ejemplo, la clase Scanner que normalmente usamos para leer por teclado, se le puede pasar un String y pedir que nos vaya dando palabras de una en una para ir comprobando si se encuentra en el diccionario.
El diccionario, si lo hacemos con un String, esta clase nos da el método contains() para con toda sencillez averiguar si la palabra existe en el diccionario.

Para resaltar las palabras coincidentes, mientras se comprueba si las palabras existen en el diccionario, se puede ir construyendo una copia del texto original en otro String, o mejor aún, en un StringBuilder.
Si se encuentra una palabra en el diccionario, la pasamos a la copia metiéndola entre corchetes por ejemplo.
Y si no se encuentra, la pasamos a la copia tal cual, sin resaltar nada.

Código: [Seleccionar]
public class Palabras {

public static void main(String[] args) {

String texto = "Este es un texto para probar un programa escrito en Java";
String diccionario = "texto programa Java";

Scanner sc = new Scanner(texto);
int contador = 0;
StringBuilder textoResaltado = new StringBuilder();

while (sc.hasNext()) {
String palabra = sc.next();
if (diccionario.contains(palabra)) {
textoResaltado.append(String.format("[%s] ", palabra));
contador++;
}
else
textoResaltado.append(String.format("%s ", palabra));
}
sc.close();

System.out.println("Texto original:\n" + texto);
System.out.println("\nDiccionario: " + diccionario);
System.out.println("\nCoincidencias: " + contador);
System.out.println("Texto con coincidendias resaltadas:\n" + textoResaltado.toString());
}

}

En pantalla, tendríamos este resultado:
Citar
Texto original:
Este es un texto para probar un programa escrito en Java

Diccionario: texto programa Java

Coincidencias: 3
Texto con coincidendias resaltadas:
Este es un [texto] para probar un [programa] escrito en [Java]

490
Comunidad / Re: Me presento
« en: 27 de Febrero 2021, 00:32 »
Bienvenido.
Espero que aprendas y también que aportes cuando te sea posible.  ;)

Un saludo.

491
Mmmhh, creo que te entiendo, pero me temo que no es posible. O yo al menos desconozco como hacerlo.

Tu idea es poder usar un método que reciba un array de clase Object, para poder aplicarlo a cualquier clase: Alumno, Comercial, Persona, Vehiculo,... ya que todos son hijos de Object.

Pero además, hacerle pasar también de algún modo el nombre del método que se ha de usar.
Si quieres sumar edades:
public double genericoSumaArray (Object[] x, getEdad() );
Si quieres sumar sueldos:
public double genericoSumaArray (Object[] x, getSueldo() );
Si quieres sumar el peso de una flota de Camiones:
public double genericoSumaArray (Object[] x, getPeso() );

Etc...

Bien, como idea, es genial... pero que yo sepa, no es posible.

De todos modos, respecto a sumar, siempre son valores int, o double, o float.
Son tipos primitivos, pero que tienen su equivalente en clase POO
int -> Integer
double - Double
float  -> Float

Así que puedes hacer que tu método genérico reciba simplemente el array Object, compruebe a que tipo de "instancia" pertenecen, castear el array a la clase  correspondiente y hacerla suma.

Este método te sirve para sumar enteros, dobles o float

Código: [Seleccionar]
public double genericoSumaArray (Object[] x) {
double suma = 0;

if (x instanceof Integer[]) {
Integer[] enteros = (Integer[]) x;
for (Integer numInt: enteros)
suma = suma + numInt;
}
else if (x instanceof Float[]) {
Float[] flotantes = (Float[]) x;
for (Float numFlo: flotantes)
suma = suma + numFlo;
}
else if (x instanceof Double[]) {
Double[] dobles = (Double[]) x;
for (Double numDob: dobles)
suma = suma + numDob;
}

return suma;
}

Así si por ejemplo quieres sumar calificaciones, pues en lugar de hacerle llegar un array de Alumno[], construyes un array de Float[] con las calificaciones.

O un Float[] con salarios, o un Integer[] con entradas de cine vendidas, o lo que sea que puedas necesitar.

492
Eso se puede hacer de mil formas diferentes, el límite lo pone la imaginación y los conocimientos sobre Java Swing.

Antes habíamos hecho que el programa comenzase ya con los dos únicos Pokemon listos para pelear.
Si queremos elegir entre tres (o más) habrá que cambiar varias cosas, ya que ahora el programa no puede comenzar listo para el combate.

Hay que dar alguna forma de que el usuario escoja los Pokemon, y como digo, hay mil formas.
Se puede mostrar un JComboBox con los nombres de los Pokemon, o un simple JOptionPane mostrando los nombres disponibles y que el usuario teclee el nombre del Pokemon escogido.

O podemos mostrar un ventana de dialogo con las imágenes de los Pokemon disponibles y que el usuario haga click en el que quiera.

La interfaz puede comenzar como antes, con los dos "paneles Pokemon", pero mostrando de inicio unas "PokeBall" y que el usuario haga click en ellas para escoger un Pokemon para cada panel.



Al clickar una Pokeball, aparece un JDialog, una ventana similar a un JFrame, y mostraría los Pokemon disponibles. Sean tres, sean cuatro, sean los que sean..



Al clickar un Pokemon, queda seleccionado dentro de ese PanelPokemon.
Luego clickamos en la otra Pokeball para elegir el oponente, y ahora ya sí, se puede combatir.



Como digo, para todo esto se requiere hacer varios cambios en lo que ya teníamos escrito, para facilitar el añadido.

Una de las cosas que añadiremos, será un ArrayList que contenga los Pokemon que hay disponibles en el juego.
La clase Pokemon habrá que modificarla. Como ahora habrán varios Pokemon, cada uno con su foto, conviene que cada Pokemon tenga la ruta a su foto correspondiente.
Antes solo teníamos dos Pokemon, así que la ruta a sus fotos la poníamos "a mano" directamente en los PanelesPokemon, porque no había elección posible.
Ahora sí que hay elección, así que no sabemos de antemano que foto mostrarán los PanelesPokemon y lo mejor es que al asignarle un Pokemon a estos paneles, sea este Pokemon quien le diga donde encontrar la foto.

De hecho, le vamos a proporcionar dos fotos a cada Pokemon, una mirando a la izquierda y otra mirando a la derecha. Según a que PanelPokemon se asignado, se mostrará una imagen o la otra, para que en la interfaz los Pokemon siempre queden encarados, mirándose el uno al otro.

También he quitado del constructor el boolean para decidir si es su turno de ataque o no.
Esto antes lo decidíamos en el momento de la creación del Pokemon, por eso estaba en el constructor.
Ahora esto no se decide al construir el Pokemon, se decidirá cuando sea seleccionado desde los PanelesPokemon por el usuario, así que no tiene sentido indicarlo en el constructor.

Marco en negrita los cambios:
Citar
public class Pokemon{
   
   private String nombre;
   private int vida;
   private Random azar = new Random();
   private String imgP1; //Ruta a la imagen cuando es player1
   private String imgP2; //Ruta a la imagen cuando es player2, misma que P1, pero invertida

   
   public String mensaje; //Mensaje que se genera tras un ataque
   public boolean suTurno; //Indica si es el turno de ataque de este Pokemon
   
   public Pokemon(String nombrePok, int vidaPok, String img1, String img2) {
      nombre = nombrePok;
      vida = vidaPok;
      mensaje = "";
      imgP1 = img1;
      imgP2 = img2;

   }
   
   public String getNombre() {
      return nombre;
   }
   
   public int getVida() {
      return vida;
   }
   
   public void setVida(int v) {
      vida = v;
   }
   
   public String getImgP1() {
      return imgP1;
   }


   public String getImgP2() {
      return imgP2;
   }


   public int atacar() {
      
      mensaje = ""; //Se generará un nuevo mensaje
      int ataque = azar.nextInt(11) + 10;
      
      if (esCritico()) {
         mensaje += "¡Ataque crítico! ";
         ataque =  (int)(ataque * 1.5);
      }
      
      mensaje += "Daño: " + ataque + " puntos";
      return ataque;
   }
   
   public void perderVida(int ataque) {
      vida -= ataque;
   }
   
   private boolean esCritico() {
      int valor = azar.nextInt(100);
      /*
       * Entre 0 y 100 hay diez valores que son
       * múltiplos de 10.
       * Es decir, hay un 10% de posibilidades
       * de obtener un valor múltiplo de 10.
       */
      
      return valor%10 == 0;
   }
   
   @Override
   public String toString() {
      return "Nombre: " + nombre + "\nVida: " + vida;
   }

}

Sobre la clase PanelPokemon, es la que va a requerir más cambios. Antes al comenzar ya estaba plenamente configurada, con su Pokemon asignado, la barra de vida ajustada al nivel de vida del Pokemon, el nombre del Pokemon,...
Ahora no, ahora ha de comenzar mostrando la foto de la Pokeball y cuando se seleccione un combatiente, ajustar nombre, foto y barra de vida según el Pokemon escogido.

Obtiene un atributo nuevo, un valor int que podrá tener dos valores: 1 ó 2.
Esto es para identificar cuál es el panel que está a la izquierda (1) y cuál a la derecha (2).
Así sabremos en que panel tenemos que poner la foto en la que el personaje mira a la derecha y en cuál la que mira a la izquierda.

Los "subpaneles" que componen este panel ahora estarán referenciados como atributos de clase para poder interactuar con ellos. Antes los instanciábamos directamente sin referenciarlos, porque no se requería acceder a ningún método que pudieran tener.

Ahora estos paneles si tendrán métodos.
PanelNombre tiene un método para configurar el nombre y PanelImagen otro para cambiar la imagen.
A esta clase interna, PanelImagen, le vamos a implementar un MouseListener para detectar cuándo el usuario hace click en la imagen.
Cuando esto ocurre, es cuando hacemos aparecer el JDialog que permite seleccionar Pokemon.

A este JDialog hay que pasarle unos parámetros, uno de ellos va a ser precisamente una referencia al PanelPokemon desde el cuál ha sido invocado.
De este modo, desde el JDialog podremos setear un Pokemon para el PanelPokemon e invocar a los métodos necesarios para que se configure respecto al Pokemon asignado.

Esta es la clase PanelPokemon reescrita. Atención especial al MouseListener de la subclase PanelImagen
Código: [Seleccionar]
public class PanelPokemon extends JPanel{

public Pokemon pokemon;
private int numPanel;
private JButton btAtacar;
private JProgressBar barraVida;
private PanelNombre pnNombre;
private PanelImagen pnImagen;
private PanelVida pnVida;

//Autoreferencia para poder transmitirsela al JDialog que selecciona Pokemon
private PanelPokemon estePanel;

public PanelPokemon(String rutaImagen, int numero) {
numPanel = numero;
pnNombre = new PanelNombre();
pnImagen = new PanelImagen(rutaImagen);
pnVida = new PanelVida();

setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(pnNombre);
add(pnImagen);
add(pnVida);
add(new PanelBoton());
estePanel = this;
}

public void reiniciar() {
if (numPanel == 1)
pnImagen.setImagen("img/pokeball_215.png");
else
pnImagen.setImagen("img/pokeball_215_p2.png");
btAtacar.setEnabled(false);
pnNombre.setNombre("Escoge Pokemon");
barraVida.setValue(0);

}

/*
* Este método lo invocará el JDialog que permite
* seleccionar Pokemon, para setearle un Pokemon
* a esta clase PanelPokemon.
* Para poder invocar este método, el JDialog necesita
* recibir una referencia a esta clase para que haya
* visibilidad entre ellas.
*/
public void setPokemon(Pokemon pok) {
pokemon = pok;
pnNombre.setNombre(pokemon.getNombre());
if (numPanel == 1) {
pnImagen.setImagen(pokemon.getImgP1());
pokemon.suTurno = true;
}
else {
pnImagen.setImagen(pokemon.getImgP2());
pokemon.suTurno = false;
}
barraVida.setMaximum(pokemon.getVida());
barraVida.setValue(pokemon.getVida());
btAtacar.setEnabled(pokemon.suTurno);
}

private class PanelNombre extends JPanel {

private JLabel nombre;

public PanelNombre() {
nombre = new JLabel("Escoge Pokemon");
nombre.setFont(new Font("Consolas", Font.BOLD, 34));
nombre.setForeground(Color.WHITE);
add(nombre);
setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.WHITE, Color.BLACK));
setBackground(new Color(23, 151, 238));
}

public void setNombre(String nombrePok) {
nombre.setText(nombrePok);
}
}

private class PanelImagen extends JPanel implements MouseListener{

private JLabel icono;

public PanelImagen(String rutaImagen) {
ImageIcon imagen = new ImageIcon(PanelPokemon.class.getClassLoader().getResource(rutaImagen));
icono = new JLabel(imagen);
icono.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(icono);
addMouseListener(this);
}

public void setImagen(String ruta) {
ImageIcon imagen = new ImageIcon(PanelPokemon.class.getClassLoader().getResource(ruta));
icono.setIcon(imagen);
}

@Override
public void mouseClicked(MouseEvent e) {
new Selector(null, true, estePanel); //JDialog recibe referencia a esta misma clase
}
@Override
public void mousePressed(MouseEvent e) { }
@Override
public void mouseReleased(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent e) { }
@Override
public void mouseExited(MouseEvent e) { }

}

private class PanelVida extends JPanel {

public PanelVida() {

barraVida = new JProgressBar();
barraVida.setMaximum(0);
barraVida.setValue(0);
barraVida.setPreferredSize(new Dimension(200, 30));
barraVida.setForeground(Color.BLUE);
add(barraVida);
barraVida.setBorder(BorderFactory.createRaisedSoftBevelBorder());

TitledBorder bordeTitulado = new TitledBorder("Nivel de Vida");
bordeTitulado.setTitleFont(new Font("Consolas", Font.ITALIC, 24));
setBorder(bordeTitulado);
}
}

private class PanelBoton extends JPanel {

public PanelBoton() {
btAtacar = new JButton("¡ATACAR!");
btAtacar.setEnabled(false);
btAtacar.setFont(new Font("Consolas", Font.BOLD, 30));
add(btAtacar);
setBorder(BorderFactory.createEmptyBorder(30, 20, 30, 30));
}
}

public void actualizarPanel() {
barraVida.setValue(pokemon.getVida());
btAtacar.setEnabled(pokemon.suTurno);
}

public void agregarAccion(ActionListener accion) {
btAtacar.addActionListener(accion);
}

}

Como hemos dicho, al hacer click en PanelImagen, se instancia la clase Selector que es un JDialog que aparece en pantalla con 8 Pokemon seleccionables.
Esta clase será quien tenga un ArrayList con 8 objetos Pokemon ya preconfigurados y listos para ser seleccionados.
En pantalla se muestran en una grilla de 2 filas y 4 columnas.

Cada imagen de estos Pokemon seleccionables, se construye mediante la subclase PanelPersonaje la cuál también implementa un MouseListener.
En esta ocasión, el MouseListener no solo responderá ante el click del ratón, también responde al momento en que la flechita del ratón pasa por encima.
De este modo, al pasar el ratón por un personaje, el fondo cambia de color para que quede resaltado respecto a los demás.
Cuando el ratón abandona su espacio, recupera el color habitual.

Cuando se hace click, se setea el Pokemon seleccionado mediante la referencia al PanelPokemon que lo ha invocado y se cierra el Selector de Pokemon.

Código: [Seleccionar]
public final class Selector extends JDialog{

private ArrayList<Pokemon> pokedex;
private PanelPokemon panelPokemon; //Referencia al panel de jugador que invoca este selector

public Selector(Frame parent, boolean modal, PanelPokemon pnPok) {
super(parent, modal);
panelPokemon = pnPok;
construirPokedex();

setLayout(new BorderLayout());
PanelMensajes pnMensaje = new PanelMensajes();
pnMensaje.setMensaje("Selecciona un personaje Pokemon");
add(pnMensaje, BorderLayout.NORTH);

JPanel roaster = new JPanel();
roaster.setLayout(new GridLayout(2, 4)); //Grilla para los 8 pokemons
for (Pokemon pok: pokedex)
roaster.add(new PanelPersonaje(pok));

add(roaster, BorderLayout.CENTER);

setTitle("Seleccionar Pokemon");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
}


private void construirPokedex() {
pokedex = new ArrayList<Pokemon>();
pokedex.add(new Pokemon("Bulbasaur", 200, "img/bulbasaur_p1.png", "img/bulbasaur_p2.png"));
pokedex.add(new Pokemon("Caterpie", 200, "img/caterpie_p1.png", "img/caterpie_p2.png"));
pokedex.add(new Pokemon("Charmander", 200, "img/charmander_p1.png", "img/charmander_p2.png"));
pokedex.add(new Pokemon("Pidgey", 200, "img/pidgey_p1.png", "img/pidgey_p2.png"));
pokedex.add(new Pokemon("Pikachu", 200, "img/pikachu_p1.png", "img/pikachu_p2.png"));
pokedex.add(new Pokemon("Rattata", 200, "img/rattata_p1.png", "img/rattata_p2.png"));
pokedex.add(new Pokemon("Squirtle", 200, "img/squirtle_p1.png", "img/squirtle_p2.png"));
pokedex.add(new Pokemon("Weedle", 200, "img/weedle_p1.png", "img/weedle_p2.png"));
}

private void cerrarDialogo() {
dispose();
}

private class PanelPersonaje extends JPanel implements MouseListener{

public Pokemon personaje;

public PanelPersonaje(Pokemon pok) {
personaje = pok;
ImageIcon imagen = new ImageIcon(
Selector.class.getClassLoader().getResource(personaje.getImgP1()));

add(new JLabel(imagen));
setToolTipText(personaje.getNombre());
setBorder(new MatteBorder(15, 15, 15, 15, Color.DARK_GRAY));
setBackground(Color.WHITE);
addMouseListener(this);
}

@Override
public void mouseClicked(MouseEvent e) {
panelPokemon.setPokemon(personaje);
cerrarDialogo();
}
@Override
public void mousePressed(MouseEvent e) { }
@Override
public void mouseReleased(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent e) {
setBackground(Color.YELLOW);
}
@Override
public void mouseExited(MouseEvent e) {
setBackground(Color.WHITE);
}
}

}

Cuando ya se han seleccionado los Pokemon, se puede comenzar la batalla.
La lógica de la batalla se lleva a cabo en la clase principal, BatallaSwing.
Esta clase requiere pocos cambios, total, en realidad tiene muy poco código.

Antes teníamos a dos Pokemon como atributos, ahora ya no.  Interactuaremos directamente con los objetos Pokemon que contiene cada PanelPokemon.

Código: [Seleccionar]
public class BatallaSwing extends JFrame{

private PanelMensajes pnMensajes;
private PanelPokemon pnPok1;
private PanelPokemon pnPok2;

public BatallaSwing() {

pnMensajes = new PanelMensajes();
pnPok1 = new PanelPokemon("img/pokeball_215.png", 1);
pnPok1.agregarAccion(new AccionAtacar());
pnPok2 = new PanelPokemon("img/pokeball_215_p2.png", 2);
pnPok2.agregarAccion(new AccionAtacar());

setLayout(new BorderLayout());

add(pnMensajes, BorderLayout.NORTH);
JPanel pnCentro = new JPanel();
pnCentro.add(pnPok1);
pnCentro.add(new PanelVS()); //PanelVS no requiere referencia, no se interactúa con ella.
pnCentro.add(pnPok2);
add(pnCentro, BorderLayout.CENTER);

setTitle("Batalla Pokemon");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}

private class AccionAtacar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {

if (pnPok1.pokemon.suTurno) {
pnPok2.pokemon.perderVida(pnPok1.pokemon.atacar());
pnMensajes.setMensaje(pnPok1.pokemon.mensaje);
pnPok1.pokemon.suTurno = false;
pnPok2.pokemon.suTurno = true;
}
else {
pnPok1.pokemon.perderVida(pnPok2.pokemon.atacar());
pnMensajes.setMensaje(pnPok2.pokemon.mensaje);
pnPok1.pokemon.suTurno = true;
pnPok2.pokemon.suTurno = false;
}

pnPok1.actualizarPanel();
pnPok2.actualizarPanel();

if (pnPok1.pokemon.getVida() <= 0 || pnPok2.pokemon.getVida() <= 0) {
String mensajeFinal = "Juego Terminado\n Ha ganado: ";
mensajeFinal += pnPok1.pokemon.getVida()>0?pnPok1.pokemon.getNombre():pnPok2.pokemon.getNombre();

JOptionPane.showMessageDialog(null, mensajeFinal, "Fin del Juego",
JOptionPane.INFORMATION_MESSAGE);


pnMensajes.setMensaje("Clicka las Pokeball para escoger Pokemon");
pnPok1.reiniciar();
pnPok2.reiniciar();
}


}
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new BatallaSwing();
}
});
}

}

Y eso es todo.
Tiene algunos fallos, por ejemplo al seleccionar un Pokemon en el lazo izquierdo, ya podemos pulsar el botón ATACAR sin haber seleccionad un rival en el otro lado.
Visualmente parece que no pasa nada pero en realidad ocurre una NullPointerException cada vez que se pulsa el botón sin tener rival escogido.

Otro fallo es que durante el combate, en cualquier momento podemos volver a escoger otro Pokemon, o el mismo, y el nuevo Pokemon comienza con la vida entera mientras el rival sigue con la barra de vida reducida por los daños.

Pero bueno, son pequeños detalles que se pueden ir puliendo.

He intentado explicar los cambios hechos lo mejor posible, pero preguntad cualquier cosa que no se entienda.

Proporciono el código fuente de dos formas:
- Una, solo el código, adjuntándolo en este mismo mensaje del foro
- Otra, código y las imágenes que he usado, en este enlace Google Drive

Se pueden usar las imágenes que uno quiera, basta con incluirlas dentro de un package en nuestro proyecto. Yo he seguido esta estructura:

493
Comunidad / Re: Hola a todos
« en: 24 de Febrero 2021, 11:26 »
Bienvenido.

Siente libre de consultar y aportar lo que quieras sobre programación.

494
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 23 de Febrero 2021, 02:06 »
Suerte con la entrega.

Yo igualmente intentaré seguir actualizándolo (hoy no he podido hacer nada...) hasta completarlo, para que sirva de futuras referencias para todo el que pase por aquí.

Y bueno, a mi también me sirve para seguir aprendiendo, la verdad es que es un ejercicio bastante completo, se está usando gran cantidad de componentes Swing.

495
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 22 de Febrero 2021, 01:39 »
El método isBlank() es relativamente nuevo, fue añadido en Java versión 11 creo..
Si tienes un JDK anterior a esa versión, no lo reconocerá.

Puedes borrarlo, tampoco es vital. Este método lo que hace es comprobar si el String solo contiene espacios en blanco.., por ejemplo, si el usuario si para el nombre solo ha pulsado unas cuantas veces la barra espaciadora.
En ese caso, el String no está vacío (empty) pero tampoco tiene un dato que resulte realmente útil y puede que queramos rechazarlo.
Por eso se incluyo ese método, para comprobar esta posibilidad de forma fácil.

Pero vamos, para un ejercicio como este, nos da igual comprobar esto o no.

496
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 21 de Febrero 2021, 21:49 »
En fin, he explicado como funciona la interfaz de este formulario.
Pongo ahora el código, que como dije, es bastante extenso ya que es un formulario con muchos componentes.
Podría haberlo separado en más clases, pero no se si sería más enrevesado que ponerlo todo junto en una sola (a excepcion del PanelFecha)

Explicarlo al completo puede ser muy tedioso, no tanto por escribirlo, si no para quien luego quiera leerlo.
Así que mejor lo pongo y si alguien tiene preguntas concretas que las haga.

Código: [Seleccionar]
public class DialogoCrearEvento extends JDialog{

private GestorEventos gestorEventos;
private Usuario usuario; //Usuario que va a crear un evento
private Font fuentePlana = new Font("Verdana", Font.PLAIN, 20);
private Font fuenteNegrita = new Font("Verdana", Font.PLAIN, 20);
private JComboBox<String> comboTipoEvento;
//Datos comunes a todos los eventos
private CampoTextoAzul campoCodigo;
private CampoTextoAzul campoTitulo;
private JTextArea areaDescripcion;
private PanelFecha fechaEvento;
private CampoTextoAzul campoRenta;
//Panel y CardLayout para altenar entre los datos extra de cada tipo evento
private PanelExtras panelExtras;
private CardLayout cardLayout = new CardLayout();
//Datos para evento deportivo
private JSpinner maximoDeporte;
private CampoTextoAzul campoEquipo1;
private CampoTextoAzul campoEquipo2;
private JComboBox<TipoDeporte> tipoDeporte;
//Datos para evento musical
private JSpinner maximoMusical;
private JComboBox<TipoMusica> tipoMusical;
private CampoTextoAzul campoSeguroMusical;
//Datos para evento religios
private JSpinner maximoReligioso;
private CampoTextoAzul campoSeguroReligioso;
//Boton para crear
private JButton btCrear;

public DialogoCrearEvento(Frame padre, boolean modal, GestorEventos gestor, Usuario user) {
super(padre, modal);
gestorEventos = gestor;
usuario = user;

iniciarComponentes();
setLayout(new BorderLayout());
add(new PanelTitulo(), BorderLayout.NORTH);
JPanel pnCentro = new JPanel();
pnCentro.setLayout(new BoxLayout(pnCentro, BoxLayout.Y_AXIS));
pnCentro.add(new PanelSelector());
pnCentro.add(new PanelDatosComunes());
pnCentro.add(panelExtras);
add(pnCentro, BorderLayout.CENTER);
add(new PanelBotonCrear(), BorderLayout.SOUTH);

setTitle("Crear Evento");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
pack();
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}

//Métodos
private void iniciarComponentes() {
comboTipoEvento = new JComboBox<String>(new String[] {"Deportivo", "Musical", "Religioso"});
comboTipoEvento.setFont(fuenteNegrita);
comboTipoEvento.setForeground(Color.BLUE);
comboTipoEvento.setBackground(Color.WHITE);
comboTipoEvento.addActionListener(new AccionSeleccionarTipoEvento());
//Componentes comunes
campoCodigo = new CampoTextoAzul(14);
campoCodigo.setEditable(false);
campoCodigo.setText(gestorEventos.getNuevoId());//Este valor lo decide el sistema
campoTitulo = new CampoTextoAzul(14);
areaDescripcion = new JTextArea(5, 22);
areaDescripcion.setFont(fuentePlana);
areaDescripcion.setForeground(Color.BLUE);
fechaEvento = new PanelFecha();
campoRenta = new CampoTextoAzul(10);
campoRenta.addFocusListener(new AccionCalcularSeguroMusical());
//Componentes deportivo
maximoDeporte = new JSpinner();
SpinnerNumberModel modeloDeporte = new SpinnerNumberModel();
modeloDeporte.setMinimum(1);
modeloDeporte.setMaximum(20000);
modeloDeporte.setValue(100);
maximoDeporte.setModel(modeloDeporte);
maximoDeporte.setFont(fuentePlana);
campoEquipo1 = new CampoTextoAzul(14);
campoEquipo2 = new CampoTextoAzul(14);
tipoDeporte = new JComboBox<TipoDeporte>(TipoDeporte.values());
tipoDeporte.setFont(fuenteNegrita);
tipoDeporte.setForeground(Color.BLUE);
tipoDeporte.setBackground(Color.WHITE);
//Componentes musical
maximoMusical = new JSpinner();
SpinnerNumberModel modeloMusical = new SpinnerNumberModel();
modeloMusical.setMinimum(1);
modeloMusical.setMaximum(25000);
modeloMusical.setValue(100);
maximoMusical.setModel(modeloMusical);
maximoMusical.setFont(fuentePlana);
tipoMusical = new JComboBox<TipoMusica>(TipoMusica.values());
tipoMusical.setFont(fuenteNegrita);
tipoMusical.setForeground(Color.BLUE);
tipoMusical.setBackground(Color.WHITE);
campoSeguroMusical = new CampoTextoAzul(8);
campoSeguroMusical.setEditable(false); //Este valor se autocalcula según la renta
//Componentes religioso
maximoReligioso = new JSpinner();
SpinnerNumberModel modeloReligioso = new SpinnerNumberModel();
modeloReligioso.setMinimum(1);
modeloReligioso.setMaximum(30000);
modeloReligioso.setValue(100);
maximoReligioso.setModel(modeloReligioso);
maximoReligioso.setFont(fuentePlana);
campoSeguroReligioso = new CampoTextoAzul(12);
campoSeguroReligioso.setEditable(false);
campoSeguroReligioso.setText(String.valueOf(Religioso.CUOTA_SEGURO)); //El valor del seguro es fijo
//Inicializamos panel de datos extra
panelExtras = new PanelExtras();
//Boton crear
btCrear = new JButton("Crear Evento");
btCrear.setFont(fuenteNegrita);
btCrear.setForeground(Color.BLUE);
btCrear.setFocusPainted(false);
btCrear.addActionListener(new AccionCrearEvento());
}

public void cerrarDialogo() {
dispose();
}

//Clases de componentes Swing
private class CampoTextoAzul extends JTextField {

public CampoTextoAzul(int columnas) {
super(columnas);
setFont(fuentePlana);
setForeground(Color.BLUE);
}
}

//Clases para los distintos paneles
private class PanelTitulo extends JPanel {

public PanelTitulo() {
JLabel titulo = new JLabel("Crear Nuevo Evento");
titulo.setFont(new Font("Verdana", Font.ITALIC, 32));
JPanel pnBienvenido = new JPanel();
pnBienvenido.add(titulo);
pnBienvenido.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(pnBienvenido);
}
}

private class PanelSelector extends JPanel {
public PanelSelector() {
add(comboTipoEvento);
TitledBorder bordeTitulo = new TitledBorder("Escoja tipo de Evento");
bordeTitulo.setTitleFont(fuentePlana);
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20),
bordeTitulo));
}
}

private class PanelConEtiqueta extends JPanel {

public PanelConEtiqueta(String textoEtiq, JComponent componente, int flotar) {
JLabel etiqueta = new JLabel(textoEtiq);
etiqueta.setFont(fuenteNegrita);
setLayout(new FlowLayout(flotar));
add(etiqueta);
add(componente);
}
}

private class PanelDatosComunes extends JPanel {

public PanelDatosComunes() {

//Panel superior tendra codigo y titulo evento
JPanel pnSuperior = new JPanel();
pnSuperior.setLayout(new GridLayout(1, 2, 5, 5));
pnSuperior.add(new PanelConEtiqueta("Código: ", campoCodigo, FlowLayout.CENTER));
pnSuperior.add(new PanelConEtiqueta("Título: ", campoTitulo, FlowLayout.CENTER));
//Panel inferior tendrá descripción, fecha y renta
JPanel pnInferior = new JPanel();
pnInferior.setLayout(new GridLayout(1, 2, 5, 5));
//A la izquierda, el area de texto
JPanel pnIzq = new JPanel();
JScrollPane sp = new JScrollPane(areaDescripcion);
pnIzq.add(sp);
TitledBorder titulo = BorderFactory.createTitledBorder("Descripción");
titulo.setTitleFont(fuentePlana);
pnIzq.setBorder(titulo);
//A la derecha, panel fecha y debajo panel renta
JPanel pnDer = new JPanel();
//pnDer.setLayout(new GridLayout(2, 1, 5, 5));
pnDer.setLayout(new BoxLayout(pnDer, BoxLayout.Y_AXIS));
pnDer.add(fechaEvento);
pnDer.add(new PanelConEtiqueta("Renta: ", campoRenta, FlowLayout.CENTER));
pnInferior.add(pnIzq);
pnInferior.add(pnDer);
//Añadimos ambos paneles, uno sobre el otro
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(pnSuperior);
add(pnInferior);
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
}
}

private class PanelExtras extends JPanel {

private JPanel pnActivo = new JPanel();

public PanelExtras() {
pnActivo.setLayout(cardLayout);
pnActivo.add(new PanelDatosExtraDeportivos(), "Deportivo");
pnActivo.add(new PanelDatosExtraMusical(), "Musical");
pnActivo.add(new PanelDatosExtraReligioso(), "Religioso");
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JSeparator(JSeparator.HORIZONTAL));
add(pnActivo);
}

public void mostrarPanel(String idPanel) {
cardLayout.show(pnActivo, idPanel);
}
}

private class PanelDatosExtraDeportivos extends JPanel {

public PanelDatosExtraDeportivos() {

setLayout(new GridLayout(2, 2, 5, 10));
add(new PanelConEtiqueta("Equipo 1: ", campoEquipo1, FlowLayout.CENTER));
add(new PanelConEtiqueta("Equipo 2: ", campoEquipo2, FlowLayout.CENTER));
add(new PanelConEtiqueta("Aforo Máximo: ", maximoDeporte, FlowLayout.CENTER));
add(new PanelConEtiqueta("Tipo Deporte: ", tipoDeporte, FlowLayout.CENTER));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
}
}

private class PanelDatosExtraMusical extends JPanel {

public PanelDatosExtraMusical() {
setLayout(new GridLayout(1, 2, 5, 10));
add(new PanelConEtiqueta("Aforo Máximo: ", maximoMusical, FlowLayout.CENTER));
add(new PanelConEtiqueta("Tipo de Música: ", tipoMusical, FlowLayout.CENTER));
add(new PanelConEtiqueta("Seguro contratado: ", campoSeguroMusical, FlowLayout.CENTER));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
}
}

private class PanelDatosExtraReligioso extends JPanel {

public PanelDatosExtraReligioso() {
setLayout(new GridLayout(1, 2, 5, 10));
add(new PanelConEtiqueta("Aforo Máximo: ", maximoReligioso, FlowLayout.CENTER));
add(new PanelConEtiqueta("Seguro contratado: ", campoSeguroReligioso, FlowLayout.CENTER));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));

}
}

private class PanelBotonCrear extends JPanel {
public PanelBotonCrear() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JSeparator(SwingConstants.HORIZONTAL));
JPanel pnBoton = new JPanel();
pnBoton.add(btCrear);
pnBoton.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
add(pnBoton);
}
}

//Clases para los Listener
private class AccionCalcularSeguroMusical implements FocusListener {
@Override
public void focusGained(FocusEvent e) {
//Nada que hacer aquí
}
@Override
public void focusLost(FocusEvent e) {
/*
* Cuando el campo renta pierde el foco,
* se calcula el importe del seguro para
* el evento musical en base al valor de la renta
*/
try {
double renta = Double.parseDouble(campoRenta.getText());
double importe = renta * Musical.CUOTA_SEGURO / 100;
campoSeguroMusical.setText(String.format("%.2f", importe));
}
catch(Exception ex) {
//Si renta no tiene valor numérico, se produce excepcion
campoSeguroMusical.setText(null);
}
}
}

private class AccionSeleccionarTipoEvento implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
panelExtras.mostrarPanel((String) comboTipoEvento.getSelectedItem());
}
}

private class AccionCrearEvento implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Recuperamos primero DATOS COMUNES
String idEv = campoCodigo.getText();
String titulo = campoTitulo.getText();
String descripcion = areaDescripcion.getText();
LocalDate fecha = fechaEvento.getFecha();
//A continuación, posible excepcion sin controlar si campoRenta no tiene un valor numérico
double renta = Double.parseDouble(campoRenta.getText());

//Ahora, según el tipo de evento, recuperamos unos datos u otros.
String tipoEvento = (String) comboTipoEvento.getSelectedItem();
switch(tipoEvento) {
case "Deportivo":
int aforoDep = (int) maximoDeporte.getValue();
String nomEquipo1 = campoEquipo1.getText();
String nomEquipo2 = campoEquipo2.getText();
TipoDeporte tipoDep = (TipoDeporte) tipoDeporte.getSelectedItem();
//Podemos crear evento
if (gestorEventos.agregarEvento(new Deportivo(idEv, titulo, descripcion, fecha, renta,
aforoDep, nomEquipo1, nomEquipo2, tipoDep))) {
gestorEventos.guardarEventos();
JOptionPane.showMessageDialog(null, "Evento Deportivo creado", "Nuevo Evento",
JOptionPane.INFORMATION_MESSAGE);
agregarIdAlUsuario(idEv);
dispose();
}
else
eventoRechazado();
break;
case "Musical":
int aforoMus = (int) maximoMusical.getValue();
TipoMusica tipoMus = (TipoMusica) tipoMusical.getSelectedItem();
//Podemos crear evento
if (gestorEventos.agregarEvento(new Musical(idEv, titulo, descripcion, fecha, renta,
aforoMus, tipoMus))) {
gestorEventos.guardarEventos();
JOptionPane.showMessageDialog(null, "Evento Musical creado", "Nuevo Evento",
JOptionPane.INFORMATION_MESSAGE);
agregarIdAlUsuario(idEv);
dispose();
}
else
eventoRechazado();
break;
case "Religioso":
int aforoRel = (int) maximoReligioso.getValue();
//Podemos crear evento
if (gestorEventos.agregarEvento(new Religioso(idEv, titulo, descripcion, fecha, renta,
aforoRel))) {
gestorEventos.guardarEventos();
JOptionPane.showMessageDialog(null, "Evento Religioso creado", "Nuevo Evento",
JOptionPane.INFORMATION_MESSAGE);
agregarIdAlUsuario(idEv);
dispose();
}
else
eventoRechazado();
}

}

private void agregarIdAlUsuario(String id) {
/*
* Para agregar, antes tenemos que castear
* a su clase hija, ya que ahora mismo
* al usuario lo tenemos instanciado
* bajo la clase padre Usuario, que
* no tiene registro de Eventos.
* Solo Administrador y DeContenidos lo tienen
*/
if (usuario instanceof Administrador)
((Administrador) usuario).agregarIdEvento(id);
else
((DeContenidos) usuario).agregarIdEvento(id);
}

private void eventoRechazado() {
JOptionPane.showMessageDialog(null, "No se pudo agregar el Evento", "Nuevo Evento",
JOptionPane.ERROR_MESSAGE);
}

}
}

En la clase principal JavaTicket añadimos un nuevo ActionListener que se encargaría de lanzar este JDialog:
Código: [Seleccionar]
//Acciones para los botones del menu gestionar eventos
private class AccionCrear implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
new DialogoCrearEvento(null, true, gestorEventos, usuarioLogueado);

}
}

Esta acción, se la hacemos llegar al panel de eventos:
Citar
   private void iniciarVista() {
      //composicion panel principal
      pnPrincipal = new PanelPrincipal();
      pnPrincipal.setAccionGestionEventos(new AccionGestionarEventos());
      pnPrincipal.setAccionGestionUsuario(new AccionGestionarUsuarios());
      //Composicion panel eventos
      pnEventos = new PanelEventos();
      pnEventos.setAccionCrear(new AccionCrear());
      pnEventos.setAccionVolver(new AccionVolver());
      //Composicion panel usuarios

Quien para poder recibirlo y asignar la acción al botón de Crear, la clase PanelEventos necesita este método:
Código: [Seleccionar]
public void setAccionCrear(ActionListener accion) {
btNuevo.addActionListener(accion);
}

Todo está actualizado en Google Drive

También puede descargarse a partir del archivo adjunto srctopic7890.zip que contiene tanto el código fuente como las imágenes (es necesario estar logado en los foros para poder descargarlo)

497
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 21 de Febrero 2021, 21:39 »
He tenido algo de tiempo para añadir la funcionalidad de Crear un Evento.

Al igual que con la creación de usuarios, usamos un JDialog con el formulario necesario para introducir los datos.
Pero esta vez es un poco más difícil.
Los eventos tienen una serie de atributos comunes, pero luego hay otros que son propios para cada tipo de evento.
Para no hacer tres formularios distintos, he hecho uno dividido en dos partes.

Una parte contiene los campos para los datos comunes. Son los que rodeo de amarillo en la imagen de abajo.
La otra parte, es un cardLayout que irá alternando entre tres formularios distintos, según el tipo de evento que señalemos en un ComboBox.
Es la parte que señalo en rojo:


Así con una sola clase podemos crear eventos de los tres tipos, pero esto implica que la clase va a tener bastante código debido a la gran cantidad de componentes Swing que intervienen.

Fijémonos en la parte amarilla, los atributos comunes.
Tiene el campo con el ID de evento. Este id no es editable, lo va a generar automáticamente el Gestor de Eventos con el formato EV000000, EV000001, EV000002.., y así sucesivamente
Tiene un campo de texto para el título un area de texto para la descripción.

Luego tiene un panel con tres Spinner para la fecha. Este panel es una clase que he escrito por separado y me ha quedado bastante bien, aunque en realidad lo más fácil habría sido poner un campo de texto y dar por hecho que el usuario va a introducir fechas correctas que podamos leer como simples String.
Pero en el enunciado he visto que para algunas acciones habrá que comparar fechas, así que no he querido dejar este campo sin asegurar "su calidad", es decir, que vamos a tener fechas válidas, nada de 45/23/2020 o alguna burrada similar.

Para esto, he escrito una clase que valiéndose de la clase LocalDate, comprueba en todo momento el año y mes que se está seleccionando, para decidir en tiempo real cuál es el valor límite para el Spinner del día.
Por ejemplo, si se marca mes valor 2 (Febrero) el límite será 28, pero si el año marcado es bisiesto, entonces el límite sería 29.
Es menos difícil de lo que parece, solo hay que manejar LocalDate (que es muy sencilla) y asignarle unos ChangeListener a los Spinner del mes y del año para que actúen sobre día cuando estos reciban algún cambio en su valor.

Código: [Seleccionar]
public class PanelFecha extends JPanel{

private JSpinner dia;
private SpinnerNumberModel modeloDia;
private JSpinner mes;
private SpinnerNumberModel modeloMes;
private JSpinner anio;
private SpinnerNumberModel modeloAnio;
private Font fuentePlana = new Font("Verdana", Font.PLAIN, 20);
private Font fuenteNegrita = new Font("Verdana", Font.PLAIN, 20);
private LocalDate fechaSelec;

public PanelFecha() {
iniciarFecha();
add(new PanelSpinner("Dia: ", dia));
add(new PanelSpinner("Mes: ", mes));
add(new PanelSpinner("Año: ", anio));
TitledBorder titulo = BorderFactory.createTitledBorder("Fecha");
titulo.setTitleFont(fuentePlana);
setBorder(titulo);
}

/**
* Retorna la fecha actualmente seleccionada
* en este panel.
* @return Fecha seleccionada.
*/
public LocalDate getFecha() {
int diaSel = (int) dia.getValue();
int mesSel = (int) mes.getValue();
int anioSel = (int) anio.getValue();
return LocalDate.of(anioSel, mesSel, diaSel);
}

/**
* El panel de fecha se inicia con la fecha actual
*/
private void iniciarFecha() {
fechaSelec = LocalDate.now();
//Año
anio = new JSpinner();
anio.setFont(fuentePlana);
modeloAnio = new SpinnerNumberModel();
modeloAnio.setMinimum(fechaSelec.getYear());
modeloAnio.setValue(fechaSelec.getYear());
anio.setModel(modeloAnio);
anio.addChangeListener(new ActualizaModeloDia());
//Mes
mes = new JSpinner();
mes.setFont(fuentePlana);
modeloMes = new SpinnerNumberModel();
modeloMes.setMinimum(1);
modeloMes.setMaximum(12);
modeloMes.setValue(fechaSelec.getMonthValue());
mes.setModel(modeloMes);
mes.addChangeListener(new ActualizaModeloDia());
//Dia
dia = new JSpinner();
dia.setFont(fuentePlana);
modeloDia = new SpinnerNumberModel();
modeloDia.setMinimum(1);
modeloDia.setMaximum(calcularMaximoDia());
modeloDia.setValue(fechaSelec.getDayOfMonth());
dia.setModel(modeloDia);
}

/**
* Calcula el máximo valor seleccionable
* para el campo dia.
* Este valor puede ser 31, 30, 28 ó 29 según
* el mes seleccionado y según si el año es
* bisiesto o no.
*/
private int calcularMaximoDia() {
switch((int)mes.getValue()) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
return 31;
case 2: //Febrero, depende de si es año bisiesto
int year = (int) anio.getValue();
if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)))
return 29;
else
return 28;
default:
return 30;
}
}

private class PanelSpinner extends JPanel {

public PanelSpinner(String texto, JSpinner spinner) {
JLabel etiq = new JLabel(texto);
etiq.setFont(fuenteNegrita);
add(etiq);
add(spinner);
setBorder(BorderFactory.createRaisedSoftBevelBorder());
}
}

/**
* Este Listener se activa cuando se cambian la fecha
* y ajusta el modelo del Spinner del dia según los valores actuales.
* Este modelo ha de cambiar según si selecciona un año bisiesto
* y/o el valor del mes
*/
private class ActualizaModeloDia implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
modeloDia.setMaximum(calcularMaximoDia());
/*
* Tras actualizar valor máximo para dia, comprobamos su
* valor actual por si ha de ser corregido
*/
if ((int) modeloDia.getValue() > (int)modeloDia.getMaximum())
modeloDia.setValue(modeloDia.getMaximum());
}
}

}


Ahora fijémonos en la parte en rojo de la imagen anterior.
Como he dicho, ese panel es un cardLayout que irá cambiando según el tipo de evento seleccionado.
Para el deportivo, tenemos los dos nombres de los equipos, un valor para el aforo y un JComboBox para el tipo de deporte.
Este JComboBox, se construye usando el enum TipoDeporte que escribimos al principio de todo.
Código: [Seleccionar]
tipoDeporte = new JComboBox<TipoDeporte>(TipoDeporte.values());
Si seleccionamos el tipo Musical, la parte inferior cambia y nos muestra tres campos distintos.



El aforo máximo, un ComboBox para el tipo de musica (también construido a partir del enum TipoMusica) y un campo que muestra el valor del seguro que se ha de pagar como extra.
Este campo no es editable y se autocalcula según el valor del campo renta.
El enunciado dice que es el 30% del importe de la renta, así que cada vez que el usuario cambia el valor del campo renta, este campo se actualiza con el nuevo cálculo.
Este valor es informativo, no se guardará cuando vayamos a crear el evento, porque la clase evento Musical ya la escribimos con un método que calcula por su propia cuenta el importe de este seguro.
Por eso le pusimos un valor constante llamado CUOTA_SEGURO, con valor 30 (30%) para hacer los cálculos con dicha constante.
De hecho, esta constante es pública y estática:
Citar
public final class Musical extends Evento{

   //Constante que establece el limite de personas que pueden asistir a este evento
   private final int AFORO_MAXIMO = 25000;
   //Constante que establece el % de cuota de seguro que se cobra sobre la renta acordada
   //Es publica y estática para que la interfaz gráfica conozca el valor de esta cuota
   public static final double CUOTA_SEGURO = 30d; //30%

Así, en este formulario, podemos acceder a esta constante para que se autocalcule el valor del seguro en pantalla.
Esto lo conseguimos con un FocusListener.
Cuando el campo renta pierde "el foco", es decir, hemos escrito algo y nos hemos pasado a otro campo, entonces se recuperar lo que se haya escrito y se calcula el importe del seguro.
Así siempre está actualizado.
Marco en negrita la línea donde llamamos a la constante de la clase Musical para saber el porcentaje de cuota que ha de pagarse por el seguro.
Además controlamos una posible excepción que ocurriría en el caso de que el campo renta estuviera vacío o tuviera letras en lugar de números y por tanto fallaría la conversión a double.

Citar
   private class AccionCalcularSeguroMusical implements FocusListener {
      @Override
      public void focusGained(FocusEvent e) {
         //Nada que hacer aquí
      }
      @Override
      public void focusLost(FocusEvent e) {
         /*
          * Cuando el campo renta pierde el foco,
          * se calcula el importe del seguro para
          * el evento musical en base al valor de la renta   
          */
         try {
            double renta = Double.parseDouble(campoRenta.getText());
            double importe = renta * Musical.CUOTA_SEGURO / 100;
            campoSeguroMusical.setText(String.format("%.2f", importe));
         }
         catch(Exception ex) {
            //Si renta no tiene valor numérico, se produce excepcion
            campoSeguroMusical.setText(null);
         }
      }
   }

Para los eventos religiosos, tenemos solo dos campos:

Un Spinner para el aforo y un campo de texto para el importe del seguro. En los eventos religiosos, este importe es siempre el mismo, independientemente de la renta.
En su clase, figura también como una constante pública y estática.

Citar
public final class Religioso extends Evento{

   //Constante que establece el limite de personas que pueden asistir a este evento
   private final int AFORO_MAXIMO = 30000;
   //Constante que establece el importe fijado como seguro por desgaste de la grama
   public static final int CUOTA_SEGURO = 2000; //Public y estático, no será necesario usar getter para acceder a este valor

La intención de hacer constantes públicas para estos valores es porque, si esto fuera un programa real, en caso de que estas cuotas quisieran ser modificadas bastaría con cambiar el valor solo dentro de sus clases para que los cambios automáticamente se replicaran en el resto de clases.
Si pusiéramos "a mano" estos valores en los formularios, habría que recorrer las clases que usen estos valores para actualizarlos las nuevas cuotas.

En un programa real, donde pueden haber decenas de clases y formularios, esto daría mucho trabajo y pie a errores si algunas clases no fueran actualizadas respecto a las demás.
(Sigue a continuación...)

498
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 21 de Febrero 2021, 20:22 »
Pues no se... :-\

Otra forma, más laboriosa pero que debería funcionar..., sería crear los packages y luego crear las clases una a una.
No copiar el archivo .java, sino desde NetBeans ir creando clases con el mismo nombre.

Luego lo que harías sería copiar el texto de los .java del zip, y pegar ese texto en las clases que vas creando dentro de NetBeans.

Es muy laborioso, pero así te aseguras de que las clases creadas, NetBeans las tiene bien registradas en ese proyecto.

499
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 21 de Febrero 2021, 19:13 »
Hmmm, mi proyecto está hecho bajo Eclipse. No se si NetBeans tendrá opción para importar proyecto desde Eclipse.

En cualquier caso, crea un nuevo proyecto llamado JavaTicket.
Dentro de ese proyecto, crea 4 packages con los nombres:
eventos, gui, img, usuarios

Ahí puedes copiar las clases del archivo zip desde el explorador de Windows a los packages de NetBeans.
Trata de replicar esta estructura:



Incluso quizás puedas copiar directamente las carpeta del zip sin crear previamente packages.
Un package en realidad, es una carpeta como otra cualquiera.


500
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 21 de Febrero 2021, 01:28 »
Hola.
Lo ya escrito no creo que cambie apenas a partir de ahora.
Desde luego la parte de gestión de usuarios se puede considerar finalizada.

Las clases Evento, puede que surja algún cambio, según vayamos añadiendo funcionalides al panel de gestión de eventos.

Al cuál por cierto, añado otro detalle que no había tenido en cuenta.
Si el usuario logueado es Limitado, además de no poder gestionar Usuarios, tampoco va a poder hacer gran cosa con los Eventos. Tan solo va a poder visualizarlos.

Así que tenemos que permitirle acceder al panel de gestión de eventos, pero desactivando los botones para las acciones en las que no tiene permisos.

Así que a PanelEventos le añadimos este método:

Código: [Seleccionar]
public void setLimitado(boolean limitado) {
//si limitado es TRUE, los enabled serán FALSE
btNuevo.setEnabled(!limitado);
btCancelar.setEnabled(!limitado);
btEditar.setEnabled(!limitado);
}

Si este método recibe un TRUE, desactivará dichos botones.

Este método será invocado cada vez que se muestre este panel en pantalla, lo hacemos desde el Listener llamado AccionGestionarEventos, que está escrito en la clase principal JavaTicket.
Al invocarlo, comprobamos si el usuario logueado es de clase Limitado, en cuyo caos enviaremos un TRUE a este método.

Citar
   //Acciones para los botones del menu principal
   private class AccionGestionarEventos implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         pnEventos.setLimitado(usuarioLogueado instanceof Limitado?true:false);
         cardLayout.show(panelActivo, "evento");
      }
   }

Y ahora sí, si nos logueamos con un usuario limitado, encontraremos algunos botones inaccesibles.

Páginas: 1 ... 20 21 22 23 24 [25] 26 27 28 29 30 ... 50

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".