Making an android colour picker application – Part 1

Making an android colour picker application – Part 1
Photo by Magda Ehlers from Pexels

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

Images are picked from gallery or camera for colour extraction using the palette library, then the extracted colours will be listed in 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.

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
apply plugin: 'com.android.application'
apply plugin: 'androidx.navigation.safeargs'

android {
    compileSdkVersion 28
    buildToolsVersion = '28.0.3'

    defaultConfig {
        applicationId "com.garageprojects.colorme"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.palette:palette:1.0.0'

    implementation 'com.github.nguyenhoanglam:ImagePicker:1.3.3'

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

    implementation 'com.jakewharton:butterknife:10.1.0'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'


    implementation 'androidx.navigation:navigation-fragment:2.1.0'
    implementation 'androidx.navigation:navigation-ui:2.1.0'


    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

}

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 ImageViewerFragment and colour extraction.

Looking forward to your comments, suggestions and corrections.

Leave a Reply

Close Menu