Advertisement
Android SDK

Android SDK: Drawing with Opacity

by

This tutorial demonstrates how to allow users to draw with opacity values. While this post builds on related tutorials published on Mobiletuts+, you can dive straight into this lesson without completing the prior posts. Read on!

The Android platform provides the resources to create drawing functionality using touchscreen interaction. In a prior Mobiletuts+ series on Creating a Drawing App, we worked through the essential features of drawing interaction in Android, including selecting from a color palette and choosing brush sizes. In this tutorial, we will focus on how to enhance a drawing application by adding opacity into app drawing functions. You can complete this tutorial without having completed the related posts, but we will reference some of the prior material throughout.


Final Preview

Here is a preview of the opacity drawing functionality:

Drawing With Opacity

The source code download includes the standalone app we build in this tutorial as well as an enhanced version of the app we built during the drawing series. Here is a preview of it with the additional functionality:

Drawing With Opacity

1. Start Your Application

Step 1

As with the pattern drawing tutorial, we will be glossing over some of the details we explored in the previous drawing series so that we can focus on the opacity drawing functionality. In addition to using opacity levels, we will also be adding a control to the user interface so that the user can select their own opacity level for drawing.

If you completed the drawing app series, you can jump straight to part 2 step 1 now. If you are creating a new app for this tutorial, start a new Android project in Eclipse now. Choose 14 as your minimum API level and select other settings of your choice. When creating the app, you can let Eclipse create a blank Activity and layout.

Add a new class to your app, naming it "DrawingView". Start with the same class content we used for the pattern drawing tutorial:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class DrawingView extends View {

	//drawing path
	private Path drawPath;
	//drawing and canvas paint
	private Paint drawPaint, canvasPaint;
	//initial color
	private int paintColor = 0xFFFF0000;
	//canvas
	private Canvas drawCanvas;
	//canvas bitmap
	private Bitmap canvasBitmap;

	//constructor
	public DrawingView(Context context, AttributeSet attrs){
		super(context, attrs);
		setupDrawing();
	}

	//prepare drawing
	private void setupDrawing(){
		drawPath = new Path();
		drawPaint = new Paint();
		drawPaint.setColor(paintColor);
		drawPaint.setAntiAlias(true);
		drawPaint.setStrokeWidth(50);
		drawPaint.setStyle(Paint.Style.STROKE);
		drawPaint.setStrokeJoin(Paint.Join.ROUND);
		drawPaint.setStrokeCap(Paint.Cap.ROUND);
		canvasPaint = new Paint(Paint.DITHER_FLAG);
	}

	//view assigned size
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
		drawCanvas = new Canvas(canvasBitmap);
	}

	//draw view
	@Override
	protected void onDraw(Canvas canvas) {
		canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
		canvas.drawPath(drawPath, drawPaint);
	}

	//respond to touch interaction
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float touchX = event.getX();
		float touchY = event.getY();
		//respond to down, move and up events
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			drawPath.moveTo(touchX, touchY);
			break;
		case MotionEvent.ACTION_MOVE:
			drawPath.lineTo(touchX, touchY);
			break;
		case MotionEvent.ACTION_UP:
			drawPath.lineTo(touchX, touchY);
			drawCanvas.drawPath(drawPath, drawPaint);
			drawPath.reset();
			break;
		default:
			return false;
		}
		//redraw
		invalidate();
		return true;
	}
}

The content of the class contains standard drawing app functionality - for more information on the details see the drawing app series.

Step 2

Let's now include the new View in the app user interface along with the other controls. Open your layout file. Replace the content with the following layout:

<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:background="#FFCCCCCC"
	android:orientation="vertical"
	tools:context=".MainActivity" >

</LinearLayout>

Inside the Linear Layout, first add an instance of the custom View, changing the package name to suit your own:

<com.example.opacitydraw.DrawingView
	android:id="@+id/drawing"
	android:layout_width="fill_parent"
	android:layout_height="0dp"
	android:layout_marginBottom="3dp"
	android:layout_marginLeft="5dp"
	android:layout_marginRight="5dp"
	android:layout_marginTop="3dp"
	android:layout_weight="1"
	android:background="#FFFFFFFF" />

We will be adding more to the UI in the next section.


2. Add Opacity Control

Step 1

In this section we will be adding a button for controlling the opacity level of our "brush".

In your layout, after the custom View, add the button for an opacity control:

<ImageButton
	android:id="@+id/opacity_btn"
	android:layout_width="wrap_content"
	android:layout_height="fill_parent"
	android:layout_gravity="center"
	android:contentDescription="opacity"
	android:src="@drawable/opacity" />

The button will launch a control through which the user will be able to set the opacity level for drawing. We will use the ID to refer to the button in the Activity code.

Tip: If you are enhancing the drawing series app, add this button in the top section of your layout file alongside the other buttons, using the same layout properties you used for them.

Step 2

You will notice that the Image Button refers to a drawable file. Let's create this now. Add a new file to your app's drawables folder(s) and name it "opacity.xml" to match the source attribute we added to the Image Button XML element. Include the following content:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:dither="true"
	android:shape="oval" >

	<size
		android:height="20dp"
		android:width="20dp" />

	<gradient
		android:endColor="#00000000"
		android:startColor="#FF000000"
		android:type="linear" />

	<stroke
		android:width="1dp"
		android:color="#66000000" />

</shape>

The shape is a circle with full opacity at one side and full transparency at the other, with a gradient fill between them.

Tip: If you're working on the drawing series app, you can use the dimension values for the medium size rather than hard-coding a dimension value in your shape drawable.

Step 3

Now let's design the little pop-up control we want to appear when the user presses the opacity button. Add a new file to your app's layout folder, naming it "opacity_chooser.xml". Enter the following layout outline:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:gravity="center"
	android:orientation="vertical" >
</LinearLayout>

Inside the Linear Layout, first add some informative text:

<TextView
	android:id="@+id/opq_txt"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:layout_marginTop="5dp"
	android:gravity="center"
	android:text="100%"
	android:textStyle="bold" />

We will update the text as the user changes the level, starting at 100% for full opacity initially. We will also use the ID in Java. To set the opacity level, the logical Android UI element is a Seek Bar. With a Seek Bar, the user interacts with a slider control, which we will set to have 0% opacity at the left and 100% on the right. Add the Seek Bar after the Text View:

<SeekBar
	android:id="@+id/opacity_seek"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:layout_margin="5dp" />

We will also be referring to this in Java. Finally, add an OK button:

<Button
	android:id="@+id/opq_ok"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="OK" />

When the user clicks OK, we will implement their chosen opacity level for subsequent drawing operations. We will also retain the chosen opacity level if the user launches the control again.

Opacity Level

3. Implement Opacity Changes

Step 1

Tip: You can skip to step 2 now if you are enhancing the app from the drawing series. Add the remaining code to your main and custom drawing View classes.

Open your main Activity class. add the following import statements:

import android.app.Dialog;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

Extend the opening line of the class declaration to implement click listening:

public class MainActivity extends Activity implements OnClickListener

Add an onClick method to your class:

@Override
public void onClick(View view){
//respond to clicks		
}

We will be adding a method to the custom View class to set the opacity level for drawing operations. So that we can do this, add an instance variable to the class, before onCreate:

private DrawingView drawView;

In onCreate, get a reference to the instance of the custom View class you have in your app layout:

drawView = (DrawingView)findViewById(R.id.drawing);

We will be able to call methods on this object to control what happens during drawing.

Step 2

Before the onCreate method in your main Activity, add an instance variable for the opacity button:

private ImageButton opacityBtn;

In onCreate, retrieve a reference to the button:

opacityBtn = (ImageButton)findViewById(R.id.opacity_btn);

Listen for clicks on the button:

opacityBtn.setOnClickListener(this);

In your onClick method, add a conditional test for the opacity button (use an else if if you're enhancing the series app):

if(view.getId()==R.id.opacity_btn){
	//launch opacity chooser
}

Step 3

Now we can launch a chooser control for setting the opacity using the layout we defined. Inside the conditional block for the opacity button in onClick, create a Dialog object, setting the title and layout for it:

final Dialog seekDialog = new Dialog(this);
seekDialog.setTitle("Opacity level:");
seekDialog.setContentView(R.layout.opacity_chooser);

Retrieve references to the Text View and Seek Bar we included in the layout:

final TextView seekTxt = (TextView)seekDialog.findViewById(R.id.opq_txt);
final SeekBar seekOpq = (SeekBar)seekDialog.findViewById(R.id.opacity_seek);

Set the maximum on the Seek Bar:

seekOpq.setMax(100);

We use 100 as the maximum possible alpha level will be 100%.

Step 4

Before we continue with the opacity control, let's add some required functionality to the custom drawing View class. Start with a new instance variable to store the current opacity level:

private int paintAlpha = 255;

The Paint method we will be using to set the opacity expects a value between 0 and 255. Add a simple get method for the value:

public int getPaintAlpha(){
	return Math.round((float)paintAlpha/255*100);
}

The level is stored as a value between 0 and 255, but we want to display it as a percentage, so we return a value between 0 and 100. Next, add a method to set the value:

public void setPaintAlpha(int newAlpha){
	paintAlpha=Math.round((float)newAlpha/100*255);
	drawPaint.setColor(paintColor);
	drawPaint.setAlpha(paintAlpha);
}

In the above, we parse the percentage value, set the color, and set the alpha.

Step 5

Back in your main Activity class, in onClick after setting the maximum on the Seek Bar control, first retrieve the existing opacity level:

int currLevel = drawView.getPaintAlpha();

Show this in the Text View and Seek Bar display:

seekTxt.setText(currLevel+"%");
seekOpq.setProgress(currLevel);

Now we want the display to update as the user slides the control up and down. Add the following event listening code:

seekOpq.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
	@Override
	public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
		seekTxt.setText(Integer.toString(progress)+"%");
	}
	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {}
	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {}
});

We only need to respond to the progress changed event, in which case we update the Text View - this will let the user see their chosen level as a numeric value. Now we need to listen for the user clicking the OK button. After the Seek Bar change listener code block, retrieve an OK button reference:

Button opqBtn = (Button)seekDialog.findViewById(R.id.opq_ok);

Now handle clicks on it:

opqBtn.setOnClickListener(new OnClickListener(){
	@Override
	public void onClick(View v) {
		drawView.setPaintAlpha(seekOpq.getProgress());
		seekDialog.dismiss();
	}
});

When the button is clicked, we call the new method we added to change the opacity level, passing the level chosen by the user, and then dismiss the Dialog. Complete the opacity button section of onClick by displaying the Dialog:

seekDialog.show();

Tip: If you're extending the series app with opacity control, you can either assume that the user wants to use full opacity when they choose a new color, or that they want to retain their chosen opacity level. To reset to 100%, you can call the setPaintAlpha method in the paintClicked method. To retain the chosen alpha level, you can add code to the setColor method to reapply the chosen alpha level after setting the new color.


Conclusion

This completes the opacity drawing functionality! You should be able to run your app, set an opacity level, then see the results in your drawing operations. If you were working on the series app and enhanced it with the pattern fills, notice that the opacity control also applies to drawing with patterns. We have now explored the various typical types of processing in drawing apps, but there are still many more you could try out in your own apps. In a forthcoming tutorial, we will look at how you can accommodate users who are not interacting via touch screens!

Related Posts
  • Code
    Android SDK
    Create a Music Player on Android: User Controls0d63m preview image@2x
    We are building a simple music player app for Android in this series. So far, we have presented a list of the songs on the device and allowed the user to make selections from it, starting playback using the MediaPlayer class in a Service class. In this final part of the series, we will let the user control playback, including skipping to the next and previous tracks, fast-forwarding, rewinding, playing, pausing, and seeking to particular points in the track. We will also display a notification during playback so that the user can jump back to the music player after using other apps.Read More…
  • Code
    Android SDK
    Create a Music Player on Android: Project Setup0d63m preview image@2x
    The Android platform provides resources for handling media playback, which your apps can use to create an interface between the user and their music files. In this tutorial series, we will create a basic music player application for Android. The app will present a list of songs on the user device, so that the user can select songs to play. The app will also present controls for interacting with playback and will continue playing when the user moves away from the app, with a notification displayed while playback elapses.Read More…
  • Code
    Android SDK
    Using the Accelerometer on Android06lom preview image@2x
    In this tutorial, we are going to explore how to use the accelerometer, one of the many hardware sensors of modern smartphones, in an Android application. I'll explain what an accelerometer is and why it may be something you want to take advantage of in your Android applications.Read More…
  • Code
    Android SDK
    Create a Hangman Game: User InterfaceAndroid hangman game preview retina
    In this series, we are creating a Hangman game for the Android platform. In the first tutorial, we set the application up to present two screens to the user and we also made a start with the user interface elements, the images and shape drawables to be precise. In the second tutorial, we will zoom in on the game's layout.Read More…
  • Code
    Android SDK
    Android SDK: Drawing with Pattern FillsPreview
    This tutorial demonstrates how to enhance a drawing application by allowing the user to paint with pattern fills rather than solid colors. Read on!Read More…
  • Code
    Android SDK
    Android SDK: Create a Drawing App - Essential FunctionalityAndroid sdk drawing app preview
    In this series, we will create a finger-painting app for Android using touch interaction. The user will be able to select from a color palette, choose a brush size, erase, create a new drawing, or save their existing drawing to the device gallery.Read More…