Making an android colour picker app – Part 1

Making an android colour picker app – Part 1
  • Post author:
  • Post comments:0 Comments

In this series of posts I want to show you how to create an android colour picker app. To follow these posts you need some android developing experience particularly java with android studio and using helper libraries like ButterKnife, Glide etc.

Images can be picked from gallery or camera. We will use a helper library called ImagePicker . ImagePicker library not only abstracts the boiler plate code for getting images but also handles the required permissions. After an image is selected we use the Palette APIs to extract colours and display them on a list view.

The app will have an activity which hosts two fragments. Here is how our app is going to look like:

Adding Dependencies

To start us off, we need to add some dependencies libraries that will easy our development. Below are the project’s and app’s build.gradle files respectively showing the added libraries.

    dependencies {
        classpath 'com.android.tools.build:gradle:3.6.2'
        classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
}
dependencies {
    implementation 'androidx.palette:palette:1.0.0'
    
    implementation 'com.github.nguyenhoanglam:ImagePicker:1.3.3'

    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0

    implementation 'com.jakewharton:butterknife:10.2.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    
    implementation 'androidx.navigation:navigation-fragment:2.2.1'
    implementation 'androidx.navigation:navigation-ui:2.2.1
}

Creating Main UI

Here is the activity_main.xml.

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <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/AppTheme.PopupOverlay" />

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

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        app:navGraph="@navigation/navigation_graph" />


</androidx.coordinatorlayout.widget.CoordinatorLayout>

As you may have noticed we are using the Android Jetpack’s Navigation component. Navigation component makes switching between UI components (activities and fragments) easier, by ensuring a consistent and predictable user experience by adhering to a set of navigation principles.

Next is the MainActivity class

import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

}

Navigation Graph

navigation graph is a resource file that contains all of your destinations and actions. The graph represents all of your app’s navigation paths. These paths informs the navigation component on how to switch between the various UI components within an app. In our navigation graph we will have two destinations (MainFragment and ImageViewerFragment) as defined in the navigation graph resource file below:-

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/navigation"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.garageprojects.colorme.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
        <action
            android:id="@+id/to_imageViewer"
            app:destination="@id/imageViewer" />
    </fragment>
    <fragment
        android:id="@+id/imageViewer"
        android:name="com.garageprojects.colorme.ImageViewerFragment"
        android:label="ImageViewer" >
        <argument
            android:name="source"
            app:argType="integer" />
    </fragment>
</navigation>

MainFragment

Below is the MainFragment layout and class code:-

<?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=".MainFragment">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_margin="@dimen/activity_vertical_margin"
        android:src="@drawable/file_image_outline"
        app:layout_constraintBottom_toTopOf="@+id/prompt"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/prompt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/sixteen"
        android:gravity="center"
        android:text="@string/an_image_is_needed_to_generate_a_palette"
        android:textSize="@dimen/text_fourteen"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.55" />


    <com.google.android.material.button.MaterialButton
        android:id="@+id/gallery"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_marginStart="@dimen/thirty_two"
        android:layout_marginTop="@dimen/sixteen"
        android:layout_marginEnd="@dimen/thirty_two"
        android:layout_marginBottom="@dimen/sixteen"
        android:text="@string/open_gallery"
        android:textAllCaps="false"
        android:textColor="@color/white"
        android:textSize="@dimen/text_sixteen"
        app:layout_constraintBottom_toTopOf="@+id/camera"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/guideline" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/camera"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_marginStart="@dimen/thirty_two"
        android:layout_marginTop="@dimen/sixteen"
        android:layout_marginEnd="@dimen/thirty_two"
        android:layout_marginBottom="@dimen/sixteen"
        android:text="@string/take_a_picture"
        android:textAllCaps="false"
        android:textColor="@color/white"
        android:textSize="@dimen/text_sixteen"
        app:layout_constraintBottom_toTopOf="@+id/guideline1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/gallery" />


    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.75" />

</androidx.constraintlayout.widget.ConstraintLayout>

Here is our MainFragment class.

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;

import butterknife.BindView;
import butterknife.ButterKnife;
public class MainFragment extends Fragment {

    @BindView(R.id.imageView)
    ImageView imageView;
    @BindView(R.id.prompt)
    TextView prompt;
    @BindView(R.id.gallery)
    Button gallery;
    @BindView(R.id.camera)
    Button camera;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_main, container, false);
        ButterKnife.bind(this, view);


        gallery.setOnClickListener(v -> navigateToImageViewer(ImageViewerFragment.SOURCE_GALLERY));

        camera.setOnClickListener(v -> navigateToImageViewer(ImageViewerFragment.SOURCE_CAMERA));

        return view;
    }

    private void navigateToImageViewer(int source) {

        MainFragmentDirections.ToImageViewer directions = MainFragmentDirections.toImageViewer(source);
        Navigation.findNavController(getView()).navigate(directions);

    }
}

This covers our first part more to come in the second part, where we will implement the ImageColorFragment, ImageColorAdapter and a utility colour name class.

Looking forward to your comments, suggestions and corrections.

Leave a Reply