No solo una imagen vale más que mil palabras, sino que además son más geniales que un texto monótono. En este artículo, aprenderemos cómo crear pegatinas desde TextView . Esto será útil si está creando una aplicación en la que desea agregar alguna funcionalidad de superposición de texto, por ejemplo, una aplicación de edición de imágenes . Además, podemos ver una muy buena implementación de esto en la aplicación Hike .
¿Como funciona?
El concepto principal que estamos usando aquí es que estamos convirtiendo TextView en Bitmap y guardando ese bitmap en el almacenamiento del teléfono. Ahora, podemos hacer «n» número de manipulaciones en ese TextView, como podemos cambiar el color, la fuente, el tamaño, el estilo, la apariencia, etc. del TextView. Cualquier cambio que hagamos en TextView se reflejará en la etiqueta (mapa de bits) formada. Teniendo esta idea en mente, comencemos a crear la aplicación.
Implementación paso a paso
Paso 1: Crear un nuevo proyecto
Para crear un nuevo proyecto en Android Studio, consulte Cómo crear/iniciar un nuevo proyecto en Android Studio . Tenga en cuenta que seleccione Java como lenguaje de programación.
Paso 2: agregar una dependencia al archivo build.gradle
Usaremos una biblioteca para seleccionar el color en tiempo de ejecución y cambiar el color de TextView en consecuencia. Así que agregue esta dependencia en el archivo build.gradle .
implementación ‘petrov.kristiyan:colorpicker-library:1.1.10’
Paso 3: Agregar archivos de fuentes
Para hacer que nuestra pegatina sea más hermosa, agregaremos alguna fuente a TextView. Para ello necesitaremos los archivos de fuentes en formato .ttf o .otf . Puede descargar cualquier archivo de fuente que desee de Internet. Ahora, una vez que haya descargado los archivos, vaya a la aplicación> res> haga clic derecho> Nuevo> Carpeta> Carpeta de fuentes y cree una nueva carpeta de recursos de Android de la fuente en Android Studio y coloque los archivos de fuentes en esa carpeta.
Usaremos seis archivos de fuente, por ejemplo, y puede descargar estos archivos desde aquí .
Paso 4: trabajar con el archivo activity_main.xml
Vaya al archivo activity_main.xml y consulte el siguiente código. A continuación se muestra el código para el archivo activity_main.xml .
XML
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#0F9D58" tools:context=".MainActivity"> <ImageButton android:id="@+id/changeFont" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:background="#43A047" android:src="@drawable/ic_android_black_24dp" /> <ImageButton android:id="@+id/changeColor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:background="#43A047" android:src="@drawable/ic_android_black_24dp" /> <Button android:id="@+id/convert" style="@style/TextAppearance.AppCompat.Widget.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:background="#7CB342" android:text="Stickerize!" android:textStyle="bold" /> <TextView android:id="@+id/stickerTextview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Geeks for Geeks" android:textAppearance="@style/TextAppearance.AppCompat.Large" android:textColor="@color/white" android:textStyle="bold" /> <EditText android:id="@+id/stickerEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:hint="Enter your text here.." android:textAlignment="center" /> </RelativeLayout>
Paso 5: trabajar con el archivo MainActivity.java
MainActivity.java MainActivity.java
Java
import android.app.ProgressDialog; import android.content.ContentValues; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.res.ResourcesCompat; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import petrov.kristiyan.colorpicker.ColorPicker; public class MainActivity extends AppCompatActivity { TextView textSticker; EditText editTextSticker; ImageButton fontchange; ImageButton colorPickerText; Button createSticker; // this will work as a counter to // change the font of TextView int i = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textSticker = (TextView) findViewById(R.id.stickerTextview); editTextSticker = (EditText) findViewById(R.id.stickerEditText); colorPickerText = (ImageButton) findViewById(R.id.changeColor); fontchange = (ImageButton) findViewById(R.id.changeFont); createSticker = (Button) findViewById(R.id.convert); createSticker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { executeSticker(); } catch (IOException e) { e.printStackTrace(); } } }); // Here we have added a TextWatcher. The onTextChanged() method will change // the text in TextVie as we type, in the EditText. This makes app more interactive. editTextSticker.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { textSticker.setText(charSequence); } @Override public void afterTextChanged(Editable editable) { } }); // Here we have implemented a small logic which changes the font of the TextView // Whenever we click this button. The counter increments by one and reset to zero // when it reaches value 6. fontchange.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { switch (i) { case 0: i = 1; // This is a very important method of this example. // The setTypeFace() method sets the font of the TextView at runtime. textSticker.setTypeface(ResourcesCompat.getFont(MainActivity.this, R.font.summer)); break; case 1: i = 2; textSticker.setTypeface(ResourcesCompat.getFont(MainActivity.this, R.font.angel)); break; case 2: i = 3; textSticker.setTypeface(ResourcesCompat.getFont(MainActivity.this, R.font.cute)); break; case 3: i = 4; textSticker.setTypeface(ResourcesCompat.getFont(MainActivity.this, R.font.mandala)); break; case 4: i = 5; textSticker.setTypeface(ResourcesCompat.getFont(MainActivity.this, R.font.painter)); break; case 5: i = 6; textSticker.setTypeface(ResourcesCompat.getFont(MainActivity.this, R.font.newfont)); break; case 6: i = 0; textSticker.setTypeface(ResourcesCompat.getFont(MainActivity.this, R.font.orange)); break; } } }); colorPickerText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // create an instance of ColorPicker and invoke the ColorPicker dialog onClick. final ColorPicker colorPicker = new ColorPicker(MainActivity.this); colorPicker.setOnFastChooseColorListener(new ColorPicker.OnFastChooseColorListener() { @Override public void setOnFastChooseColorListener(int position, int color) { // get the integer value of color selected from the dialog box and // the color of the TextView. textSticker.setTextColor(color); } @Override public void onCancel() { colorPicker.dismissDialog(); } }) // set the number of color columns // you want to show in dialog. .setColumns(5) // set a default color selected in the dialog .setDefaultColorButton(Color.parseColor("#000000")) .show(); } }); } // This method creates a Bitmap from the TextView // and saves that into the storage private void executeSticker() throws IOException { // Create an OutputStream to write the file in storage OutputStream imageOutStream; // Although the ProgressDialog is not necessary but there may be cases when // it might takes 2-3seconds in creating the bitmap.(This happens only when there is a // large chunk of cache and also when app is running multiple threads) final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this); progressDialog.setMessage("Please wait.."); progressDialog.show(); // All the three methods are discussed later in this article. // destroyDrawingCache(),buildDrawingCache(),getDrawingCache(). textSticker.destroyDrawingCache(); textSticker.buildDrawingCache(); Bitmap textStickerBitmap = textSticker.getDrawingCache(); // From Android 10 onwards using the former method gives error, because // there is a security/privacy update in Android Q which doesn't allow // access to third party files. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // In order to create a new image file in // storage we do the following steps. ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, "gfg.png"); values.put(MediaStore.Images.Media.MIME_TYPE, "image/png"); values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); imageOutStream = getContentResolver().openOutputStream(uri); // this method writes the file in storage. And finally // our sticker has been created and // successfully saved in our storage textStickerBitmap.compress(Bitmap.CompressFormat.PNG, 100, imageOutStream); // close the output stream after use. imageOutStream.close(); progressDialog.dismiss(); // Now, incase you want to use that bitmap(sticker) // at the very moment it is created // we do the following steps. // Open a Inputstream to get the data from file final InputStream imageStream; try { // use the same uri which we previously used // in writing the file, as it contains // the path to that file. imageStream = getContentResolver().openInputStream(uri); final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream); // create a drawable from bitmap. Drawable drawable = new BitmapDrawable(getResources(), selectedImage); // You can do anything with this drawable. // This drawable contains sticker png. } catch (FileNotFoundException e) { Toast.makeText(MainActivity.this, "File not found!!", Toast.LENGTH_SHORT).show(); } // The else condition is executed if the device // Android version is less than Android 10 } else { String imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString(); File image = new File(imagesDir, "gfg.jpg"); imageOutStream = new FileOutputStream(image); textStickerBitmap.compress(Bitmap.CompressFormat.PNG, 100, imageOutStream); imageOutStream.close(); final Uri imageUri = Uri.fromFile(image); final InputStream imageStream; try { imageStream = getContentResolver().openInputStream(imageUri); final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream); Drawable drawable = new BitmapDrawable(getResources(), selectedImage); // You can do anything with this drawable. // This drawable contains sticker png. } catch (FileNotFoundException e) { Toast.makeText(MainActivity.this, "File not found!!", Toast.LENGTH_SHORT).show(); } } // Finally, print a success message. Toast.makeText(this, "Sticker created successfully!!", Toast.LENGTH_SHORT).show(); } }
Salida: ejecutar en dispositivo físico
Nota sobre el escalado automático en el modo de compatibilidad: cuando el escalado automático no está habilitado, este método creará un mapa de bits del mismo tamaño que esta vista. Debe evitar llamar a este método cuando la aceleración de hardware está habilitada. Si no necesita el mapa de bits de caché de dibujo, llamar a este método aumentará el uso de la memoria y hará que la vista se represente en el software una vez, lo que afectará negativamente el rendimiento.
Métodos importantes utilizados en este ejemplo
- buildDrawingCache: fuerza la creación de la memoria caché de dibujo si la memoria caché de dibujo no es válida.
- destroyDrawingCache: libera los recursos utilizados por el caché de dibujo. Si llama a buildDrawingCache() manualmente sin llamar a setDrawingCacheEnabled(true), debe limpiar el caché con este método después.
- getDrawingCache: devuelve el mapa de bits en el que se almacena en caché este dibujo de vista. El mapa de bits devuelto es nulo cuando el almacenamiento en caché está deshabilitado. Si el almacenamiento en caché está habilitado y el caché no está listo, este método lo creará. Hay una función sobrecargada de esto con un parámetro booleano que indica si el mapa de bits generado debe escalarse en función de la densidad actual de la pantalla cuando la aplicación está en modo de compatibilidad. El método de parámetro cero funciona igual que getDrawingCache (falso).
- setDrawingCacheEnabled: activa o desactiva la caché de dibujo. Cuando la caché de dibujo está habilitada, la siguiente llamada a getDrawingCache() o buildDrawingCache() dibujará al espectador en un mapa de bits.
Alcance futuro
- Puede agregar la funcionalidad de aumentar o disminuir el tamaño de TextView en tiempo de ejecución.
- Puede agregar un lienzo a su aplicación, esto le permitirá arrastrar, estirar y rotar TextView, lo que sería más interactivo.
- También puede intentar implementar esto en una aplicación de chat, donde el usuario puede generar una etiqueta a partir del texto escrito.
- Intenta escribir en diferentes idiomas y crea la pegatina.
Notas:
1. No requerimos permisos de lectura y escritura aquí porque las pegatinas o los mapas de bits que estamos guardando en el almacenamiento del teléfono son propiedad de nuestra propia aplicación.
2. A partir de Android 10, se cambió el método para acceder a los archivos desde el almacenamiento del dispositivo. Si usa métodos anteriores, puede obtener resultados absurdos.
3. Si llama a buildDrawingCache() manualmente sin llamar a setDrawingCacheEnabled(true), debe limpiar el caché llamando a destroyDrawingCache() después.