Open In App

How to Create Text Stickers in Android?

Improve
Improve
Like Article
Like
Save
Share
Report

Not only a picture is worth a thousand words but also, they are cooler than monotonous text. In this article, we will learn how to create stickers from the TextView. This will be helpful if you are creating an app in which you want to add some text overlay functionality, for example, an image editing app. Also, we can see a very good implementation of this in the Hike App.

Image Source: Hike Android App

How Does it Work?

The main concept we are using here is that we are converting the TextView into Bitmap and saving that bitmap in the phone storage. Now, we can do “n” number of manipulations in that TextView, like we can change the color, font, size, style, the appearance of the, etc of the TextView. Any changes we make in the TextView reflect in the sticker(bitmap) formed. Keeping this idea in mind, let’s begin creating the App. Note that we are going to implement this project using the Java language. 

Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.

Step 2: Adding a dependency to the build.gradle file

We will be using a library for picking up the color at runtime and changing the color of TextView accordingly. So add this dependency in build.gradle file.

implementation ‘petrov.kristiyan:colorpicker-library:1.1.10’

Step 3: Adding font files

To make our sticker more beautiful we will add some font to the TextView. To do so we will require the font files in .ttf or .otf format. You can download any font file you like from the internet. Now once you have downloaded the files, go to the app > res > right-click > New > Folder > Font Folder and create a new Android Resources folder of the font in android studio and place the font files in that folder.

We will be using six font files for example and you can download these files from here.

Step 4: Working with the activity_main.xml file

Go to the activity_main.xml file and refer to the following code. Below is the code for the activity_main.xml file.

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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>


Step 5: Working with the MainActivity.java file

Go to the MainActivity.java file and refer to the following code. Below is the code for the MainActivity.java file. Comments are added inside the code to understand the code in more detail.

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();
    }
}


Output: Run on Physical Device

Note about auto-scaling in compatibility mode: When auto-scaling is not enabled, this method will create a bitmap of the same size as this view. You should avoid calling this method when hardware acceleration is enabled. If you do not need the drawing cache bitmap, calling this method will increase memory usage and cause the view to be rendered in software once, thus negatively impacting performance.

Important Methods used in this Example

  • buildDrawingCache: Forces the drawing cache to be built if the drawing cache is invalid.
  • destroyDrawingCache: Frees the resources used by the drawing cache. If you call buildDrawingCache() manually without calling setDrawingCacheEnabled(true), you should clean up the cache with this method afterward.
  • getDrawingCache: Returns the bitmap in which this view drawing is cached. The returned bitmap is null when caching is disabled. If caching is enabled and the cache is not ready, this method will create it. There is an overloaded function of this with a boolean parameter which indicates whether the generated bitmap should be scaled based on the current density of the screen when the application is in compatibility mode. The zero parameter method works the same as getDrawingCache(false).
  • setDrawingCacheEnabled: Enables or disables the drawing cache. When the drawing cache is enabled, the next call to getDrawingCache() or buildDrawingCache() will draw the viewer in a bitmap.

Future Scope

  1. You can add the functionality of increasing or decreasing the TextView size at runtime.
  2. You can add a canvas to your app, this will allow dragging, stretching, rotating the TextView which would be more interactive.
  3. You can also try to implement this in a Chat App, where the user can generate a sticker from the text typed.
  4. Try typing in different languages and create the sticker.

Notes:

1. We don’t require read write permissions here because the stickers or the bitmaps which we are saving in phone storage are owned by our own app.

2. From Android 10, method for accessing files from device storage has been changed. If you use earlier methods you might get absurd results.

3. If you call buildDrawingCache() manually without calling setDrawingCacheEnabled(true), you should cleanup the cache by calling destroyDrawingCache() afterwards.



Last Updated : 23 Feb, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads