jueves, 14 de noviembre de 2013

Generando un documento PDF en Android



Como generar un documento PDF en Android
(How create PDF document in Android)

En este tutorial vamos a generar un documento PDF en un dispositivo Android:
  • Generar un documento PDF, en nuestro tutorial, crearemos una factura y la guardaremos en la sdcard.
  • Leeremos este documento y lo mostraremos en la pantalla del dispositivo.
  • Además enviaremos en documento PDF como adjunto por correo electrónico. 
  • Utilizaremos el tipo de fuente externa arialuni.ttf unicode que soporta caracteres cirílicos

Para este tutorial utilizaremos Android Studio (I/O preview) 0.3.5.
Además para generar el PDF vamos a utilizar las librerías iText Java PDF que podéis descargar desde aquí itextg-5.4.4.zip de este paquete solo utilizaremos itextg-5.4.4.jar, también la encontrarán en el proyecto que dejaré para descargar al final del tutorial.

Lo primero es crear un proyecto nuevo.
Después de haber generado el proyecto nuevo, crearemos una carpeta con el nombre de assets\fonts donde copiaremos nuestro archivo de fuentes arialuni.ttf , como muestra la imagen señalado con el número 1 en rojo. Esta carpeta íntegra la copiamos dentro de la carpeta build\ComAndroidSupportAppcompact... para evitar file not found en tiempo de ejecución, como se muestra en el número 2 en rojo.

También crearemos una carpeta con el nombre de lib donde copiaremos la librería nuestra itextg-5.4.4.jar, como muestra la siguiente imagen en el número 3 en rojo.







Lo siguiente es crear las dependencias a esta librería (itextg-5.4.4.jar) en nuestro proyecto, lo haremos desde los (Open Module Settings).

Esta opción nos muestra la pantalla de configuración, lo primero es seleccionar Dependencias número 1 en rojo, después pulsamos el botón +  número 2 en rojo, y buscamos en nuestro proyecto en la carpeta lib la librería  itextg-5.4.4.jar


Revisaremos en nuestro proyecto el archivo build.gradle para ver si tenemos bien configuradas nuestras dependencias, debería quedarnos algo parecido a esto:

Después de haber creado y configurado nuestro proyecto, comenzaremos a trabajar con el activity_main.xml, aquí crearemos básicamente tres botones, uno para crear otro para leer y otro para enviar el documento.


    
        



Para obtener el siguiente menú:






































A continuación veremos como quedaría la clase asociada MainActivity.java:

public class MainActivity extends ActionBarActivity {
    InvoiceObject invoiceObject = new InvoiceObject();
    private String INVOICES_FOLDER = "Invoices";
    private String FILENAME = "InvoiceSample.pdf";
    //Declaramos la clase PdfManager
    private PdfManager pdfManager = null;

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

        //Creamos una factura desde nuestro código solo para poder generar el documento PDF
        //con esta información
        createInvoiceObject();

        //Código generado por Android Studio
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }

        try {
            //Instanciamos la clase PdfManager
            pdfManager = new PdfManager(MainActivity.this);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }

        Button create_pdf = (Button)findViewById(R.id.button_create_pdf);
        Button read_pdf = (Button)findViewById(R.id.button_read_pdf);
        Button send_email_pdf = (Button)findViewById(R.id.button_email_pdf);

        create_pdf.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Create PDF document
                assert pdfManager != null;
                pdfManager.createPdfDocument(invoiceObject);
            }
        });

        read_pdf.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                assert pdfManager != null;
                pdfManager.showPdfFile(INVOICES_FOLDER + File.separator + FILENAME,MainActivity.this);
            }
        });

        send_email_pdf.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String emailTo ="mailTo@gmail.com";
                String emailCC ="mailCC@yahoo.com";
                assert pdfManager != null;
                pdfManager.sendPdfByEmail(INVOICES_FOLDER + File.separator + FILENAME,emailTo,emailCC, MainActivity.this);
            }
        });
    }

    //Código generado por Android Studio
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    //Código generado por Android Studio
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        switch (item.getItemId()) {
            case R.id.action_settings:
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
    //Código generado por Android Studio
    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }
    }

    //Creando la factura por hard code
    private void createInvoiceObject(){
        invoiceObject.id=1234;
        invoiceObject.companyName="Movalink.com";
        invoiceObject.companyAddress="Calle Madrid, 1024. Cp. 30100";
        invoiceObject.companyCountry="España";
        invoiceObject.clientName="Telefónica";
        invoiceObject.clientAddress="Calle Asturias, 25, Cp. 40100";
        invoiceObject.clientTelephone="900 123 123";
        invoiceObject.date= "12/11/2013";
        invoiceObject.total=77.5;

        InvoiceDetails invoiceDetails1 = new InvoiceDetails();
        invoiceDetails1.itemCode="ART001";
        invoiceDetails1.itemName="Article 1 description";
        invoiceDetails1.quantity=2;
        invoiceDetails1.price=12.5;
        invoiceDetails1.total=25.0;

        InvoiceDetails invoiceDetails2 = new InvoiceDetails();
        invoiceDetails2.itemCode="ART002";
        invoiceDetails2.itemName="Article 2 description";
        invoiceDetails2.quantity=5;
        invoiceDetails2.price=10.5;
        invoiceDetails2.total=52.5;

        invoiceObject.invoiceDetailsList = new ArrayList<invoicedetails>();
        invoiceObject.invoiceDetailsList.add(invoiceDetails1);
        invoiceObject.invoiceDetailsList.add(invoiceDetails2);
    }
}

A continuación mostraremos el código de la clase utilizada para Generar, Mostrar y Enviar por e-mail el documento PDF, el código esta lo suficientemente comentado para que se puedan entender los procedimientos y la lógica de funcionamiento.

PdfManager.java


public class PdfManager {
    private static Context mContext;
    private static final String APP_FOLDER_NAME = "com.movalink.pdf";
    private static final String INVOICES = "Invoices";
    private static Font catFont;
    private static Font subFont ;
    private static Font smallBold ;
    private static Font smallFont ;
    private static Font italicFont ;
    private static Font italicFontBold ;

    //Declaramos nuestra fuente base que se encuentra en la carpeta "assets/fonts" folder
    //Usaremos arialuni.ttf que permite imprimir en nuestro PDF caracteres Unicode Cirílicos (Ruso, etc)
    private static BaseFont unicode;

    //!!!Importante: La carpeta "assets/fonts/arialuni.ttf" debe estar creada en nuestro projecto en
    //la subcarpeta "PdfCreator/build/exploded-bundles/ComAndroidSupportAppcompactV71900.aar"
    //En el caso de que Android Studio la eliminara la copiamos manualmente
    //PdfCreator/build/exploded-bundles/ComAndroidSupportAppcompactV71900.aarassets/fonts/arialuni.ttf
    private static File fontFile = new File("assets/fonts/arialuni.ttf");

    //Constructor set fonts and get context
    public PdfManager(Context context) throws IOException, DocumentException {
        mContext = context;
        //Creamos los distintos estilos para nuestro tipo de fuente.
        unicode = BaseFont.createFont(fontFile.getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        catFont = new Font(unicode, 22,Font.BOLD, BaseColor.BLACK);
        subFont = new Font(unicode, 16,Font.BOLD, BaseColor.BLACK);
        smallBold = new Font(unicode, 12,Font.BOLD, BaseColor.BLACK);
        smallFont = new Font(unicode, 12,Font.NORMAL, BaseColor.BLACK);
        italicFont = new Font(unicode, 12,Font.ITALIC, BaseColor.BLACK);
        italicFontBold = new Font(unicode, 12,Font.ITALIC|Font.BOLD, BaseColor.BLACK);
    }

    //Generando el documento PDF
    public void createPdfDocument(InvoiceObject invoiceObject) {
        try {

            //Creamos las carpetas en nuestro dispositivo, si existen las eliminamos.
            String fullFileName = createDirectoryAndFileName();

            if(fullFileName.length()>0){
                Document document = new Document();
                PdfWriter.getInstance(document, new FileOutputStream(fullFileName));

                document.open();

                //Creamos los metadatos del alchivo
                addMetaData(document);
                //Adicionamos el logo de la empresa
                addImage(document);
                //Creamos el título del documento
                addTitlePage(document, invoiceObject);
                //Creamos el contenido en form de tabla del documento
                addInvoiceContent(document,invoiceObject.invoiceDetailsList);
                //Creamos el total de la factura del documento
                addInvoiceTotal(document, invoiceObject);

                document.close();

                Toast.makeText(mContext, "PDF file created successfully", Toast.LENGTH_SHORT).show();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String createDirectoryAndFileName(){

        String FILENAME = "InvoiceSample.pdf";
        String fullFileName ="";
        //Obtenemos el directorio raiz "/sdcard"
        String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
        File pdfDir = new File(extStorageDirectory + File.separator + APP_FOLDER_NAME);

        //Creamos la carpeta "com.movalink.pdf" y la subcarpeta "Invoice"
        try {
            if (!pdfDir.exists()) {
                pdfDir.mkdir();
            }
            File pdfSubDir = new File(pdfDir.getPath() + File.separator + INVOICES);

            if (!pdfSubDir.exists()) {
                pdfSubDir.mkdir();
            }

            fullFileName = Environment.getExternalStorageDirectory() + File.separator + APP_FOLDER_NAME + File.separator + INVOICES + File.separator + FILENAME;

            File outputFile = new File(fullFileName);

            if (outputFile.exists()) {
                outputFile.delete();
            }
        } catch (ActivityNotFoundException e) {
            Toast.makeText(mContext,e.getMessage(), Toast.LENGTH_SHORT).show();
        }
        return fullFileName;
    }

    //PDF library add file metadata function
    private static void addMetaData(Document document) {
        document.addTitle("movalink PDF");
        document.addSubject("Using iText");
        document.addKeywords("Java, PDF, iText");
        document.addAuthor("movalink.com");
        document.addCreator("movalink.com");
    }

    //Creando el Título y los datos de la Empresa y el Cliente
    private static void addTitlePage(Document document, InvoiceObject invoiceObject)
            throws DocumentException {

        Paragraph preface = new Paragraph();
        // Adicionamos una línea en blanco
        addEmptyLine(preface, 1);
        // Adicionamos el títulos de la Factura y el número
        preface.add(new Paragraph(mContext.getResources().getString(R.string.invoice_number) + invoiceObject.id, catFont));
        preface.add(new Paragraph(mContext.getResources().getString(R.string.invoice_date) + new Date(), italicFont));

        //Adicionamos los datos de la Empresa
        preface.add(new Paragraph(mContext.getResources().getString(R.string.company) + " " + invoiceObject.companyName,smallFont));
        preface.add(new Paragraph(invoiceObject.companyAddress ,smallFont));
        preface.add(new Paragraph(invoiceObject.companyCountry,smallFont));

        addEmptyLine(preface, 1);

        //Adicionamos los datos del Cliente
        preface.add(new Paragraph(mContext.getResources().getString(R.string.client_title), smallBold));

        preface.add(new Paragraph(mContext.getResources().getString(R.string.client_name) + " " + invoiceObject.clientName,smallFont));
        preface.add(new Paragraph(invoiceObject.clientTelephone,smallFont));
        preface.add(new Paragraph(invoiceObject.clientAddress ,smallFont));
        preface.add(new Paragraph(invoiceObject.clientCountry));

        addEmptyLine(preface, 1);

        //Adicionamos el párrafo creado al documento
        document.add(preface);

        // Si queremos crear una nueva página
        //document.newPage();
    }

    //Creamos el contenido de la factura, las líneas con los artículos.
    private static void addInvoiceContent(Document document, java.util.List<invoicedetails> invoiceDetail) throws DocumentException {

        Paragraph paragraph = new Paragraph();
        addEmptyLine(paragraph, 1);
        // Creamos una tabla con los títulos de las columnas
        createInvoiceTable(paragraph, invoiceDetail);
        // Adicionamos el párrafo al documento
        document.add(paragraph);

    }

    //Creamos el subtotal y el total de la factura.
    private static void addInvoiceTotal(Document document, InvoiceObject invoiceObject) throws DocumentException {

        Paragraph paragraph = new Paragraph();
        addEmptyLine(paragraph, 1);
        // Adicionamos la tabla al párrafo
        createTotalInvoiceTable(paragraph, invoiceObject);
        // Adicionamos el párrafo al documento
        document.add(paragraph);

    }

    //Procedimiento para crear los títulos de las columnas de la factura.
    private static void createInvoiceTable(Paragraph tableSection, java.util.List<invoicedetails> invoiceDetails)
            throws DocumentException {

        int TABLE_COLUMNS = 5;
        //Instaciamos el objeto Pdf Table y creamos una tabla con las columnas definidas en TABLE_COLUMNS
        PdfPTable table = new PdfPTable(TABLE_COLUMNS);// number of table columns

        //Definimos el ancho que corresponde a cada una de las 5 columnas
        float[] columnWidths = new float[]{80f, 200f, 50f, 80f, 100f};
        table.setWidths(columnWidths);

        //Definimos el ancho de nuestra tabla en %
        table.setWidthPercentage(100);

        // Aquí les dejos otras propiedades que pueden aplicar a la tabla
        // table.setBorderColor(BaseColor.GRAY);
        // table.setPadding(4);
        // table.setSpacing(4);
        // table.setBorderWidth(1);

        //Definimos los títulos para cada una de las 5 columnas
        PdfPCell cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.detail_code),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        //Adicionamos el título de la primera columna
        table.addCell(cell);

        cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.detail_description),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        //Adicionamos el título de la segunda columna
        table.addCell(cell);

        cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.detail_amount),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        //Adicionamos el título de la tercera columna
        table.addCell(cell);

        cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.detail_price),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        //Adicionamos el título de la cuarta columna
        table.addCell(cell);

        cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.detail_total),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        //Adicionamos el título de la quinta columna
        table.addCell(cell);

        //Creamos la fila de la tabla con las cabeceras
        table.setHeaderRows(1);

        //Creamos las lineas con los artículos de la factura;
        for (InvoiceDetails orderLine : invoiceDetails) {
            createInvoiceLine(orderLine, table);
        }

        tableSection.add(table);
    }


    //Procedimiento para crear una lines vacía
    private static void addEmptyLine(Paragraph paragraph, int number) {
        for (int i = 0; i < number; i++) {
            paragraph.add(new Paragraph(" "));
        }
    }
    //Procedimiento para adicionar una imagen al documento PDF
    private static void addImage(Document document) throws IOException, DocumentException {

        Bitmap bitMap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitMap.compress(Bitmap.CompressFormat.JPEG, 80, stream);
        byte[] bitMapData = stream.toByteArray();
        Image image = Image.getInstance(bitMapData);
        //Posicionamos la imagen el el documento
        image.setAbsolutePosition(400f, 650f);
        document.add(image);
    }


    //Procedimiento para crear las líneas de la factura en forma de tabla.
    private static void createInvoiceLine(InvoiceDetails invoiceLine, PdfPTable table) {
        PdfPCell cell = new PdfPCell();

        //Adicionamos celdas sin formato ni estilos, solo el valor
        table.addCell(invoiceLine.itemCode);
        table.addCell(invoiceLine.itemName);

        //Adicionamos celdas con formato y estilo: (font, align) para el correspondiente valor
        cell.setPhrase(new Phrase(String.valueOf(invoiceLine.quantity),smallFont));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

        //Adicionamos celdas con formato y estilo: (font, align)
        cell.setPhrase(new Phrase(String.valueOf(invoiceLine.price), smallFont));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

        //Adicionamos celdas con formato y estilo: (font, align)
        cell.setPhrase(new Phrase(String.valueOf(invoiceLine.total), smallFont));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

    }


    //Procedimiento para crear los totales y subtotales de la factura en forma de tabla.
    //Misma lógica utilizada para crear los títulos de las columnas de la factura
    private static void createTotalInvoiceTable(Paragraph tableSection, InvoiceObject orderHeaderModel)
            throws DocumentException {

        int TABLE_COLUMNS = 2;
        PdfPTable table = new PdfPTable(TABLE_COLUMNS);

        float[] columnWidths = new float[]{200f, 200f};
        table.setWidths(columnWidths);

        table.setWidthPercentage(100);

        //Adicionamos el título de la celda
        PdfPCell cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.invoice_subtotal),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

        double subTotal = orderHeaderModel.total;
        //Adicionamos el contenido de la celda con el valor subtotal
        cell = new PdfPCell(new Phrase(String.valueOf(subTotal)));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);

        table.addCell(cell);

        //Adicionamos el título de la celda
        cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.invoice_tax),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

        //Adicionamos el contenido de la celda con el valor tax
        cell = new PdfPCell(new Phrase(String.valueOf(0)));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

        //Adicionamos el título de la celda
        cell = new PdfPCell(new Phrase(mContext.getResources().getString(R.string.detail_total),smallBold));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

        //Adicionamos el contenido de la celda con el valor total
        cell = new PdfPCell(new Phrase(String.valueOf(orderHeaderModel.total)));
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        table.addCell(cell);

        tableSection.add(table);

    }

    //Procedimiento para mostrar el documento PDF generado
    public void showPdfFile(String fileName, Context context){
        Toast.makeText(context, "Leyendo documento", Toast.LENGTH_LONG).show();

        String sdCardRoot = Environment.getExternalStorageDirectory().getPath();
        String path = sdCardRoot + File.separator + APP_FOLDER_NAME + File.separator + fileName;

        File file = new File(path);

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(file),"application/pdf");
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            context.startActivity(intent);
        }
        catch (ActivityNotFoundException e) {
            Toast.makeText(context, "No Application Available to View PDF", Toast.LENGTH_SHORT).show();
        }
    }

    //Procedimiento para enviar por email el documento PDF generado
    public void sendPdfByEmail(String fileName, String emailTo, String emailCC, Context context){

        Intent emailIntent = new Intent(Intent.ACTION_SEND);
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Movalink PDF Tutorial email");
        emailIntent.putExtra(Intent.EXTRA_TEXT, "Working with PDF files in Android");
        emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{emailTo});
        emailIntent.putExtra(Intent.EXTRA_BCC, new String[]{emailCC});

        String sdCardRoot = Environment.getExternalStorageDirectory().getPath();
        String fullFileName = sdCardRoot + File.separator + APP_FOLDER_NAME + File.separator + fileName;

        Uri uri = Uri.fromFile(new File(fullFileName));
        emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
        emailIntent.setType("application/pdf");

        context.startActivity(Intent.createChooser(emailIntent, "Send email using:"));
    }

}

El resultado de nuestro documento PDF es el siguiente:





































Enviamos por e-mail el documento PDF, utilizando Intent.ACTION_SEND




































No olvidar hacer los cambios pertinentes en el AndoridManifest.xml, para permitir la lectura y escritura en nuestro dispositivo.



    
    
    
Puede descargar el código fuente en: PDFCreatorSource

martes, 27 de agosto de 2013

Galería de Imágenes usando swipe



Como crear una Galería de Imágenes usando "GridView" y "Swipe"
(How create image gallery using gridview and swipe)

Intentaremos crear una galería de imagen bastante sencilla, con la funcionalidad de:
  • Mostrar la miniaturas en un "GridView".
  • Seleccionar una imagen para verla en tamaño original.
  • una vez seleccionada una imagen podremos hacer "swipe" a la derecha o a la izquierda para continuar viendo las imágenes de la galería en tamaño original.
  • Al volver a la galería de miniaturas, siempre tendremos seleccionada la ultima imagen que vimos en tamaño original.  

Para ello crearemos solamente tres activities.
- MainActivity (Se crea por defecto cuando creamos el proyecto)
- ShowGalleryActivity
- SwipeImageActivity

Y ademas crearemos cuatro layouts.
- activity_main (Se crea por defecto cuando creamos el proyecto)
- images_gallery (Contiene el GridView)
- show_image (Es el contenedor de la imagen original, con algunos textos de ayuda)
- swipe_image_layout (Contiene el componente ViewPager, donde cargaremos el layout show_image, con las imágenes originales para hacer el efecto de "swipe").

Las imágenes que utilizaremos para esta galería las colocaremos, en una de nuestras carpetas de recursos, en nuestro caso drawable-xhdpi

Para este tutorial hemos usado Android Studio (I/O Preview) 0.2.5

1.- Después de haber creado un nuevo projecto, copiaremos nuestras imágenes a la carpeta de recursos drawable-xhdpi.
2.- A continuación creamos un layout llamado images_gallery




3.- Creamos una Activity llamada ShowGalleryActivity y la enlazamos con el 
images_gallery layout (setContentView(R.layout.images_gallery);)

A continuación el código con los comentarios suficientes para que se pueda entender.
public class ShowGalleryActivity extends Activity {

    public static int mSelected = 0;
    private GridView gridview;
    // Hacemos referencia a nuestras imagenes en miniatura de la carpeta de recursos
    public static Integer[] mImagesIds = {
            R.drawable.i1_thumb, R.drawable.i2_thumb,
            R.drawable.i3_thumb, R.drawable.i4_thumb,
            R.drawable.i5_thumb, R.drawable.i6_thumb,
            R.drawable.i7_thumb, R.drawable.i8_thumb,
            R.drawable.i9_thumb, R.drawable.i10_thumb,
            R.drawable.i11_thumb, R.drawable.i12_thumb,
            R.drawable.i13_thumb, R.drawable.i14_thumb,
            R.drawable.i15_thumb, R.drawable.i16_thumb,
            R.drawable.i17_thumb, R.drawable.i18_thumb,
            R.drawable.i10_thumb, R.drawable.i20_thumb,
            R.drawable.i21_thumb, R.drawable.i22_thumb,
            R.drawable.i23_thumb, R.drawable.i24_thumb,
            R.drawable.i25_thumb, R.drawable.i26_thumb,
            R.drawable.i27_thumb, R.drawable.i28_thumb,
            R.drawable.i29_thumb, R.drawable.i30_thumb,
            R.drawable.i31_thumb
    };


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

        //Hacemos referncia nuestro GridView
        gridview = (GridView) findViewById(R.id.gridview);
        //Asignamos el ImageAdapter que hemos creado en esta misma clase al GridView
        gridview.setAdapter(new ImageAdapter(this));
        //Nos movemos al elemento seleccionado del GridView
        gridview.setSelection(mSelected);
    }
    @Override
    protected void onStart() {
        super.onStart();
        gridview = (GridView) findViewById(R.id.gridview);
        gridview.setAdapter(new ImageAdapter(this));
        gridview.setSelection(mSelected);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.show_gallery_activity, menu);
        return true;
    }

    //ImageAdapter para almacenar las imagemes en el GridView
    public class ImageAdapter extends BaseAdapter {
        private Context mContext;
        public ImageAdapter(Context c) {
            mContext = c;
        }
        public int getCount() {
            return mImagesIds.length;
        }
        public Object getItem(int position) {
            return null;
        }
        public long getItemId(int position) {
            return 0;
        }

        // Creamos un ImageView por cada imagen miniatura referenciada en nuestro ImageAdapter
        public View getView(final int position, View convertView, ViewGroup parent) {
            ImageView imageView;
            if (convertView == null) {  // if it's not recycled, initialize some attributes
                imageView = new ImageView(mContext);
                imageView.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 130));
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                    imageView.setCropToPadding(true);
                } else {
                    imageView.setScaleType(ImageView.ScaleType.FIT_XY);
                }
                imageView.setPadding(2, 2, 2, 2);
            } else {
                imageView = (ImageView) convertView;
            }
            //Asignamos a cada ImageView una imagen de nuestro "Array" de recursos
            //utilizamos "setImageResource" ya que nuestras imagenes estan almacenadas en una
            //carpeta de recursos en nuestro proyecto.
            // "mImagesIds" es un Array de enteros, ya que almacena el Id del recurso, no la imagen en si.
            imageView.setImageResource(mImagesIds[position]);

            //En el evento click del ImageView obtenemos el indice de la imagen seleccionada
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mSelected = (Integer) view.getTag();
                    notifyDataSetChanged();

                    String index = String.valueOf(position);
                    Bundle extras = new Bundle();

                    //Pasamos el indice de la imagen seleccionada a la activity que se encarga de mostrar la image Original
                    //y hacer el "swipe"
                    extras.putString("position", index);
                    startActivity(new Intent(ShowGalleryActivity.this, SwipeImageActivity.class).putExtras(extras).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
                }
            });
            try {
                imageView.setTag(position);
                //Ponemos un borde de color naranja a la imagen en miniatura seleccionada en el GrisView
                if (position == mSelected) {
                    imageView.setBackgroundColor(Color.parseColor("#ff6203"));
                } else {
                    imageView.setBackgroundColor(Color.TRANSPARENT);
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return imageView;
        }
    }
}


5.- A continuación creamos un layout llamado swipe_images_layout que servirá de contenedor de la colección de imágenes


    

6.- Creamos otro layout con la estructura de cada elemento de la colección de imágenes.


    

    

        
    

    

        
    


Nota: Aquí usamos el componente <android.support.v4.view.ViewPager> por esta razón, debemos copiar en la carpeta lib de nuestro proyecto la librearía: android-support-v4.jar  

6.- Creamos una activity llamada SwipeImageActivity. A continuación les dejo el código con los comentarios suficientes para que se pueda entender.

public class SwipeImageActivity extends Activity {

    //Hacemos referencia a nuestras imagenes originales de la carpeta de recursos
    public static Integer[] mImagesIds = {
            R.drawable.i1, R.drawable.i2,
            R.drawable.i3, R.drawable.i4,
            R.drawable.i5, R.drawable.i6,
            R.drawable.i7, R.drawable.i8,
            R.drawable.i9, R.drawable.i10,
            R.drawable.i11, R.drawable.i12,
            R.drawable.i13, R.drawable.i14,
            R.drawable.i15, R.drawable.i16,
            R.drawable.i17, R.drawable.i18,
            R.drawable.i10, R.drawable.i20,
            R.drawable.i21, R.drawable.i22,
            R.drawable.i23, R.drawable.i24,
            R.drawable.i25, R.drawable.i26,
            R.drawable.i27, R.drawable.i28,
            R.drawable.i29, R.drawable.i30,
            R.drawable.i31
    };

    private boolean hideSwipeText;
    private String[] imagesDescriptions;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.swipe_images_layout);

        //Optenenos del GridView de la activity anterior el indice de la imagen que hemos
        //seleccionado
        String i = getIntent().getStringExtra("position");
        //Lo convertimos a entero
        int index = Integer.parseInt(i);

        //Obtenemos la decripcion de la imagen seleccionada de una lista en la carpeta values\arrays.xml
        imagesDescriptions = getResources().getStringArray(R.array.images_descriptions);

        SwipeImagePagerAdapter swipeNewsAdapter = new SwipeImagePagerAdapter();
        ViewPager newsPager = (ViewPager) findViewById(R.id.swipe_pager);
        newsPager.setAdapter(swipeNewsAdapter);
        newsPager.setCurrentItem(index);

        newsPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int i, float v, int i2) {
                ShowGalleryActivity.mSelected = i;
            }

            @Override
            public void onPageSelected(int i) {

            }

            @Override
            public void onPageScrollStateChanged(int i) {

            }
        });
    }

    //Creamos el SwipeImagePagerAdapter. Donde utilizaremos el layout "show_images"
    //para cargar la imagen original y los textos de descripcion de la imagen
    private class SwipeImagePagerAdapter extends PagerAdapter {
        @Override
        public int getCount() {
            return ShowGalleryActivity.mImagesIds.length;
        }
        /**
         * Create the page for the given position.  The adapter is responsible
         * for adding the view to the container given here, although it only
         * must ensure this is done by the time it returns from
         * {@link #finishUpdate(android.view.ViewGroup)}.
         *
         * @param collection The containing View in which the page will be shown.
         * @param position   The page position to be instantiated.
         * @return Returns an Object representing the new page.  This does not
         * need to be a View, but can be some other container of the page.
         */
        @Override
        public Object instantiateItem(ViewGroup collection, int position) {

            LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            //Refereciamos el show_images layout
            View view = inflater.inflate(R.layout.show_images, null);
            LinearLayout swipeDescription = (LinearLayout) view.findViewById(R.id.swipe_description);

            if (hideSwipeText) {
                swipeDescription.setVisibility(View.GONE);
            }

            hideSwipeText = true;

            //Refereciamos el objeto ImageView del "show_images" layout
            ImageView imageView = (ImageView) view.findViewById(R.id.gallery_image);

            //Asignamos a cada ImageView una imagen de nuestro "Array" de recursos
            //utilizamos "setImageResource" ya que nuestras imagenes estan almacenadas en una
            //carpeta de recursos en nuestro proyecto.
            // "mImagesIds" es un Array de enteros, ya que almacena el Id del recurso, no la imagen en si.
            imageView.setImageResource(mImagesIds[position]);

            //Refereciamos el objeto TextView del "show_images" layout, para colocar la descripcion de la imagen
            TextView imageDescription = (TextView) view.findViewById(R.id.image_description);

            //Asignamos el test descriptivo de la imagen al objecto TextView.
            imageDescription.setText(imagesDescriptions[position].toString());

            //Adicionamos el "view" que hemos creado con los objectos ImageView y TextView a la coleccion ViewGroup
            collection.addView(view, 0);

            return view;
        }

        /**
         * Remove a page for the given position.  The adapter is responsible
         * for removing the view from its container, although it only must ensure
         * this is done by the time it returns from {@link #finishUpdate(android.view.ViewGroup)}.
         *
         * @param collection The containing View from which the page will be removed.
         * @param position   The page position to be removed.
         * @param view       The same object that was returned by
         *                   {@link #instantiateItem(android.view.View, int)}.
         */
        @Override
        public void destroyItem(ViewGroup collection, int position, Object view) {
            collection.removeView((View) view);
        }
        /**
         * Determines whether a page View is associated with a specific key object
         * as returned by instantiateItem(ViewGroup, int). This method is required
         * for a PagerAdapter to function properly.
         *
         * @param view   Page View to check for association with object
         * @param object Object to check for association with view
         * @return
         */
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return (view == object);
        }
        /**
         * Called when the a change in the shown pages has been completed.  At this
         * point you must ensure that all of the pages have actually been added or
         * removed from the container as appropriate.
         *
         * @param arg0 The containing View which is displaying this adapter's
         *             page views.
         */
        @Override
        public void finishUpdate(ViewGroup arg0) {
        }

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {
        }
        @Override
        public Parcelable saveState() {
            return null;
        }
        @Override
        public void startUpdate(ViewGroup arg0) {
        }
    }
}

Nota: Las descripciones de las imagenes se encuentran en la carpeta res\values\arrays.xml

7. -Por ultimo modificaremos nuestro Main Activity para hacer el llamado nuestra galería de imágenes:

El layout associado a la Main Activity (activity_main) queda de la siguiente manera:


    

En la clase MainActivity, capturamos el evento del click del botón, para así mostrar la galería:


public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button showGallery = (Button) findViewById(R.id.button);

        showGallery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, ShowGalleryActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        return true;
    } 
}
Así debería quedar nuestro proyecto:

Y el resultado seria el siguiente:

Y después de seleccionar una imagen:

Puede descargar el código fuente en: ImageGallerySource

miércoles, 14 de agosto de 2013

uCatalog. Universal Catalog. Ahora también en idioma Ruso.



uCatalog.  Universal Catalog. Ahora también en idioma Ruso.

Универсальный Каталог теперь на русском языке