With the release of Android Lollipop, several new support libraries have been created. One of the new libraries is for the Palette
class. This new class makes it easy to extract prominent colors from bitmap images, which is useful if you want to style other view components to match colors from your image, such as a background for the image or a text color with suitable contrast. One way I like to use this is to color the ripple drawable behind the image. It is a subtle effect, but I think it is a nice improvement over the standard grey.
For example, you can use a palette to create a color-coordinated title card for a song based on its album cover or to adjust an app’s toolbar color when its background image changes.
Palette
object gives you access to the colors in a Bitmap
image while also providing six main color profiles from the bitmap to help inform your design choices.
In this tutorial, we are going to learn how to Select Colors with the Palette API and using Picasso to load images.
1. Create a new project in Android Studio by navigating to File ⇒ New ⇒ New Project and fill required details. By default my activity is MainActivity.java.
2. Open res ⇒ values ⇒ strings.xml and add below string values. These are some strings that we are going to use in our project.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<resources> <string name="app_name">Android Palette Demo</string> <string name="vibrant">Vibrant</string> <string name="vibrant_light">Vibrant Light</string> <string name="vibrant_dark">Vibrant Dark</string> <string name="muted">Muted</string> <string name="muted_light">Muted Light</string> <string name="muted_dark">Muted Dark</string> <string name="grid_activity_label">Grid Activity</string> <string name="second_image">Second Image</string> <string name="first_image">First Image</string> <string name="grid_palette_example">Grid Palette Example</string> </resources> |
3. Before you can use Palette in your projects you need to add the following compile line to your Gradle dependencies block in your build.gradle
file.
1 |
compile 'com.android.support:palette-v7:25.3.0' |
As i am using Picasso library also for loading image url and RecyclerView for Grid representation, so the build.gradle will change as per below:
1 2 3 |
compile 'com.android.support:palette-v7:25.3.0' compile 'com.android.support:recyclerview-v7:25.3.0' compile 'com.squareup.picasso:picasso:2.3.2' |
4. Create activity_main.xml layout and place the below code. This layout will contain the displayed image and TextViews to display the extracted colors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" tools:context="com.androidpalette_demo.MainActivity"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="gridPaletteExample" android:text="@string/grid_palette_example" android:textColor="@android:color/white" android:textSize="14sp" /> <!-- Radio Group to toggle between Images --> <RadioGroup android:id="@+id/radio_group" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="5dp"> <RadioButton android:id="@+id/first_image_radio" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" android:checked="true" android:gravity="center_vertical" android:text="@string/first_image" android:textColor="@android:color/white" android:textSize="14sp" /> <RadioButton android:id="@+id/second_image_radio" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" android:gravity="center_vertical" android:text="@string/second_image" android:textColor="@android:color/white" android:textSize="14sp" /> </RadioGroup> <!-- ImageView to display selected Image --> <ImageView android:id="@+id/main_image_view" android:layout_width="match_parent" android:layout_height="180dp" android:layout_gravity="center" android:layout_margin="5dp" android:scaleType="fitCenter" /> <!-- TextView Labels to display different Palette colors background --> <TextView android:id="@+id/vibrantView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="10dp" android:text="@string/vibrant" android:textColor="@android:color/white" android:textSize="14sp" /> <TextView android:id="@+id/vibrantLightView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="10dp" android:text="@string/vibrant_light" android:textColor="@android:color/white" android:textSize="14sp" /> <TextView android:id="@+id/vibrantDarkView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="10dp" android:text="@string/vibrant_dark" android:textColor="@android:color/white" android:textSize="14sp" /> <TextView android:id="@+id/mutedView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="10dp" android:text="@string/muted" android:textColor="@android:color/white" android:textSize="14sp" /> <TextView android:id="@+id/mutedLightView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="10dp" android:text="@string/muted_light" android:textColor="@android:color/white" android:textSize="14sp" /> <TextView android:id="@+id/mutedDarkView" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:padding="10dp" android:text="@string/muted_dark" android:textColor="@android:color/white" android:textSize="14sp" /> </LinearLayout> |
5. After creating layout xml let’s move to generating Palette process:
In order to generate the Palette
object, there is a static from(Bitmap)
method on the Palette
class. This method returns a Palette.Builder
object that you can use to tweak the generated Palette
. Be sure that the Bitmap
you supply to the from()
method is neither null nor recycled, or it will throw an IllegalArgumentException
.
Once you have the builder, you can use one of the generate methods to create thePalette
object. One option is to use the no-argument generate()
method like so.
Generate a Palette
instance using Palette
’s from(Bitmap bitmap)
method to first create a Palette.Builder
from a Bitmap
. The builder can then generate the palette either synchronously or asynchronously.
Use synchronous palette generation if you want to create the palette on the same thread as the method being called. If you generate the palette asynchronously on a different thread, use the onGenerated()
method to access the palette immediately after it has been created.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Generate palette synchronously and return it public Palette createPaletteSync(Bitmap bitmap) { Palette p = Palette.from(bitmap).generate(); return p; } // Generate palette asynchronously and use it on a different // thread using onGenerated() public void createPaletteAsync(Bitmap bitmap) { Palette.from(bitmap).generate(new PaletteAsyncListener() { public void onGenerated(Palette p) { // Use generated instance } }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* Generate palette asynchronously and use it on a different thread using onGenerated() */ private void createPaletteAsync(Bitmap bitmap) { Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { // Use generated instance //work with the palette here int defaultValue = 0x000000; int vibrant = palette.getVibrantColor(defaultValue); int vibrantLight = palette.getLightVibrantColor(defaultValue); int vibrantDark = palette.getDarkVibrantColor(defaultValue); int muted = palette.getMutedColor(defaultValue); int mutedLight = palette.getLightMutedColor(defaultValue); int mutedDark = palette.getDarkMutedColor(defaultValue); } }); } |
When a palette is generated, it tries to pick six swatches which match certain criteria:
Which one you choose depends on your use case. Vibrant and Dark Vibrant are the ones that developers will use mostly though.
Each Swatch contains the following methods:
The Palette.Builder
allows you to customize your palette by choosing how many colors are in the resulting palette, what area of your image the builder uses to generate the palette, and what colors are allowed in the palette. For example, you can filter out the color black or ensure that the builder only uses the top half of an image to generate your palette.
Fine-tune your palette’s size and colors with the following methods from the Palette.Builder
class:
Palette.Filter
and modify its isAllowed()
method to determine which colors are filtered from the palette.Palette.Builder
takes longer to generate palettes with more colors.Target
color profile to the builder. If the default Target
s are not sufficient, advanced developers can create their own Target
s using a Target.Builder
.But what if you do not like Palette’s color selections? In this case, you can grab all of the swatches which make up the Palette as so:
1 |
List<Palette.Swatch> swatches = palette.getSwatches(); |
You can then iterate over them and choose whichever color you want.
The following code uses the methods to synchronously generate a palette, get its vibrant swatch, and change the colors of a toolbar to match the bitmap image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* Set the background and text colors of a toolbar given a bitmap image to match */ public void setToolbarColor(Bitmap bitmap) { // Generate the palette and get the vibrant swatch // See the createPaletteSync() and checkVibrantSwatch() methods // from the code snippets above Palette p = createPaletteSync(bitmap); Palette.Swatch vibrantSwatch = checkVibrantSwatch(p); // Set the toolbar background and text colors Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); toolbar.setBackgroundColor(vibrantSwatch.getRgb()); toolbar.setTitleTextColor(vibrantSwatch.getTitleTextColor()); } |
Below are the snapshots with two different images displaying extracted Palette colors and also with changed toolbar background color and title color.
6. Now open your MainActivity.java and place the below code. In this class we are using all the above steps that we learnt in above point.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
package com.androidpalette_demo; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.graphics.Palette; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.ImageView; import android.widget.RadioGroup; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView vibrantView, vibrantLightView, vibrantDarkView, mutedView, mutedLightView, mutedDarkView; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); implementRadioGroupListener(); //Initially load first image toggleImages(R.drawable.photo); } /* Init all Views */ private void initViews() { imageView = (ImageView) findViewById(R.id.main_image_view); vibrantView = (TextView) findViewById(R.id.vibrantView); vibrantLightView = (TextView) findViewById(R.id.vibrantLightView); vibrantDarkView = (TextView) findViewById(R.id.vibrantDarkView); mutedView = (TextView) findViewById(R.id.mutedView); mutedLightView = (TextView) findViewById(R.id.mutedLightView); mutedDarkView = (TextView) findViewById(R.id.mutedDarkView); } /* Implement Check listener over Radio group */ private void implementRadioGroupListener() { final RadioGroup radioGroup = (RadioGroup) findViewById(R.id.radio_group); radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { // Finding ID/Position of radio group child int pos = radioGroup.indexOfChild(findViewById(checkedId)); //Toggle between images switch (pos) { case 0: toggleImages(R.drawable.photo); break; case 1: toggleImages(R.drawable.download); break; } } }); } /* Toggle method to switch between images */ private void toggleImages(int drawableIcon) { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), drawableIcon);//Get bitmap from drawable resources imageView.setImageBitmap(bitmap);//set bitmap over image view setToolbarColor(bitmap);//set toolbar color according bitmap createPaletteAsync(bitmap);//generate palette asynchronously } /* Set the background and text colors of a toolbar given a bitmap image to match */ public void setToolbarColor(Bitmap bitmap) { // Generate the palette and get the vibrant swatch // See the createPaletteSync() and checkVibrantSwatch() methods // from the code snippets above Palette p = createPaletteSync(bitmap); Palette.Swatch vibrantSwatch = checkVibrantSwatch(p); // Set the toolbar background and text colors Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); toolbar.setBackgroundColor(vibrantSwatch.getRgb()); toolbar.setTitleTextColor(vibrantSwatch.getTitleTextColor()); } // Generate palette synchronously and return it public Palette createPaletteSync(Bitmap bitmap) { return Palette.from(bitmap).generate(); } // Return a palette's vibrant swatch after checking that it exists private Palette.Swatch checkVibrantSwatch(Palette p) { Palette.Swatch vibrant = p.getVibrantSwatch(); if (vibrant != null) { return vibrant; } return null; } /* Generate palette asynchronously and use it on a different thread using onGenerated() */ private void createPaletteAsync(Bitmap bitmap) { Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { // Use generated instance //work with the palette here int defaultValue = 0x000000; int vibrant = palette.getVibrantColor(defaultValue); int vibrantLight = palette.getLightVibrantColor(defaultValue); int vibrantDark = palette.getDarkVibrantColor(defaultValue); int muted = palette.getMutedColor(defaultValue); int mutedLight = palette.getLightMutedColor(defaultValue); int mutedDark = palette.getDarkMutedColor(defaultValue); vibrantView.setBackgroundColor(vibrant); vibrantLightView.setBackgroundColor(vibrantLight); vibrantDarkView.setBackgroundColor(vibrantDark); mutedView.setBackgroundColor(muted); mutedLightView.setBackgroundColor(mutedLight); mutedDarkView.setBackgroundColor(mutedDark); } }); } /* Open Grid Activity */ public void gridPaletteExample(View view) { startActivity(new Intent(this, RecyclerView_Activity.class)); } } |
7. As we will also work in RecyclerView and Picasso. So lets create new xml layout naming recycler_view_activity.xml and place the below code to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.Toolbar android:id="@+id/recycler_view_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" /> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="5dp" /> </LinearLayout> |
8. After creating recycler_view_activity layout, now create custom row layout for recycler view item naming recycler_view_custom_row.xml and place the below code to it. In this layout we are only taking ImageView and TextView to display the Palette colors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/background_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="3dp" android:orientation="vertical"> <ImageView android:id="@+id/custom_image_view" android:layout_width="match_parent" android:layout_height="120dp" android:scaleType="fitCenter" /> <TextView android:id="@+id/text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:gravity="center" android:padding="5dp" android:textSize="15sp" /> </LinearLayout> |
9. Now create POJO class for RecyclerView custom layout naming RecyclerItemModel.java and place the below code to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.androidpalette_demo; /** * Created by sonu on 27/03/17. */ /* Model for Grid Item */ public class RecyclerItemModel { private String label, imageUrl; public RecyclerItemModel(String label, String imageUrl) { this.label = label; this.imageUrl = imageUrl; } public String getLabel() { return label; } public String getImageUrl() { return imageUrl; } } |
10. Now create new activity class naming RecyclerView_Activity.java and place the below code to it. In this class we are taking some image urls and adding them to ArrayList.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
package com.androidpalette_demo; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import java.util.ArrayList; /** * Created by sonu on 27/03/17. */ public class RecyclerView_Activity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.recycler_view_activity); setUpToolbar(); setUpRecyclerView(); } /* Set Up toolbar */ private void setUpToolbar() { Toolbar toolbar = (Toolbar) findViewById(R.id.recycler_view_toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } /* Set Up Recycler View */ private void setUpRecyclerView() { RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2); recyclerView.setLayoutManager(gridLayoutManager); getRecyclerViewArrayList(recyclerView); } /* generate array list and set adapter over recycler view */ private void getRecyclerViewArrayList(RecyclerView recyclerView) { ArrayList<RecyclerItemModel> recyclerItemModels = new ArrayList<>(); recyclerItemModels.add(new RecyclerItemModel("Google Chrome", "https://upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Google_Chrome_for_Android_Icon_2016.svg/2000px-Google_Chrome_for_Android_Icon_2016.svg.png")); recyclerItemModels.add(new RecyclerItemModel("Google Now", "https://cdn.pixabay.com/photo/2015/10/31/12/56/google-1015752_960_720.png")); recyclerItemModels.add(new RecyclerItemModel("Google Plus", "https://upload.wikimedia.org/wikipedia/commons/f/fb/Google-plus-circle-icon-png.png")); recyclerItemModels.add(new RecyclerItemModel("Google Photos", "https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/Google-Picasa-icon-logo.png/1016px-Google-Picasa-icon-logo.png")); recyclerItemModels.add(new RecyclerItemModel("Google Maps", "http://maxpixel.freegreatpicture.com/static/photo/1x/Maps-Gps-Map-Icon-Google-Maps-Logo-Navigation-1797882.png")); recyclerItemModels.add(new RecyclerItemModel("Google Play Music", "https://upload.wikimedia.org/wikipedia/commons/thumb/7/74/Play_music_triangle.svg/2000px-Play_music_triangle.svg.png")); recyclerItemModels.add(new RecyclerItemModel("Google Play Store", "https://cdn.pixabay.com/photo/2016/08/31/00/49/google-1632434_960_720.png")); recyclerItemModels.add(new RecyclerItemModel("Google Drive", "https://upload.wikimedia.org/wikipedia/commons/thumb/7/75/Google_Drive_Logo.svg/1804px-Google_Drive_Logo.svg.png")); recyclerItemModels.add(new RecyclerItemModel("Google Allo", "https://upload.wikimedia.org/wikipedia/commons/d/d0/Google_Allo_Logo.png")); recyclerItemModels.add(new RecyclerItemModel("Google Contacts", "https://upload.wikimedia.org/wikipedia/commons/b/b7/Google_Contacts_logo.png")); RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, recyclerItemModels); recyclerView.setAdapter(adapter); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { //Finish activity on home back button click case android.R.id.home: finish(); break; } return super.onOptionsItemSelected(item); } } |
11. Finally create adapter for RecyclerView naming RecyclerViewAdapter.java and place the below code to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
package com.androidpalette_demo; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.support.v7.graphics.Palette; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; import java.util.ArrayList; /** * Created by sonu on 27/03/17. */ public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static class DemoViewHolder extends RecyclerView.ViewHolder { private ImageView imageView; private TextView textView; DemoViewHolder(View itemView) { super(itemView); imageView = (ImageView) itemView.findViewById(R.id.custom_image_view); textView = (TextView) itemView.findViewById(R.id.text_view); } } private ArrayList<RecyclerItemModel> recyclerItemModelArrayList; private Context context; public RecyclerViewAdapter(Context context, ArrayList<RecyclerItemModel> recyclerItemModelArrayList) { this.recyclerItemModelArrayList = recyclerItemModelArrayList; this.context = context; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_custom_row, parent, false); return new DemoViewHolder(v); } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { RecyclerItemModel model = recyclerItemModelArrayList.get(position); if (holder instanceof DemoViewHolder) { ((DemoViewHolder) holder).textView.setText(model.getLabel());//set text label //Use picasso or any other image library to load image url Picasso.with(context) .load(model.getImageUrl()) .resize(300, 300)//use resize if yoou are using centreCrop .centerCrop() .into(new Target() { @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { //When bitmap loaded successfully display bitmap over image view and generate palette ((DemoViewHolder) holder).imageView.setImageBitmap(bitmap); Palette.from(bitmap) .generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { Palette.Swatch textSwatch = palette.getVibrantSwatch(); //Check if swatch is null or not if (textSwatch == null) { //If null display toast Toast.makeText(context, "Got Null swatch !!", Toast.LENGTH_SHORT).show(); return; } ((DemoViewHolder) holder).textView.setTextColor(textSwatch.getTitleTextColor());//set title text color ((DemoViewHolder) holder).textView.setBackgroundColor(textSwatch.getRgb());//set text background color or root background color // textSwatch.getBodyTextColor(); //Set the body text color if you need } }); } @Override public void onBitmapFailed(Drawable errorDrawable) { } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { } }); } } @Override public int getItemCount() { return recyclerItemModelArrayList.size(); } } |
Below are the process how Palette Generation work with Picasso or any other library:
The default code of Picasso to load images is:
1 |
Picasso.with(context).load(image_url).into(imageView); |
The problem in above code is that we have no access to the loaded image. The library downloads the image, resize it and define sets it into the ImageView in a totally automated way. To allow us to handle the downloaded image and extract it’s colors, lets change some lines of code. Instead of loading the image into the ImageView, we will load it into a custom target. This way, we will have access to the bitmap, making easier to extract it’s colors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Picasso.with(context) .load(model.getImageUrl()) .into(new Target() { @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { } @Override public void onBitmapFailed(Drawable errorDrawable) { } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { } }); |
Now we can handle the bitmap inside the method onBitmapLoaded. We can extract it’s colors and then set the bitmap as image source in our ImageView. But not that fast. Changing the target of image loading has another consequence: Picasso now has no dimensions to resize the image. When the loading target is an ImageView, Picasso can resize the bitmap with it’s dimensions. A custom target has no dimensions, so the bitmap we receive as parameter has the original size. For that reason, we have some more lines of code. We must call resize and centerCrop to reduce the image before processing it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Picasso.with(MainActivity.this) .load(model.getImageUrl()) .resize(300,300)//or any other dimension of your choice .centerCrop() .into(new Target() { @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { } @Override public void onBitmapFailed(Drawable errorDrawable) { } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { } }); |
Finally we can extract images’s colors. We can create a new instance from Palette class using the method from, using our bitmap as parameter. A new color set will be generated with the method generate(). This process can take some time, so it will be executed in an asynchronous way, avoiding main thread blocks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { //When bitmap loaded successfully display bitmap over image view and generate palette ((DemoViewHolder) holder).imageView.setImageBitmap(bitmap); Palette.from(bitmap) .generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { Palette.Swatch textSwatch = palette.getVibrantSwatch(); //Check if swatch is null or not if (textSwatch == null) { //If null display toast Toast.makeText(context, "Got Null swatch !!", Toast.LENGTH_SHORT).show(); return; } ((DemoViewHolder) holder).textView.setTextColor(textSwatch.getTitleTextColor());//set title text color ((DemoViewHolder) holder).textView.setBackgroundColor(textSwatch.getRgb());//set text background color or root background color // textSwatch.getBodyTextColor(); //Set the body text color if you need } }); } |
When the process finishes, we will have a palette with multiple combinations. After Palette generations we get all type of colors as we got in MainActivity.java use getTitleTextColor() to get text color and getRgb() for text background color. Don’t forget that the sets can be null, since an image may have no vibrant colors,
Below is a screenshot of what we got when we open RecyclerView_Activity.java.
12. Make sure you give INTERNET permission in AndroidManifest.xml file to load image urls.
1 |
<uses-permission android:name="android.permission.INTERNET" /> |
13. Finally you are all done. Now you can also use the Palette in your app.
Thanks.
Subscribe to us and get the latest news.