jueves, 30 de marzo de 2017

Como Crear Ventana Java manualmente facilmente y sin layouts con Control de Eventos y ActionListener

Como crear una Ventana en Java manualmente sin layouts,  con Control de Eventos y ActionListener

 
Crear una ventana para datos sin utilizar el Wizard o diseñador de frames, para mi es el método mas idóneo para muchos novatos y estudiantes de java para aprender a crear las ventanas, por que permite que se conozcas mas profundamente como funciona la librería swing y que opciones tenemos a mano, sin tener que recurrir a la ayuda del wizard. En mi opinión es mas limpio y fácil de documentar que usar el diseñador del IDE que estemos utilizando, ya sea NetBeans o Eclipse los mas utilizados por programadores de Java.

Vamos a ponernos a escribir código desde el principio, empezando por crear el proyecto. Si ya sabes crear un proyecto, simplemente sigue adelante con el tutorial. Si no, pulsa sobr el este link para ir al tutorial de creación de proyectos en NetBeans.


Una vez creado el proyecto, vamos a dar el siguiente paso que es crear los paquetes y las clases que necesitemos, y luego escribir el código que se requiera en la clase inicio que será nuestra clase principal y desde la cual correremos el programa.

En este tutorial crearemos dos ventanas. Una será un menú, y otra será un formulario.

Vamos a crear la primera ventana:

Creamos un paquete en el cual se guardaran todas las ventanas a crear, para ello nos colocamos dentro de nuestro proyecto sobre el icono Source Packages y pulsamos el botón derecho del ratón. 

Esto abrirá una menú contextual y en el cual nos colocaremos con el ratón sobre la opción New, lo cual hará que se muestra un nuevo menú contextual donde pulsaremos sobre la opción Java Package...








Se nos abrirá una ventana igual o similar a la siguiente, en donde escribimos en el recuadro Package Name el nombre que le vamos a poner a nuestro paquete. dejamos los demás recuadros sin tocar nada. Pulsamos en el botón Finish.

  

Lo anterior genera un nuevo paquete llamado ventanas, que al estar vacío presenta un color gris. Posicionamos el puntero del ratón sobre este icono y pulsamos el botón derecho. Se abrirá un submenú contextual, en donde con el ratón nos colocamos sobre New, y en el nuevo submenú que se abre pulsaremos sobre la opción Java Class. Esta acción abre una ventana similar a la abajo indicada. Escribimos el nombre de la clase, en este caso Menu de mi ejemplo y pulsamos sobre el botón Finish.


Lo anterior, crea la clase y la resalta dentro del paquete ventanas, como vemos en la columna izquierda que muestra cuerpo del proyecto y nos ubica directamente dentro de la clase para que comencemos a escribir el código de la misma, como vemos en la imagen.


Vamos a ir creando nuestras ventanas, con el mínimo de componentes que necesitemos para darles funcionalidad, es decir, crearé un solo botón que será parte de las opciones del menú.
En realidad Java tiene librerías para Menú, (JMenu, JMenuBar) que podríamos utilizar, pero la idea es hacerlo a nuestra manera. Luego podemos experimentar con las librerías y determinar en que caso nos conviene hacerlo de una forma u otra.
Los componentes iniciales serán JFrame (ventana), JLabel (etiquetas), JTextField (recuadros de texto editables), JButton (botones).
Aquí debemos tener claro que hay dos formas de escribir una clase para hacer un Frame, mediante herencia y sin usar herencia. En nuestro caso usaré inicialmente herencia, y vamos heredar del paquete JFrame.
Veamos el código hasta, ahora:

Lo primero que podemos notar son esos puntos amarillos con un punto mas pequeño rojo a la izquierda de las lineas. Esto señala que tenemos un error de escritura o que falta algún elemento o librería.
Nos colocamos encima del primer punto, y hacemos click sobre él. Inmediatamente nos sale una ventana de ayuda, con diferentes opciones para subsanar el error, explicaciones del posible motivo que los genera, o sugerencias para corregirlo. Veamos la imagen para entenderlo.
Como vemos la ventana de ayuda nos dice que podemos añadir la librería javax.swing.JFrame o crear una clase JFrame. Evidentemente lo que vamos hacer es importar la librería. Así que si hacemos click sobre la opción resaltada, NetBeans nos añade automáticamente la librería como vemos en la imagen:

Los puntos restantes son causados por la misma razón, es decir, que debemos añadir las librerías. Pero cuidado!, algunas veces se nos presentan otras librerías relacionadas, pero que tienen un efecto distinto al deseado. En este caso el paquete javax.swing es el correcto, y ademas los otros errores son generados por paquetes que están contenidos en el mismo, es decir forman parte del paquete, con lo que se colocamos un asterisco, o sea, javax.swign.*, resolvemos todos. Vamos a verlo en la imagen:

 Todos los puntos de error desaparecen. Yo prefiero añadir cada librería individualmente. A continuación vamos a escribir el constructor por defecto, la definición de la ventana, y la definición del metodo componentes, que estará vació y que luego desarrollaremos.


Con lo anterior ya tenemos la ventana contenedora del menú que vamos a definir. Ahora vamos a probarlo, vamos a escribir lo siguiente new Menu(); en la clase inicio, que es la principal y desde donde vamos a correr la aplicación. Lo colocamos dentro del método main. Asi que ahora vamos a editar inicio que esta en la pestaña que tenemos al lado izquierdo en la imagen, identificada como inicio.java

Vemos que nos sale un error y su pulsamos sobre el punto y veremos que es que debemos importar, la clase Menu que esta dentro del paquete ventanas. pero OJO, aquí sale una opción adicional y es lo que decía yo antes cuando escribí cuidado!. Veamos la imagen:
Nosotros hemos escrito nuestra propia clase Menu, que es la que hemos definido antes, porque si escogemos cualquiera de las otras opciones que tiene una clase Menu, lo más seguro es que los resultados no sean los deseados. Por lo tanto aqui escogemos nuestra clase que es la primera opción. Fijate que esta también en el paquete que hemos creado llamado ventanas. Escogemos entonces la opción Add import for ventanas.Menu.

Si ahora probamos el código escrito por nosotros que sucederá?
Vamos averiguarlo colocando nuestro ratón dentro de cualquier sitio dentro del código de la clase inicio y pulsamos el botón derecho del ratón. Se nos abre el submenú contextual y escogemos la opción Run File pulsando sobre ella.



Con esto corremos nuestra aplicación y debemos obtener un resultado igual o similar al siguiente:




Como podemos ver parece ser que la ventana es un poco larga, para ser un menú normal. Puede que necesitemos menos. Para ajustar nuestras medidas de la ventana, simplemente accedemos de nuevo la clase Menu, y modificamos el setSize(x,y); donde "x" es el ancho e "y" el alto. Vamos hacerlo como práctica si lo deseas, sino continua mas adelante después de esta parte. Dentro de la clase Menu, vamos a setSize(400,600) y lo cambiamos a setSize(400,450). Con esto veremos el nuevo aspecto. Volvemos a la clase menú y volvemos a correr la aplicación. Abajo podemos ver ahora la diferencia.



Podemos apreciar que ahora nuestra ventana Menu es mas corta. Otra cosa que deben apreciar los estudiantes noveles es que NetBeans nos indica cuando el programa esta corriendo, y lo hace en la parte de abajo indicando que proyecto esta corriendo. Si ahora volvemos a correr el programa desde la clase inicio, se ejecutaría otra instancia, con lo que tendríamos dos ventanas Menu abiertas.

Ahora continuemos construyendo nuestro menú a nuestro gusto. Para ello volvemos a la clase Menu y vamos a incluir los componentes necesarios para crear la opciones del menú en forma de botones (button). Igualmente pondremos una etiqueta (label), que la usaremos como un subtitulo.

Vamos hacer unos cambios al principio de la clase Menu, vamos a cambiar los JLabel, eliminar los JTextFiled y cambiar los JButton. Debido a que vamos a destinar esta ventana a un menú, lo que necesitamos básicamente son botones.

Solo se escribieron para enseñar los errores generados por la falta de librerías y la forma de como solucionarlos.


 Ahora volvemos a escribir labelSubtitulo y cambiamos los botones por btnRegistro y btnSalir. Después en el método componentes definimos estos componentes como vemos a continuación:

 


 



Vemos que en el método componentes se instancia el componente con new, se le asigna un lugar en la ventana con setBounds, se le coloca un texto y luego se añade a la ventana mediante add.

Ahora vamos a la clase inicio, boton derecho y Run File, para volver a correr nuestro programa. El siguiente debe ser el resultado o similar.




 Como vemos ya tenemos armado algo del menú, pero falta la funcionalidad. Pero es que antes de ponernos a escribir los demás botones de opciones que falta, es preferible completar el funcionamiento, para un botón y luego de que este funcione, simplemente copiar, pegar y sustituir nombre y textos.

Para añadirle funcionalidad al menú falta que el programa haga algo cuando pulsamos sobre los botones Registro y Salir. Lo conseguimos añadiendo la librería ActionListener. Vamos a ver el codigo, empezamos por implementar la interface ActionListener en nuestra clase Menu:


Vemos el punto rojo que nos marca que hay error o falta algo, pulsamos sobre el para ver que es lo que necesitamos, y veremos en primera instancia que es importar la libreria. Pulsamos para que la importe y obtenemos lo siguiente:


Pulsamos y Add import para que NetBeans importe la librería escribiendo el código por nosotros y vemos ahora que aunque ya ha importado la librería, nos sigue marcando un error.


Volvemos a pulsar sobre el error y ahora nos indica lo siguiente:


Como vemos NetBeans nos indica que hay que implementar los métodos abstractos de la interface que se acaba de importar. Pulsamos sobre Implement all abstract methods y NetBeans nos escribirá automáticamente los métodos, pero OJO, los mismos no estarán definidos y veremos como hacerlo luego, por ahora vamos a dejar que NetBeans los cree por nosotros.

Ya el código a tomado un tamaño que hace imposible, que podamos tomar desde la pantalla una captura de la imagen y que contenga los cambios. Por ello vamos a copiar y pegar el código a continuación.

/** @author José Miguel Guimerá Padrón.  */
package ventanas;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class Menu extends JFrame implements ActionListener {
   
    private JLabel labelSubtitulo;
    private JButton btnRegistro,btnSalir;
   
    public Menu(){

        componentes();
                                                      // Al cerrar la ventana
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// se detiene prog
    setSize(400,450); // Tamaño de la Ventana Ancho y Largo
    setLocationRelativeTo(null); // Centra la ventana en el monitor
    setLayout(null); // elimina toda plantilla.
    setResizable(false); // evita se pueda modificar el tamaño de ventana
    setVisible(true); // hace visible la ventana
    setTitle("  ** Menú Principal **"); // Le pone un titulo a la ventana
   
     }

   
    public void componentes(){

            labelSubtitulo=new JLabel();
            labelSubtitulo.setBounds(110, 25, 180, 20);
            labelSubtitulo.setText("Seleccione  una  opción");
            add(labelSubtitulo);
           
            btnRegistro=new JButton();
            btnRegistro.setBounds(120, 50, 150, 20);
            btnRegistro.setText("Registrar");
            add(btnRegistro);
            btnRegistro.addActionListener(this);
           
            btnSalir=new JButton();
            btnSalir.setBounds(120,400, 150, 20);
            btnSalir.setText("Salir");
            add(btnSalir);
       
     }



// codigo añadido por la interface ActionListener

    @Override
    public void actionPerformed(ActionEvent e) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
 
} // fin de la clase Menu



Esta última parte es la que ha añadido el NetBeans por nosotros y también añadió la librería java.awt.event.ActionEvent;

No hemos puesto ningún ActionListener en el botón Salir, para dejar que Uds. completen esos detalles que simplemente es fijarse como está en los demás.

Ahora vamos a definir que debe hacer el programa según sea el botón que pulsemos en el menú. Vamos a fijar nuestra atención en la ultima parte de la clase Menu y vamos a modificarla de la siguiente forma.

@Override public void actionPerformed(ActionEvent e) {

    if(e.getSource().equals(btnRegistro)){
      
       System.out.println("Se ha pulsado el botón Registrar");
       System.out.println("Esta accion abrira una futura ventana de Registro");
      
        }


Ahora vamos a volver a correr nuestra aplicación y probemos pulsar en el botón de Registrar y ver si se escribe el mensaje por la consola.  Vamos a verlo:


 Podemos ver que ya el botón Registrar tiene funcionalidad. Ahora vamos a repetir el proceso de crear una nueva ventana. Será la ventana Registro, la cual será llamada en su momento por el botón que acabamos de probar.

Si algo no ha ido como se esperaba y tiene un resultado negativo, es hora de volver atrás y repasar todo.


Yo soy de los que piensa, que mientras mas simplificado trabajemos, mas rápido y limpio escribiremos el código para obtener lo que se desea al final. Para ello les sugiero que hagamos los siguientes  cambios en la clase Inicio, eliminemos los comentarios y vamos a comentar la sentencia // new Menu(); y agregamos la siguiente sentencia new Registro(); con esto podemos ir probando la programación que vayamos escribiendo mientras definimos en nueva ventana. Una vez que nuestra nueva ventana tenga el aspecto que deseamos, pondremos los controles necesarios y restituimos este cambio anterior que hemos realizado.




Ahora vamos a escribir el código para la nueva pantalla que será algo parecido a los siguiente:

/** @author José Miguel Guimerá Padrón. */
package ventanas;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class Registro extends JFrame implements ActionListener {
   
    private JLabel labelSubtitulo,labelNombres,labelApellidos;
    private JTextField txtNombres,txtApellidos;
    private JButton btnGuardar,btnCancelar;
   
    public Registro(){


        componentes();

        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        setSize(400,450); // Tamaño de la Ventana Ancho y Largo
        setLocationRelativeTo(null); // Centra la ventana en el monitor
        setLayout(null); // elimina toda plantilla.
        setResizable(false); // evita modificar el tamaño de ventana
        setVisible(true); // hace visible la ventana
        setTitle("  ** Módulo de Registros **"); // Pone titulo a la ventana   
   
    }
   
    public void componentes(){
       
       
labelSubtitulo = new JLabel();
       
labelSubtitulo.setBounds(92, 20, 260, 20);
       
labelSubtitulo.setText("Rellene todos los recuadros");
        add(labelSubtitulo);
       
        /*--------- Campos ---------*/
       
        labelNombres = new JLabel(); // etiqueta
       
labelNombres.setBounds(20, 50, 150,20);
       
labelNombres.setText("Nombres");
        add(
labelNombres);
       
       
txtNombres = new JTextField(); // recuadro a rellenar
       
txtNombres.setBounds(90, 50, 300, 20);
        add(txtNombres);
       
        labelApellidos = new JLabel();
       
labelApellidos.setBounds(20, 80, 150, 20);
       
labelApellidos.setText("Apellidos");
        add(
labelApellidos);
       
        txtApellidos = new  JTextField();
       
txtApellidos.setBounds(90, 80, 300, 20);
        add(
txtApellidos);
       
        /*---------- Botones ----------*/
       
        btnGuardar = new JButton();
       
btnGuardar.setBounds(100, 400, 100, 20);
       
btnGuardar.setText("Guardar");
       
btnGuardar.addActionListener(this);
        add(
btnGuardar);

        btnCancelar = new JButton();
       
btnCancelar.setBounds(220, 400, 100, 20);
       
btnCancelar.setText("Cancelar");
       
btnCancelar.addActionListener(this);
        add(
btnCancelar);
   
    }

@Override
  public void actionPerformed(ActionEvent e) {
       
     if(e.getSource().equals(btnGuardar)){
       System.out.println("Lanzamos una rutina para grabar los datos");
 

        }
    }
   
}


Debemos recordar que podemos cambiar el tamaño de la ventana si así es requerido, y colocar los recuadros y etiquetas de otra forma. Aquí simplemente se muestra cómo, sin hacer muchos cambios, podemos construir una nueva ventana, basada en la anterior.

En esta nueva ventana si hay un elemento de control que debemos destacar y es la sentencia:

     setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

Esta sentencia es diferente en la clase (ventana) Menu, ya que en caso de la nuevas ventanas no deseamos que cerrando la ventana se temrine la ejecucción del programas, sino que vuelva el control al menu principal.

Con la sentencia anterior lo que se logra es que si el usuario pulsa sobre :


No suceda nada, o sea, que para salir de la ventana se debe pulsar Guardar o Cancelar. Guardar simplemente debería ejecutar una acción de grabar datos en algún archivo o base de datos.En ejemplo simplemente escribe en la consola simulando la grabación. Cancelar simplemente aborta lo que se tenga rellenado y devuelve el control a la ventana Menu.

Vamos a correr ahora el programa y ver como va quedando. Recuerda que hemos hecho un cambio en Inicio para que solo muestre la ventana Registro

 


Vemos en la imagen que todo ha ido bien. Si pulsamos sobre Guardar varias veces, simplemente se repetirá el mensaje "Lanzamos una rutina para grabar los datos". El botón cancelar no funciona porque no le hemos asignado ninguna acción. En este punto nos encontramos que que no podemos ni salir ni cerrar la ventana, así que para solucionar este problema y terminar con el programa debemos pulsar en la parte inferior de NetBeans en la "x" que está al lado derecho del indicador del programa que se está corriendo, como se ve en la imagen inferior.



Ahora vamos añadir algunas cosas al código anterior de la clase Registro, a la clase Menu y modificar nuevamente la clase Inicio. Vamos a empezar con la clase Menu ya que estamos con ese código.


En la clase Registro iremos hasta está sección que vemos abajo en la imagen que es el método que se añade al implementar ActionListener y que captura las acciones de pulsar con el ratón el botón Guardar en este caso, para añadir dos sentencias: this.dispose(); y luego new Menu();



Continuamos con la clase Menu, e igualmente vamos a dirigirnos a este mismo método y vamos a eliminar dos lineas y añadir dos lineas que serán this.dispose() y new Registro().

Recuerda que las dos clase tiene el mismo método definido en cada caso según su funcionalidad con respecto al botón pulsado.


Ahora vamos a modificar la clase Inicio, eliminamos Registro() y descomentamos Inicio(), para que el programa vuelva a iniciar desde la ventana Menu, procedemos a la modificación y la dejamos así:
 

Volvemos a correr nuestro programa y veremos como funciona. El comportamiento que debe tener es que muestra la ventana Menu y al pulsar sobre el botón Registro, ya no hay mensaje de que se va abrir una nueva ventana, sino que es la propia ventana Registrar la que aparece lista para ser rellenada y Guardar luego los datos insertados serán representados por un mensaje en consola y volverá al Menu.

Aquí los dejo el código final de cada un a de las clases:

/* Clase Inicio */


/** @author José Miguel Guimerá Padrón */
package principal;

import ventanas.*;

public class inicio {
   
    public static void main(String[] args) {
       
       new Menu();

     }
 }


/*Clase Menu*/

/** @author José Miguel Guimerá Padrón.  */
package ventanas;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class Menu extends JFrame implements ActionListener {
   
    private JLabel labelSubtitulo;
    private JButton btnRegistro,btnSalir;
   
    public Menu(){

        componentes();
                                                      // Al cerrar la ventana
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// se detiene el programa
        setSize(400,450); // Tamaño de la Ventana Ancho y Largo
        setLocationRelativeTo(null); // Centra la ventana en el monitor
        setLayout(null); // elimina toda plantilla.
        setResizable(false); // eviata que se pueda modificar el tamaño de ventana
        setVisible(true); // hace visible la ventana
        setTitle("  ** Menú Principal **"); // Le pone un titulo a la ventana
   
     }
   
    public void componentes(){

            labelSubtitulo=new JLabel();
            labelSubtitulo.setBounds(110, 25, 180, 20);
            labelSubtitulo.setText("Seleccione  una  opción");
            add(labelSubtitulo);
           
            btnRegistro=new JButton();
            btnRegistro.setBounds(120, 50, 150, 20);
            btnRegistro.setText("Registrar");
            add(btnRegistro);
            btnRegistro.addActionListener(this);
           
            btnSalir=new JButton();
            btnSalir.setBounds(120,400, 150, 20);
            btnSalir.setText("Salir");
            add(btnSalir);
       
     }

    @Override
    public void actionPerformed(ActionEvent e) {

        if(e.getSource().equals(btnRegistro)){

   //       System.out.println("Se ha pulsado el botón Registrar");
   //       System.out.println("Esta accion abrira una futura ventana de Registro");
            this.dispose();           
            new Registro();
        }
    }
 
} // fin de la clase Menu



/*Clase Registro*/

/** @author José Miguel Guimerá Padrón. */
package ventanas;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class Registro extends JFrame implements ActionListener {
   
    private JLabel labelSubtitulo,labelNombres,labelApellidos;
    private JTextField txtNombres,txtApellidos;
    private JButton btnGuardar,btnCancelar;
   
    public Registro(){
       
        componentes();

        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        setSize(400,450); // Tamaño de la Ventana Ancho y Largo
        setLocationRelativeTo(null); // Centra la ventana en el monitor
        setLayout(null); // elimina toda plantilla.
        setResizable(false); // eviata que se pueda modificar el tamaño de ventana
        setVisible(true); // hace visible la ventana
        setTitle("  ** Módulo de Registros **"); // Le pone un titulo a la ventana   
   
    }
   
    public void componentes(){
       
        labelSubtitulo = new JLabel();
        labelSubtitulo.setBounds(92, 20, 260, 20);
        labelSubtitulo.setText("Rellene todos los recuadros");
        add(labelSubtitulo);
       
        /*--------- Campos ---------*/
       
        labelNombres = new JLabel(); // etiqueta
        labelNombres.setBounds(20, 50, 150,20);
        labelNombres.setText("Nombres");
        add(labelNombres);
       
        txtNombres = new JTextField(); // recuadro a rellenar
        txtNombres.setBounds(90, 50, 300, 20);
        add(txtNombres);
       
        labelApellidos = new JLabel();
        labelApellidos.setBounds(20, 80, 150, 20);
        labelApellidos.setText("Apellidos");
        add(labelApellidos);
       
        txtApellidos = new  JTextField();
        txtApellidos.setBounds(90, 80, 300, 20);
        add(txtApellidos);
       
        /*---------- Botones ----------*/
       
        btnGuardar = new JButton();
        btnGuardar.setBounds(100, 400, 100, 20);
        btnGuardar.setText("Guardar");
        btnGuardar.addActionListener(this);
        add(btnGuardar);

        btnCancelar = new JButton();
        btnCancelar.setBounds(220, 400, 100, 20);
        btnCancelar.setText("Cancelar");
        btnCancelar.addActionListener(this);
        add(btnCancelar);
   
    }

    @Override
    public void actionPerformed(ActionEvent e) {
       
        if(e.getSource().equals(btnGuardar)){
            System.out.println("Lanzamos una rutina para grabar los datos");
            this.dispose();
            new Menu();
        }
       
    }
   
} // fin de la clase Registro

En un nuevo tutorial haremos una continuación de este programa, usando una base de datos y añadiendo otros campos y nuevas clases. Pondré un link al mismo una vez lo tenga terminado

Hasta aquí dejo el tutorial.  Espero sus comentarios, para aclarar conceptos, como añadir opciones en caso de que no puedan hacerlo con lo que ya esta explicado, etc... por favor haganlo en los comentarios.

Asi mismo tu opinión es muy valiosa, asi que si tienes alguna observacion sobre el código y formas distinta o alternativas de hacelro por favor comentalo para publicarlas y enriquecer el texto.

Como siempre si detectas algún error u omisión, o hacer una observación importante, por favor deja un comentario, para corregir, cambiar los conceptos que sean necesarios. Muchas Gracias por visitar mi blog.

9 comentarios:

Unknown dijo...

Excelente explicación.

Gracias

Unknown dijo...


Es muy útil y lleva mucho tiempo usar aplicaciones para eventos.
uso de aplicaciones móviles para eventos

Unknown dijo...

Excelente explicación.

Flavia dijo...

Me sumo al comentario de Gonzalo....EXCELENTE!!!
Muchas Gracias.

The Change dijo...

Amigo, muchas gracias. Muy bien explicado, saludos.

Anónimo dijo...

Muy bueno recién comienzo y he logrado captar una buena idea, gracias por tu trabajo!!

Unknown dijo...

👍👏

calamerino dijo...

caso de que no puedan hacerlo con lo que ya esta explicado, etc... por favor haganlo en los comentarios.

Asi mismo tu opinión es muy valiosa, asi que si tienes alguna observacion sobre el código y formas distinta o alternativas de hacelro por favor comentalo para publicarlas y enriquecer el texto.

Como siempre si detectas https://coaching-mastery.com/emulador-3d-para-pc/

Emmanuel dijo...

Buen contenido

Publicar un comentario

Tu comentario puede ser muy útil, asi que no dudes en dejar el tuyo.
Si es relacionado con algún problema, error. etc. trata de ser muy explicito, y detallar el problema y las circunstancias en las que se produjeron. Si lanza algún error trata de copiarlo y pegarlo junto con una descripción del problema, ya que a veces los errores del sistema no son en realidad la causa del problema.

Vistas de página en total