Página Principal

miércoles, 30 de marzo de 2016

Objetos Parcelable en Android


Si llegaron aquí, se encontraron con el problema de ¿Cómo paso objetos complejos a través de las actividades? Bueno cabe aclarar que según sé, no es la única forma (en JAVA podríamos serializar objetos). Pero, Android nativamente nos provee de esta nueva clase extendida llamada Parcelable.

No voy a comparar ni evaluar cuál es mejor, pero asumo que ésta última se integra mejor, especialmente por estar creada para Android.

Vamos a crear el típico ejemplo de objeto Persona como si fuera java pero implementando Parcelable. Se vera de esta forma:


public class Persona implements Parcelable 
{
 SimpleDateFormat formateador;
 
    String nombre;
    int edad;
    Date fechaNacimiento;
 
    public Persona() {
        super();
  formateador = new SimpleDateFormat("dd/MM/yyyy");
    }
 
    public String getNombre() {
        return nombre;
    }
 
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
 
    public int getEdad() {
        return edad;
    }
 
    public void setEdad(int edad) {
        this.edad = edad;
    }
 
    public String getFechaNacimiento() {
        if ( fechaNacimiento!= null )
  {
   return formateador.format(fechaNacimiento);
  }
  else
  {
   return "";
  } 
    }
 
    public void setFechaNacimiento(Date fecha_Nacimiento) {
        try
  {
   this.fechaNacimiento = formateador.parse(fecha_Nacimiento);
  }
  catch (ParseException e)
  {
   this.fechaNacimiento = null;
  }
    }
 

NOTA: La fecha de nacimiento solo se guarda como Date, pero esta manejada como String y no Date. Esto es porque en Android y más precisamente si utilizamos SQLite, solo habrá TEXT. Entonces como puse en el ejemplo, será la mejor forma de capturar y formatear el objeto

Ahora bien, hasta aquí creamos un objeto común y corriente. Pero cuando implementan Parcelable les exigirá que coloquen al menos dos nuevos métodos (writeToParcel() y describeContents()). El segundo no lo utilizaras. El primero si es importante ya que nos permitirá escribir nuestro objeto en un Parcel.


@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(this.nombre);
    dest.writeInt(this.edad);
    dest.writeString(formateador.format(fechaNacimiento));
}
 

Nota: Otra ventaja de manejar la fecha como String es que tanto para escribir como leer un Parcel no reconoce la clase Date.

También es necesario crear un constructor que reciba un parámetro tipo Parcel, el cual nos permitirá crear nuestro objeto desde allí.


public Producto(Parcel in){
  super();
  formateador = new SimpleDateFormat("dd/MM/yyyy");
  readFromParcel(in);
}

private void readFromParcel(Parcel in) {

  this.nombre = in.readString();
  this.edad = in.readInt();
  try
  {
   this.fechaNacimiento = formateador.parse(in.readString());
  }
  catch (ParseException e)
  {
   this.fechaNacimiento = null;
  }    
    }

Nota Importante: Como se puede observar, tanto para leer como escribir un Parcel, no existe key o una posición o algo que nos indique que dato extraemos, por lo que en este caso el orden si altera el producto, será importante siempre escribir o leer los datos en el mismo orden o generara un error o mínimo se intercambiaran los atributos.

Ahora para generar las instancias de nuestra clase parcelable necesitamos un objeto Parcelable.Creator, el cual se verá así:


public static final Parcelable.Creator<Persona> CREATOR = new Parcelable.Creator<Persona>() {
    public Persona createFromParcel(Parcel source) {
        return new Persona(source);
    }
 
    public Persona[] newArray(int size) {
        return new Persona[size];
    }
};

Una vez hecho esto, ya tendríamos el objeto funcional. Bien ahora les mostrare como darle uso práctico en el traspaso de actividades por medio de Intent...


//En Main
 
 Persona unaPersona= new Persona();
 
 EditText nombreEdittext = (EditText) findViewById(R.id.nombreEditText);
        EditText edadEditText = (EditText) findViewById(R.id.edadEditText);
        TextView fechaTextView = (TextView) findViewById(R.id.fechaTextView);

 unaPersona.setNombre(nombreEdittext.getText().toString());
 unaPersona.setEdad(edadEditText.getText().toString());
 unaPersona.setFechaNacimiento(fechaTextView.getText().toString());
 
 Intent intent = new Intent(this, Activity2.class);     
        intent.putExtra("OBJETO_PERSONA", persona);
        startActivity(intent);
 
//En la Activity2
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity2);
 
    Bundle b = getIntent().getExtras();
    Persona persona = b.getParcelable("OBJETO_PERSONA");
 
    EditText editTextNombre = (EditText)findViewById(R.id.editTextNombre);
    EditText editTextEdad = (EditText)findViewById(R.id.editTextEdad);
    EditText editTextFecha = (EditText)findViewById(R.id.editTextFecha);
 
    editTextNombre.setText(persona.getNombre());
    editTextEdad.setText(String.valueOf(persona.getEdad()));
    editTextFecha.setText(persona.getFechaNacimiento());
 

Cualquier duda comenten
Un saludo, Cidius

domingo, 27 de marzo de 2016

EditText con Botones símil WhatsApps

Hola gente:


Acá en esta entrega voy a explicar cómo hacer que un EditText de Android se “vea” como si tuviera botones dentro de él, como en WhatsApp...


En principio, desconociendo si verdaderamente la gente que diseño este formato lo hace de otra forma. Debo decirles que esto es solamente un efecto visual, por eso antes escribí  el “vea”.

Si amigos, lo que debemos hacer es trabajar a nivel XML. Primero debemos insertar un EditText y tantos botones (dependiendo nuestro diseño serán ImageButton) quedáramos dentro de un componente layout con orientación horizontal (Para que estén uno al lado del otro).


Lo siguiente es tan simple como modificar el estilo al layout contenedor con esta línea:
style="@android:style/Widget.EditText"

Quedando de esta forma…



 <LinearLayout
        style="@android:style/Widget.EditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" 
        android:layout_marginTop="10dp">

        <EditText
            android:id="@+id/editProductoBuscar"
            android:layout_width="210dp"
            android:layout_height="wrap_content"
            android:background="@null" >
        </EditText>

        <ImageButton
            android:id="@+id/buttonProductoBusqueda"
            android:layout_width="38dp"
            android:layout_height="38dp"
            android:src="@drawable/lupitas" />

        <ImageButton
            android:id="@+id/imageButtonProductoCam"
            android:layout_width="40dp"
            android:layout_height="38dp"
            android:layout_marginLeft="2dp"
            android:src="@android:drawable/ic_menu_camera" />
    </LinearLayout>
 



Así es como se vería el diseño final de ese layout mostrado:


Espero que les sirva
Un saludo, Cidius


viernes, 25 de marzo de 2016

Guardar imagenes con SQLite

Hola amigos!
Estoy iniciando mis primeros pasos en este mundo de los blogger. Así que sepan disculpar lo desordenado...

Llevo tiempo programando en android, primero en eclipse y ahora en Android Studio cada vez mas, pero me sucedió que buscando información, vídeos, código reciclable, lo que sea sobre como tratar las imágenes con una base de datos SQLite y no encontré nada que me cerrara, por eso decidí volcar algo de contenido sobre este tema. Si bien encontré sugerencias, diferencia entre guardar la imagen en la base de datos o solo una ruta a la memoria, que es mejor, y porque. Me parece que les puedo aportar algo de luz

Mi idea y luego de leer algunos foros, nada es definitivo, pero me intereso mas guardar la imagen en la SDcard, en alguna carpeta destino, y conservar la ruta de esa imagen en la base de dato en forma de String.

En esta entrada voy a explicar todo mediante una actividad que primero te permita tomar una foto, se crea todo el proceso de archivo, se guarda en la SDcard y se extrae la dirección completa, para luego guardarla en una Base de datos.

En el siguiente ejemplo muestro la actividad principal.


   
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import app.com.databasefull.database.SQLcontrolador;

public class MiActivity extends AppCompatActivity {

    //ruta base de la SDcard: /storage/sdcard0/...
    private final String ruta_fotos = Environment.getExternalStorageDirectory().
                                            getAbsolutePath() +"/Kiosquito/com/imagenes/";
    private File file = new File(ruta_fotos);
    private Button botonTomarFoto, botonVerListaDeFotos;
    private TextView textview;
    SQLcontrolador sqlcontrol;

    Uri uri;
    File mi_foto;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mi);

        //Inicializo y abro la base;
        sqlcontrol = new SQLcontrolador(this);
        sqlcontrol.abrirBaseDeDatos();

        //======== codigo nuevo ========
        botonTomarFoto = (Button) findViewById(R.id.btnTomaFoto);
        botonVerListaDeFotos = (Button) findViewById(R.id.buttonVerFotos);
        textview = (TextView) findViewById(R.id.textView);

        //Si no existe crea la carpeta donde se guardaran las fotos
        file.mkdirs();
        //accion para el boton para tomar una foto;
        botonTomarFoto.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            String file = ruta_fotos + getCode() + ".png";
            mi_foto = new File( file );
            try {
            mi_foto.createNewFile();
            } catch (IOException ex) {
                    Log.e("ERROR ", "Error:" + ex);
                    }
                //
                uri = Uri.fromFile( mi_foto );
                //Abre la camara para tomar la foto
                Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                //Guarda imagen
                cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                //Retorna a la actividad
                startActivityForResult(cameraIntent, 0);

                }
        });
        //Boton que ejecuta una actividad con un listview que muestra las imagenes (*)-1
        botonVerListaDeFotos.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent imagenesIntent = new Intent(getApplicationContext(),VerFotos.class);
                startActivity(imagenesIntent);
            }
        });
        //====== codigo nuevo:end ======
        }
        /**
        * Metodo privado que genera un codigo unico segun la hora y fecha del sistema
     * * @return photoCode
     * */
            @SuppressLint("SimpleDateFormat")
    private String getCode() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
        String date = dateFormat.format(new Date() );
        String photoCode = "pic_" + date;
        return photoCode;
        }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_tomar_foto, menu);
        return true;
        }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // Comprobamos si el resultado de la segunda actividad es "RESULT_CANCELED".
        if (resultCode == RESULT_CANCELED) {
            // Si es así mostramos mensaje de cancelado por pantalla.
            Toast.makeText(this, "Resultado cancelado", Toast.LENGTH_SHORT)
                    .show();
        } else {
            //este es un text que solo muestra la dir en la actividad;
            textview.setText("dir:"+ uri.getPath());
            //Insertamos la direccion y nombre de la imagen en la base de datos (*)-2
            sqlcontrol.insertarImagen(uri.getPath(),mi_foto.getName());
            }
        }
    }
   


(*)-1 Para mostrar la imagen en un ListView tienen que usar un Adaptador base, que son los mas personalizable. Un pequeño problema que van a tener es que la foto que sacan tiene una gran resolución, y cuando las enlistar por medio del adaptador en mi dispositivo mas de 4 fotos estallaba.

Rápidamente leí que para mostrar fotos como una especie de galería, lo mejor era mostrar la miniatura de la imagen. Encontré un códigos para reciclar y obtener la imagen miniatura pero no supe hacerlo andar.
Luego de buscar bastante encontré una función que redimenciona la foto dinámicamente en java.

Aqui paso a mostrar el getView() del BaseAdapter junto con la función de redimencionarImagen. Sepan que al adaptador lo creo pasandole un ArrayList de un objeto imagen q tiene 3 atributos (id,nombre y ruta)



 @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        // Declare Variables
        TextView txtTitle;
        ImageView imgImg;

        //http://developer.android.com/intl/es/reference/android/view/LayoutInflater.html
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View itemView = inflater.inflate(R.layout.item_lista_img, parent, false);

        // Locate the TextViews in listview_item.xml
        txtTitle = (TextView) itemView.findViewById(R.id.tv_titulo_single_post_circuito);
        imgImg = (ImageView) itemView.findViewById(R.id.imagen_single_post_circuito);

        //Aqui creo un File con la ruta de la imagen
        File imageFile = new  File(listaObjeto.get(position).getRutaImagen());
        if(imageFile.exists()){
            //muestro el nombre de la imagen
            txtTitle.setText(listaObjeto.get(position).getNombreImagen().substring(0,18));
            //Aqui decodifico, genero y guardo en bitmap lo que esta en la direccion
            Bitmap bm=BitmapFactory.decodeFile(imageFile.getAbsolutePath());
            //Antes de mostrar la imagen en el ImageView la redimenciono
            imgImg.setImageBitmap(redimensionarImagenMaximo(bm,50,50));
        }

        return itemView;
    }

    public Bitmap redimensionarImagenMaximo(Bitmap mBitmap, float newWidth, float newHeigth){
        //Redimensionamos
        int width = mBitmap.getWidth();
        int height = mBitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeigth) / height;
        // create a matrix for the manipulation
        Matrix matrix = new Matrix();
        // resize the bit map
        matrix.postScale(scaleWidth, scaleHeight);
        // recreate the new Bitmap
        return Bitmap.createBitmap(mBitmap, 0, 0, width, height, matrix, false);
    }
 

(*)-2 Les mostrare las 2 funciones que utilizo para insertar y leer las rutas y nombres de las imagenes dentro de nuestra base de datos



public void insertarImagen(String ruta, String nombre){

        ContentValues cv = new ContentValues();
        cv.put(DBhelper.IMAGEN_NOMBRE, nombre);
        cv.put(DBhelper.IMAGEN_RUTA, ruta);
        long id= database.insert(DBhelper.IMAGEN_TABLA, null, cv);
    }

    public ArrayList<ImagenObjeto> leerImagenes() {

        Cursor c = database.rawQuery("SELECT _id, nombre,ruta FROM imagenbd", null);

        ArrayList<ImagenObjeto> listImagen= new ArrayList<ImagenObjeto>();
        ImagenObjeto unImagenObj; //creo un objeto Imagen

        while(c.moveToNext()){
            unImagenObj= new ImagenObjeto();
            unImagenObj.setIdImagen(c.getInt(0));
            unImagenObj.setNombreImagen(c.getString(1));
            unImagenObj.setRutaImagen(c.getString(2));
            listImagen.add(unImagenObj); //Agrego a la lista el objeto
        }
        return listImagen;

    }
 

Bueno esto seria todo lo básico que se necesita para manejar imágenes con una base de datos SQLite.
En breve mostrare un vídeo con la App andando para que vean como funciona
Espero que les sirva y cualquier cosa pregunten.

Un saludo;