Cloudzilla Logo

Extracting Colors from Images using the Palette API in Android

User Interface (UI) is very important when developing Android apps. Many developers tend to neglect this aspect. A great UI gives the user a great experience. Color selection in an app is very crucial in making it successful.

In this tutorial, we will extract colors to use in our app from an image.

Prerequisites

To follow along with this tutorial, the reader should:

  • Have Android Studio installed and know how to create Android projects.
  • Have a good understanding of the Kotlin programming language.
  • Have a good understanding of XML.
  • Be able to use ViewBinding.
  • Have a basic knowledge on Android Toolbar.

Goals

By the end of this tutorial, the reader should:

  • Have an understanding of what Palette API is.
  • Know how to set up the Palette API library.
  • Know how to extract colors from an image using the Palette API.

What is Palette?

Palette is a support library in Android. It extracts prominent colors from Bitmap images. We can use it in styling view components in the app.

The views matche the prominent color from the image. For instance, the toolbar, background, or even text color.

Advantages of Using Palette API

  • It provides a helper class to extract prominent colors from an image.
  • We can use colors obtained to make elegant application UI designs.
  • We can customize the color Palette using in-build methods. For instance, adding filters and much more.

Step 1. Create a new Android project

Launch Android Studio, select New Project then Empty Activity. Name it Palette Demo. Click finish and wait for it to build.

Create Project

Step 2: Set up the Palette library

Add the following dependency in the app-module level build.gradle file.

implementation("com.android.support:palette-v7:28.0.0")

Step 3: Set up the layout for our project

In this step, we will design the UI. This will contain a ToolBar, an ImageView, a Button, and TextViews.

ActivityMain.xml file

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <com.google.android.material.appbar.AppBarLayout

       android:id="@+id/appBarLayout"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:theme="@style/Theme.MyApplication.AppBarOverlay"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent">
       
       <androidx.appcompat.widget.Toolbar
           android:id="@+id/toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           android:background="?attr/colorPrimary"
           app:popupTheme="@style/Theme.MyApplication.PopupOverlay" />
   </com.google.android.material.appbar.AppBarLayout>

   <ImageView
       android:id="@+id/imageView"
       android:layout_width="match_parent"
       android:layout_height="250dp"
       android:src="@drawable/index"
       app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
       tools:layout_editor_absoluteX="26dp" />

   <Button
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Change Toolbar Color"
       android:textSize="18sp"
       android:layout_marginTop="8dp"
       android:textAllCaps="false"
       android:id="@+id/change_toolbar_color_btn"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/imageView"/>

   <TextView
       android:id="@+id/lightVibrant"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:gravity="center"
       android:text="Light Vibrant"
       android:textAllCaps="false"
       android:textSize="18sp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/change_toolbar_color_btn" />

   <TextView
       android:id="@+id/vibrant"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:gravity="center"
       android:text="Vibrant"
       android:textAllCaps="false"
       android:textSize="18sp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/lightVibrant" />

   <TextView
       android:id="@+id/lightMuted"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:gravity="center"
       android:text="Light Muted"
       android:textAllCaps="false"
       android:textSize="18sp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/vibrant" />

   <TextView
       android:id="@+id/muted"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:gravity="center"
       android:text="Muted"
       android:textSize="18sp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/lightMuted" />

   <TextView
       android:id="@+id/darkMuted"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:gravity="center"
       android:text="Dark Muted"
       android:textAllCaps="false"
       android:textSize="18sp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/muted" />

   <TextView
       android:id="@+id/darkVibrant"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="8dp"
       android:gravity="center"
       android:text="Dark Vibrant"
       android:textAllCaps="false"
       android:textSize="18sp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/darkMuted" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 4: Create a Palette object

A palette object allows us to access the prominent colors in an image bitmap. We use palettes to style our application by changing the application's color scheme based on the image bitmap.

To create a palette, we generate an instance using from(bitmap: Bitmap) method. This creates a Builder from a bitmap.

The builder either generates synchronous or asynchronous palettes. To create a palette on the same thread as the method we are invoking, we use synchronous palette .To create a palette on another thread, we use asynchronous palette.

TheonGenerated() method is used to access the palette.

To create a synchronous palette, we use:

// Synchronous Palette generated and returned
fun createPaletteSync(bitmap: Bitmap): Palette = Palette.from(bitmap).generate()

To create an asynchronous palette, we use:

// Palette created asynchronously. We use it on another thread using onGenerated() method
fun createPaletteAsync(bitmap: Bitmap) {
   Palette.from(bitmap).generate { palette ->
       // Use the generated instance
   }
}

To generate a palette, I would suggest using asynchronous technique. Synchronous generation may not create a smooth experience. This is evident on older devices or when the Bitmap object is relatively large.

Step 5: Extracting color profiles

A Target defines each color extracted. We score the colors against the profile. This is done based on the saturation and the number of pixels in the bitmap image.

The palette extracts the following six color profiles using the respective methods:

  • Light Vibrant : Palette.getLightVibrantSwatch()
  • Dark Vibrant: Palette.getDarkVibrantSwatch()
  • Vibrant: Palette.getVibrantSwatch()
  • Light Muted: Palette.getLightMutedSwatch()
  • Dark Muted: Palette.getDarkMutedSwatch()
  • Muted: Palette.getMutedSwatch()

We are going to use swatches to get colors from the bitmap image. We use Palette.Swatch object to get each color profile. The palette has other methods for accessing more information about the color profiles.

They include:

  • getPopulation gets the amount of pixels represented by this swatch.
  • getRgb gets the color RGB value.
  • getBodyTextColor and getTitleTextColor get text color RGB value for use over the swatch’s color.

The get<Profile>Swatch method usually needs no parameter. But, it may return null if a particular profile is not present in the bitmap image. Before accessing a swatch, first, check if it is null or not to prevent your app from crashing.

The following code checks if the swatch is present in the image bitmap. Otherwise, the default background color is set to Gray.

if(palette?.lightMutedSwatch != null){
   setBackgroundColor(palette?.lightMutedSwatch!!.rgb
}
else{
    setBackgroundColor(Color.GRAY)
}

Here is the main activity's code:

ActivityMain.kt file

// Press Alt + Enter to import the libraries

class MainActivity : AppCompatActivity() {

   private lateinit var binding: ActivityMainBinding
   private val TAG = "MainActivity"
   
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       binding = ActivityMainBinding.inflate(layoutInflater)
       setContentView(binding.root)

       setSupportActionBar(binding.toolbar)
       createPaletteAsync((ContextCompat.getDrawable(this,R.drawable.index) as BitmapDrawable).bitmap)
   }

   private fun createPaletteAsync(bitmap: Bitmap) {
       Palette.from(bitmap).generate(){ palette ->
           // Change toolbar background color
           binding.changeToolbarColorBtn.setOnClickListener {
               binding.toolbar.setBackgroundColor(palette?.vibrantSwatch!!.rgb)
           }

           binding.lightVibrant.apply {
               setBackgroundColor(palette?.lightVibrantSwatch!!.rgb)
           }

           binding.vibrant.apply {
               setBackgroundColor(palette?.vibrantSwatch!!.rgb)
           }

           binding.lightMuted.apply {
               if(lightVibrantSwatch != null ){
                   setBackgroundColor(palette?.lightMutedSwatch!!.rgb)
               }
               else{
                   setBackgroundColor(Color.Grey)
               }
           }

           binding.muted.apply {
               setBackgroundColor(palette?.mutedSwatch!!.rgb)
           }   

           binding.darkMuted.apply {
               setBackgroundColor(palette?.darkMutedSwatch!!.rgb)
           }

           binding.darkVibrant.apply {
               setBackgroundColor(palette?.darkVibrantSwatch!!.rgb)
           }
       }
   }
}

Demo Screens

Upon running the app, this is what to expect:

Screen one

Screen two

Conclusion

The Palette library is a powerful tool that we can use to make elegant UI designs in Android apps. It opens doors to infinite possibilities when it comes to the set up of colors and themes.

Use this tool to materialize your application as you continue exploring and learning.

Check out the entire project on this GitHub repository.

Happy coding!


Peer Review Contributions by: Eric Gacoki

Author
Robert Muriithi
Robert is an undergraduate student pursuing Computer Science. He is an Android developer with a speciality in Kotlin and Java. Robert is part of the Google Developers Community in Kibabii University. He is a tech enthusiast and loves bike riding.
More Articles by Author
Related Articles
Cloudzilla is FREE for React and Node.js projects
No Credit Card Required

Cloudzilla is FREE for React and Node.js projects

Deploy GitHub projects across every major cloud in under 3 minutes. No credit card required.
Get Started for Free