Archive for the 'Android' Category

Two Progress Bars for Android

I built two different views to show progress bars in Android. One is a horizontal progress bar and the other is a vertical progress bar, made up of a stack of horizontal bars. Here they are displayed in a demo app, running on a Galaxy Nexus phone.

galaxy-nexus-2

galaxy-nexus-1

For the horizontal progress, you can control the colors used, the width and height of the bar, and the number of divisions. For the vertical progress bar, I wanted it to look like a stack of bars. When you run the demo app, you can see the values change by touching a progress bar.

How It Works

The three main parts of this demo app are the following:

  1. The main layout file.
  2. The definition of the custom attributes that are used in the layout file.
  3. The view definition itself: ProgressBarView.

The layout xml file for the activity is activity_main.xml. Within it are three view definitions for the progress bars. The first is the vertical progress bar. Note that it uses a custom view type and note that the view has custom attributes. Examples are bar_initial_value, bar_num_divisions, and bar_orientation. A value of 1 for bar_orientation indicates a vertical progress bar.

<com.wglxy.example.progressbars.ProgressBarView
 custom:bar_initial_value="3"
 custom:bar_num_divisions="5"
 custom:bar_spacing="2dp"
 custom:bar_orientation="1"
 custom:bar_color1="@color/bar_green"
 custom:bar_color2="@color/bar_dark_green"
 android:background="@color/background_black"
 android:padding="20dp"
 android:layout_weight="1"
 android:layout_width="0dp"
 android:layout_height="wrap_content"
 android:layout_margin="32dp"
 />

The first horizontal bar definition looks like this:

<com.wglxy.example.progressbars.ProgressBarView

 custom:bar_initial_value="1"
 custom:bar_num_divisions="4"
 custom:bar_spacing="2dp"
 custom:bar_orientation="0"
 custom:bar_color1="@color/bar_green"
 custom:bar_color2="@color/bar_off"
 android:background="@color/bar_off"
 android:layout_width="64dp"
 android:layout_height="16dp"
 android:layout_margin="20dp"
 />

Inside the activity_main.xml file, there is a line at the top that tells the system that there are custom definitions being used. It is the line with “xmlns:custom”.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:custom="http://schemas.android.com/apk/res/com.wglxy.example.progressbars"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent" 
 >
...
</FrameLayout>

The custom attributes are defined in the values folder in a file named “attrs_views.xml”. Each definition describes the name and format of a custom style that can appear in the layout xml file. The declare-styleable part indicates the name of the view that the attributes apply to.

<resources>
 <declare-styleable name="ProgressBarView">
 <attr name="bar_color1" format="color"/>
 <attr name="bar_color2" format="color"/>
 <attr name="bar_initial_value" format="integer"/>
 <attr name="bar_num_divisions" format="integer"/>
 <attr name="bar_height" format="dimension"/>
 <attr name="bar_spacing" format="dimension"/>
 <attr name="bar_orientation" format="integer"/>
 </declare-styleable>
</resources>

In class definition for ProgressBarView, the attribute values are processed in the constructor code. The code looks to see an attribute is present, and if it is, it overrides the default value that would be used.

public ProgressBarView (Context context, AttributeSet attrs, int style) {
   super (context, attrs, style);
   readAttrs (context, attrs);
 }
/**
 * Read the attribute set and set view variables.
 *
 * @param attrs AttributeSet
 * @return void
 */
private void readAttrs (Context context, AttributeSet attrs) {
  TypedArray a = context.obtainStyledAttributes (attrs, 
                    R.styleable.ProgressBarView);
  final int N = a.getIndexCount();
  for (int i = 0; i < N; ++i) {
     int attr = a.getIndex(i);
     switch (attr) {
     case R.styleable.ProgressBarView_bar_color1:
       int c = a.getColor (attr, Color.WHITE);
       setBarColor1 (c);
       break;
     case R.styleable.ProgressBarView_bar_color2:
       int c2 = a.getColor (attr, Color.GRAY);
       setBarColor2 (c2);
       break;
     case R.styleable.ProgressBarView_bar_initial_value:
       int val = a.getInt (attr, 0);
       setValue (val);
       break;
     case R.styleable.ProgressBarView_bar_num_divisions:
       int numdiv = a.getInt (attr, DEFAULT_NUM_DIVISIONS);
       setNumDivisions (numdiv);
       break;
     case R.styleable.ProgressBarView_bar_height:
       int bh = a.getDimensionPixelSize (attr, DEFAULT_BAR_PIXEL_HEIGHT);
       setBarHeight (bh);
       break;
     case R.styleable.ProgressBarView_bar_spacing:
       int bspace = a.getDimensionPixelSize (attr, DEFAULT_BAR_PIXEL_SPACING);
       setBarSpacing (bspace);
       break;
     case R.styleable.ProgressBarView_bar_orientation:
       int orient = a.getInt (attr, DEFAULT_ORIENTATION);
       setBarOrientation (orient);
       break;
     }
   }  // end for
   a.recycle();
 }

As is typical for a subclass of View, the actual drawing of the view is done in the onDraw method . By the time it runs, all the custom attributes have been handled and their values are in instance variables of the view. My onDraw  method checks to see which orientation is in effect and then calls a method to draw the bars either horizontally or vertically.

@Override protected void onDraw(Canvas canvas) {
 int orient = getBarOrientation ();
if (orient == VERTICAL_ORIENTATION) drawOnCanvasV (canvas);
 else drawOnCanvasH (canvas);
 }

Let’s look at the code for the horizontal progress bar. The progress bar it draws is made up of N rectangles., where N is defined by the number of divisions. For example, if the number of divisions is 10 and current value is 2, 2 bars will be drawn with bar color 1, followed by 8 bars of bar color 2.

It starts by examining the variables that control the display: number of divisions, bar height, spacing, etc. Then it draws N rectangles, one for each of the divisions.

/**
 * Draw progress bar oriented horizontally.
 */
void drawOnCanvasH (Canvas canvas) {
 int maxValue = getNumDivisions ();
 int numValue = getValue ();
 if (numValue > maxValue) numValue = maxValue;
 if (numValue < 0) numValue = 0;
 int bspace = getBarSpacing ();
 int customBarHeight = getBarHeight ();
 int numDivs = getNumDivisions ();
 if (numDivs <= 0) numDivs = 1;
 int currentVal = getValue ();
 int color1 = getBarColor1 ();
 int color2 = getBarColor2 ();
 int vw = getWidth ();
 int vh = getHeight ();
 float vwf = (float) vw;
 float vhf = (float) vh;
// Draw a row of bars. Use up the entire width of the view. 
 // Add spacing between the bars. Number of spaces is 1 less than the numDivs.
 float x = 0.0f + getPaddingLeft ();
 float y = 0.0f + getPaddingTop ();
 float barWidth = (vwf - (float) ((numDivs - 1) * bspace) 
                  - getPaddingLeft () - getPaddingRight ()) 
 / (float) numDivs;
 float deltaX = barWidth + bspace;
 float deltaY = 0.0f;
 float barHeight = vhf - getPaddingTop () - getPaddingBottom ();
 if (customBarHeight > 0) barHeight = customBarHeight;
 int rcolor = color1; 
 // Draw N rectangles. Choose the color based on whether the current value 
 // is larger than the index value for the rectangle.
 // The width of the bar is chosen to account for the space 
 // that gets added between rectangles.
 for (int j = 0; j < numDivs; j++) {
   if (j < numValue) rcolor = color1;
   else rcolor = color2;
   mPaint.setColor (rcolor);
   canvas.drawRect (x, y, x + barWidth, y + barHeight, mPaint);
   x += deltaX;
   y += deltaY;
 }
}

When you use this view in a more dynamic application, you do what you normally do with a view. That is, you set values and call the view’s invalidate method to get the changes to appear. An example of that is shown in the onClick method in the MainActivity class.

public void onClick(View v) {
  ProgressBarView pb = (ProgressBarView) v;
  int numDivisions = pb.getNumDivisions ();
  int val = pb.getValue ();
  val++;
  if (val > numDivisions) val = 0;
  pb.setValue (val);
  pb.invalidate ();
}

Source Code

You can download the source code for this demo from  the Wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.2 (API 17). It works in all API levels from API 10 on up.

References

Creating a View Class 

Link to useful Android resources

I found a link on Google+ to a very useful blog post. It’s called “Resources every Android developer must know“. The author is Sergey Povzner.

Drag-Drop for Android GridView (V4)

About a year and a half ago, I built an example Android app that allows you to drag images from one spot in a GridView to another. I have had several requests to show how the drag-drop framework used in that example could be adapted to the current Android support (V4+) for drag-drop operations. I have done that work and I am making the source code available in this post.

The sample app works the same as the one described in “Drag-Drop for An Android GridView“. There is a grid displayed on the screen. An “Add Image” button allows you to add images to the screen. Those images can be dragged onto the grid. Any images in the grid can be dragged around. There is also a trash can icon on the screen. Images can be dragged there to remove them from the screen. The image below shows what the app looks like on a Nexus 7 tablet.

drag-griview-v4-05

How It Works

Usually, I do an extensive write-up to explain how my demo apps work. However, this time, I am starting with a very short description in this section and posting the source code. I will add a another longer post in the coming months.

If you read my earlier article about dragging views onto a GridView, you know that the drag-drop framework I use originates with the Android Launcher code for API 8. With a combination of interface classes for DragSource, DropTarget, a DragLayer, and DragController,  the Launcher defined a useful framework for handling drag-drop operations. How I adapted those classes from the Launcher code is described in my earlier blog post. In the newer version of my GridView demo app, I have modified the classes a little bit.

DragActivity – This is the main activity of the demo app. It sets up a grid of images, defines a listener for drag events, and connects it to the views displayed on the screen.

DragSource – A drag source is a view where drag operations start. There is a DragSource interface class that defines the methods that a view must implement to be a source.

DropTarget – A drop target is a view that accepts dragged objects. An interface class describes the methods every DropTarget must implement.

DragController – This object is the controller that does most of the work to support dragging and dropping. It receives the drag events and interacts with DragSource and DropTarget objects. It also sends information back to the activity that created it. That activity is expected to implement the DragDropPresenter interface.

DragDropPresenter – This interface defines the set of methods an activity must support in order to work with a DragController.

ImageCell – An ImageCell is a subclass of ImageView. It is used to display an image on the grid. Since objects can be moved from one spot on the grid to another, an ImageCell is both a DragSource and a DropTarget.

ImageCellAdpater – The object that is used by the GridView to place ImageCell objects on the grid.

DeleteZone – This object is a DropTarget. It allows objects to be dragged onto it for disposal. It is used to implement the trash can that you see on the screen.

In this object model, here is how an object moves on the screen. The user touches a view on the screen. The view touched signals an event, onClick, to its listener, which is the DragActivity. The activity passes the event on to its DragController object. If the view is a DragSource, the controller initiates a drag operation by asking the source for its ClipData and the view to display as the drag shadow. It then has the view start sending drag events (via a call to v.startDrag). As the user moves the view around on the screen, all the events associated with drag-drop are sent to the DragController. The DragController makes everything happen from there. When the shadow view is over a drop target, the view, if it is accepting dropped objects, is informed (via method calls to onDragEnter and onDragExit) so it can provide visual feedback to the user that a potential drop spot has been found. If the user releases the object over a drop target, the target object is informed (via onDrop) so it can perform the operation. There are also method calls made back to the DragActivity so it knows when dragging begins, when it ends, and whether it was successful.

When you study the code for DragController, you will find that there are a lot of conditions being checked. That’s because of the DragController working with two kinds of views (sources, targets) and because of the way I wanted the GridView to work. You can only put one image in a cell. The cell must be empty to accept an image. If you drag an object and drop it somewhere that does not accept drops, it goes back to the cell it started in.

Source Code

You can download the source code for this demo from  the Wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.2 (API 17). It works in all API levels from API 11 on up.

Potential Improvements

This sample app is an adaptation of the drag-drop framework from my earlier sample apps. The first of these (Moving Views In Android – Drag-Drop) goes back to January 2011. That one is the result of studying the Android Launcher code from API Level 8. I simplified the drag-drop framework a bit, but basically maintained its set of classes (DragSource, DropTarget, DragController, etc.).So the current example is a continuation of that framework. As described in the earlier section, you see that it has basically the same classes and interfaces.

It did not take much work to adapt to the Android drag-drop framework, which was introduced in API 11. However, it’s not clear to me that having a set of interface classes and defining View subclasses (e.g. ImageCell) is the easiest way to support drag-drop. In fact, there is a note to that effect in the “Drag and Drop” article. It says “You will probably want to use the listener in most cases. When you design UIs, you usually don’t subclass View classes …”. What I intend to try in the next month or so is another variation of this GridView example. In it, I will support dragging onto the grid, using only a listener. It will be interesting to see if that approach turns out to be easier.

Android Fragments Example

One thing I think is essential for understanding a feature in Android is a working example. The difference between a good write-up and the same write-up with source code attached is huge for me. I fully appreciate the great documentation and resources provided on the Android Developers website and on other websites. However, the working example is usually my guarantee that I will eventually understand a feature.

A case in point for me is the Fragments feature. I have been using them for about a year now so I am getting more comfortable using them in my app work. Several times in that time period, I have started apps with Fragments in Android 4+ and had to rework them so I could support older versions of Android. For me, that’s Android 2.3.3 (API 10). Fortunately, that’s easy to do because of the Android V4 Compatibility package that allows you to use Fragments in the earlier versions.

Since the need keeps coming up for me to have both Fragments and backward compatibility, I decided to build myself a simple demo app that combines both. That demo app helps me remember how to do that. Often, it is my starter kit for a new app. That’s what I am writing up and sharing in this blog post.

n7-and-lg-01

Uses for Fragments

If you have not read about Fragments, here’s my version of why they are useful. They make it easier to have UI components that you can use in different ways in your app. Two main uses: (1) handling differences between your app in landscape mode and your app in portrait; (2) handling differences in your UI when running on a tablet.

The following app screenshots illustrate the points. The first is the app on a small device in landscape. Nothing too surprising is going on. If you select a title on the left, the text for that title appears on the right. Reorient to portrait mode and you will see what’s in Figure 2. There is only room for the titles list. When you select a title, the whole screen changes. Titles are hidden and the details appear. The remaining figures show the same app on a tablet. There is more than enough screen in either orientation so the titles are always visible.

fragments-lg-02

Figure 1 – Landscape orientation, small screen

fragments-lg-01fragments-lg-01a

Figure 2 – Portrait orientation, small screen

fragments-n7-01

Figure 3 – Portrait orientation, Nexus 7 tablet, 1280 x 800

How It Works

You have to know only a few things to get this to work:

  1. How fragments can be used to display information in the two situations: standalone activity (Figure 2) and as part of a wider view (Figure 1 or Figure 3).
  2. How to structure your app to support different layouts for portrait and landscape
  3. How to structure your app to support the larger screens of tablets.
  4. How to provide backward compatibility for the Android versions prior to Android 4.

This demo example makes use of the code available with the Fragment class definition. That article explains the first two points above.

For point #3 above, I suggest reading one of my earlier blog posts, “My Dashboard User Interface on Android Tablets“. It explains how to use different layout folders to hold layout xml for different sizes of screens. Two of the most useful references in the References section “New Tools For Managing Screen Sizes”  and “Supporting Multiple Screens“.

The last point in list above has to do with using the new Fragment class in earlier versions of Android. For that, you should read about the V4 compatibility library for Android. Once I had the basic example working, I followed their instructions to make the demo example backward compatible. I did what they described in the section “Using the V4 Library APIs”.

  • Use FragmentActivity as the superclass for you own Activity classes if they are going to use Fragments.
  • When you need a FragmentManager object, call getSupportFragmentManager rather that getFragmentManager.

I did not do anything about the action bar and title bar. They are what they are in each of the Android versions. If you want to read about some options for the action bar, read my note on “Getting Started With Android V4“.

Source Code

You can download the source code for this demo from  the wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.2 (API 17). It works in all API levels from API 10 on up.

Conclusion

You can find examples of fragments in many places; the same goes for V4 compatibility. So, in a way, there is not a lot new here. However, if you know you want both fragments and V4 compatibility, consider using this app as a starter for your next project.

References

Fragments

V4 compatibility library for Android

Supporting Multiple Screens

Presentation: Play Testing a Space War App

I was the speaker at Tridroid, the Raleigh area Meetup group for Android, on February 7. I talked about play testing and we conducted a short play test session on a game app that I am working on. I hope everyone there enjoyed it as much as I did.

See my previous post for a longer description of the session.

The presentation is available on Google Drive. Click here: presentation on Play Testing a Space War App.

Play Testing a Space War App

I found a great blog post about game play testing: “Play testing a game design the low fidelity way“, written by Chris Khoo. It came at a good time for me because I was stuck last July, not making much progress on my Space War app. The reminder about play testing was good, but what helped me the most was the “low fidelity” part.

Chris Khoo’s situation was similar to mine. His goal was to “build a game which casual strategy fans could easily pick up”. The challenges he faced  included  a “cumbersome user interface” and  the problem of “too many commands available to a user at any one point in time”. In his blog post, he describes how he began a redesign and started it with paper and pencil prototypes and old board game pieces.

Since low fidelity seemed to work for him, I thought I’d try it too. I did a bit of work on a chalkboard and then I moved to paper prototyping. Where am I today? Well, still not finished, but I have made a lot of progress. I have, I hope, greatly simplified the user interface, particularly at the introductory (novice) level of the game. At this point, I am ready to hold my first play testing session. It’s now a working Android app, but still low fidelity. The graphics certainly show that. Still I want to test the game mechanics and get a sense of the overall playability of the game.

Screenshots for Starship app

If you are in the Raleigh, North Carolina area and would like to attend my presentation and play testing session on February 7, here is the link for the meetup: Play Testing session at Tridroid.

I will say more about the play testing session and my Space War app in future posts.

Multitouch Panning and Zooming Examples for Android

I have done some work to help me understand multitouch features in Android. I built a simple demo with examples of pinch zooming and panning. Most of the examples involve using drawing operations on the canvas of a view.

zoom-pan-blog-01zoom-pan-01

About two years ago, I started some work on moving images in Android. I started with the “Making Sense of Multitouch” blog article and ended up doing some simple things with moving images and then drag-drop based on the Android Launcher (see references for links). It’s interesting to me that I have gone back to that same article for a different purpose. I am working on apps where the basic zoom and pan operations are needed.

Overview of the Examples

1 Basic Multitouch

The first example does the basics of multitouch as explained in the Multitouch article. An image displays on the screen. You can touch the image and move it around. You can also use two fingers to do pinch-zoom and zoom out.

zoom-pan-04

I did this as the first step because I wanted to be sure that I understood the Multitouch article and had a working example.

To understand how this example works, go through the Mulitouch article and then get the source code for my examples (see below). My adaptation of the Multitouch article is in class PanZoomView.

/**
 * This view supports both zooming and panning.
 * What gets shown in the view depends on the onDraw method provided by subclasses.
 * The default onDraw method defined displays R.drawable.zoom_view_sample.
 */
public class PanZoomView extends View

The key things to understand in the article and the code are these:

  • How the canvas moves around. See the call to “canvas.translate(x, y)” in method onDraw.
  • How the canvas is scaled during a pinch-zoom gesture. See the call to :canvas.scale(mScaleFactor, mScaleFactor)” in method onDraw.
  • How onTouchEvent handles touch and multitouch events. It sets variables so the calls to translate and scale work.

2 Canvas

In the second section, there are two examples that do essentially the same thing for handling panning and zooming as the first one. The difference is that instead of an image being moved around and zoomed, it is a circle and a rectangle. Those are drawn on the canvas in the onDraw method of the view.

zoom-pan-05 zoom-pan-06

I did these as the next step in understanding how to build my views for my own app. Zooming an image was step one. Zooming a custom canvas was step 2. To help me understand pinch zoom and the point around which zooming occurs, I display a small circle at the point of focus for the scaling of the view. Place two fingers on the screen and look for that dot, halfway between your two fingers.

The Circles example supports both panning and zooming. The Rectangles example supports only zooming. I found myself getting a bit confused while trying to understand for translation on the canvas and scaling so both examples were useful to me.

3. Pan Zoom Listener

PanZoomListener is an interesting example. It comes from Java Pan / Zoom listener for Android (reference 2 below). Rather than doing a custom view every time you want to have panning and zooming, which is what all the other examples do, you simply set up a listener on a view you already have working.

FrameLayout f = new FrameLayout(context);
FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams (...);
imageView imageView = new ImageView (this);
view.addView (imageView, fp);
view.setOnTouchListener(new PanAndZoomListener(view, imageView, Anchor.TOPLEFT));

That may look like a lot to do, but it’s not nearly as complicated as what you do when you build a view of your own with custom handling of panning and zooming.

It works pretty well, but there are a few things that you’d need to adjust because not all of the operations are completely smooth. I definitely like the idea. I adapted the code and built my own example.

What you see in the example I did is an image. As soon as you touch it, it resizes and centers itself. After that, you can use pinch zoom on it. The focus of the zoom is the point between your two fingers.

zoom-pan-09 zoom-pan-10

4. Image Squares

This example was another step toward getting the right panning and zooming effects in the app that I am working on. In the app, I want to be able to draw a grid of squares on a canvas. In the example, I don’t draw the entire grid but simply some squares along a diagonal from top to bottom. This example supports zooming but not moving the canvas around as you touch the screen with one finger. The focus point of the zoom is shown with a small circle again.

zoom-pan-07

You cannot move the canvas around with a simple touch. However, if you start a pinch-zoom gesture and then move both fingers around the image does shift. I never figured out exactly what was causing that. So watch out for that if build on this example.

5. Fixed Point Zoom

Since I was having a few problems with the canvas jumping around a bit, I decided to try using a fixed point for zooming. The last  example zooms in and out around the center dot shown on the screen.

zoom-pan-11 zoom-pan-14

The code structure of this example is a bit different than the earlier ones. All of the views in these examples are subclasses of a PanZoomView class. The view used in this example is LargeGridView. It defines its own drawOnCanvas method like all the rest do, but it also defines its own onDraw method. By the time I got here, I found that the flexible framework I built in PanZoomView to explore the different aspects of panning and zooming wasn’t quite right for fixed point zooming.

Fixed point zoom is what I have ended up using in a couple of apps.

Conclusion

I have done this article a bit differently than some of my earlier blog tutorials. I have not included as much code and explanations.  I assume that most readers will dig into the code as they need to. Download the source code using the links in the next section.

What I wanted to do here is show the progression of my thinking and understanding as I worked on an interesting problem. If you are new to Android, I suggest following a similar progression. Start simple and add more complex features as your understanding increases.

Source Code

You can download the source code for this demo from  the wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.2 (API 17). It works in all API levels from API 10 on up.

Source code is also available as a zip file from Google Drive.

References

Making Sense of Multitouch – I recommend this to anyone looking for a good starting point for touch handling in Android.

Java / Pan Zoom Listener for Android - Article that describes the PanZoomListener. Definitely, take a look at this one.

Moving Views in Android – Part 1 – a simple example that moves an image when you click a button

Moving Views in Android – Part 2, Drag and Drop – an example based on the Launcher code of Android 2.2.

2012 in review

The WordPress.com software prepared a 2012 annual report for this blog.

Here’s an excerpt:

About 55,000 tourists visit Liechtenstein every year. This blog was viewed about 220,000 times in 2012. If it were Liechtenstein, it would take about 4 years for that many people to see it. Your blog had more visits than a small country in Europe!

Click here to see the complete report.



Follow

Get every new post delivered to your Inbox.

Join 73 other followers