Animating the SearchView0 comments
I’ve recently been doing some experimenting with the SearchView widget. This widget was introduced in Android 3.0 but has also had a few additions and updates to it since then. It was designed to be used mainly in an ActionBar , but it’s also possible to use it within any layout.
One of the cool things about the SearchView is that it can be collapsed (iconified) so that it only takes a small amount of space, and when pressed it can go into an expanded state so that text can be entered and searched Collapsing and expanding the SearchView works fine, but for me something immediately did not feel right. If you try this yourself you’ll notice that there is no transition animation between the collapsed and expanded states. Things just disappear and appear, it’s like a magician's trick without the puff of smoke. To make things better it would be good if there were some animations when collapsing and expanding the SearchView, maybe a slide in and out. So after a bit of digging around I noticed that the SearchView essentially is a Layout that contains a few LinearLayouts. Since Android 3.0 it’s been possible to animate layout changes within a LinearLayout by setting an attribute called animateLayoutChanges. When this is set, removing a View from the layout will cause it to fade out and the remaining views will slide in to fill the empty space left by the removed view. Adding a view will cause existing views to slide out of the way to make room for the new view. So in theory we could just set this attribute on a LinearLayout in the SearchView and our transitions between the collapse and expand state will be animated. Not quite, since the LinearLayout is within the SearchView we do not have access to the XML so we need to do the equivalent in Java. We can do this by setting a LayoutTransistion on the LinearLayout within the search. There’s a little more work than usual to get a reference to this LinearLayout, but once we have done that setting the layout transition is just one line of code. Here’s the code to do this: SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
//Get the ID for the search bar LinearLayout
int searchBarId = searchView.getContext().getResources().getIdentifier("android:id/search_bar", null, null);
//Get the search bar Linearlayout
LinearLayout searchBar = (LinearLayout) searchView.findViewById(searchBarId);
//Give the Linearlayout a transition animation.
searchBar.setLayoutTransition(new LayoutTransition());
With these few lines of code we now have an animated expand and collapse for the SearchView, this is how it looks: This is a video of the animations running in an emulator. On a real device the animations run much more smoothly. It’s not perfect, but its a fair improvement for not much effort. To make things even better it would also be nice to animate the adding and removing of buttons once the SearchView is in the expanded state. Unfortunately this is not something that can be done with a few lines of code. It’s most likely that to achieve this a new SearchView would have to be written with built in support for animations. Maybe that could be another blog post. Crossfading images in a ListView2 comments
I’ve recently been doing some work with ListViews that contain images populated from a server. In this scenario the ListView shows a default image and once the required image is downloaded it then displays the downloaded image. Below shows two screenshots of the TED app one showing a ListView with default images, and the other showing the ListView once the images have been downloaded.
The TED app is an example of a really nice android app and it works great. But if you try it, and watch closely as the listview populates the images, you’ll notice that once each image is loaded the default TED image disappears and the downloaded images appears. There’s no transition, one disappears and the other appears. This is ok but does create a slightly jarring experience, in the real world we’re not accustomed to things just appearing and disappearing. To help create a better experience what we can do is create a transition between the two images. In this case a crossfade transition would work well. Gradually fading the default image out and downloaded image in. Here’s a video showing an example App’ that I created with this crossfade transition. This video shows an example app that I wrote to test the crossfade transition on the images in a ListView. It’s a simple app that just downloads images from a Picasa JSON feed. Hopefully you’ll agree that the crossfade of the images is a nice addition and really adds to the user experience. To do this I first looked at the TransitionDrawable in the Android libs. This looked like a good candidate as it allows two drawables to be associated with it that you can then crossfade between. Unfortunately this class didn’t quite work for the scenario of transitioning in Listviews as it assumes that you have both drawables when the TransitionDrawable is created. Obviously in this scenario we don’t have both drawables until the second drawable has been downloaded from the server. So with a little more searching I then found a class called CrossFadeDrawable that is part of the Shelves app created by Romain Guy. This class is in fact very similar to TransistionDrawble but it also has methods that allow the setting of the end (downloaded) drawable, so it doesn’t need to have both drawables defined up front. The CrossFadeDrawble was almost exactly what I needed , but there was still one more thing that needed to be fixed. The original CrossfadeDrawble assumed that the start Image (default image) and the end image(downloaed image) would be of the same dimensions. This was not the case in my scenario. What I wanted was something that would scale the downloaded image to fit inside the default start image. The last thing to do was to add a tweak to the CrossfadeDrawable so that it would use a matrix transformation to scale the downloaded image until it would fit into the dimensions of the default (start) image. So that was all that needed to be done to create the crossfade transition. Of course since we are using a list view we still have to create an Adapter to populate the list and an Async task to download the images and update the CrossfadeDrawable. Flash and AIR the nirvana of mobile development?0 commentsRecently I've been looking at the new pre-release of AIR for Android. From me this is a really interesting technology. I’m familiar with both Java and Flex. But although Android uses Java, there are still a fair number of new components to learn about and additional to this, there is also a learning curve involved with getting to grips with some of the Android ways of architecting applications, such as using Activities, Services, Intents etc.. If I can use Flex, things become instantly more familiar. There’s no new UI components to learn and I have a full understanding of how to structure my apps. But while this is nice for writing applications for Android, what is also very appealing is the prospect that I will be able to port these applications very easily to other platforms such as Meego, Blackberry and others. While a lot of media focus has been on Flash and AIR running on Android (and not on iPhones). There has also been some talk of Flash and AIR running on Platforms such as Meego (Nokia) and Blackberry. If this turns out to be the case this would be great for Flash/Flex developers. Instantly applications written for AIR would have a much wider market, as the cost and time for developers to get their applications running across multiple platforms would be reduced dramatically. You may have noticed, that I'm mainly focusing on AIR here and not Flash. For me AIR on mobile devices is a more interesting prospect at the moment. Why? Well my experience of using mobile phones is that small installed application give a better user experience than web based applications. This is mainly due to the speed of data connections. An installed app doesn’t have to load any presentation and UI elements, it just needs to get the data, so it’s more responsive. As mobile networks develop and their bandwidths increase I'm sure this will all change, but then changing an AIR application to a Flash application is pretty straight forward, so that’s covered too. All of this of this sounds great. Using established tools, languages, and frameworks, with cross platform support and the ability to run locally installed or web applications. What mobile developer could want more? So there must a be a catch, right? Well at the moment true cross platform support is still not a reality and although Flash and Flex are said to be supported on Android 2.2, that doesn’t necessarily mean that a phone that has Android 2.2 will run flash or AIR applications. For example, I installed a modded ROM of Android 2.2 on a HTC magic, and then installed the AIR runtime and an AIR application with no problems. Unfortunately the application simply would not run (it ran fine, albeit rather slowly, in Android emulator). It still seems that it’s early days yet for AIR and Flash on mobile. The next release of the Flex framework, known as Hero, is due for release towards the end of this year promising intergrated support for development of both desktop and mobile devices. So as devices become more powerful, and if the promises live up to the hype, it does seems that Flash and Flex developers could have a whole new world of opportunities on mobile devices.
Installing Adobe AIR runtime on Android 2.1 emulator11 comments
Here's a quick guide that I've put together on getting an Adobe AIR runtime installed in an Android emulator running 2.1 (Eclair). I haven't had any chance to do testing on this process, but the runtime seems to install OK. I'm off on holidays soon, so if I get chance I'll put up a quick post of how to create and install a Flash application, but I'm getting short of time, so here at least is a guide to installing the AIR runtime in an emulator running 2.1. Yes that's 2.1 not 2.2.
First thing you'll need to do is to sign up for the adobe prerelease. Which you can do here: http://labs.adobe.com/technologies/air2/android/ Once you've done that you'll get a login and will be able to download the software and documentation: These are the options that you have to download the the AIR for Android builds. As you can see there are runtimes for Eclair and Froyo, so even if you do not have a phone that is running Android 2.2 you can still have a runtime for Android 2.1. There is even a runtime emulator for Eclair so if you don't have an actual device with either 2.1 or 2.2, you can still try your AIR applications out in an emulator! Since I only have an emulator I'm going to use this runtime on my setup. So the runtime that we are interested in here is, Runtime_Emulator_Eclair_20100517.apk. The fact that you can run applications on 2.1 is also born out in the FAQs: To develop an AIR for Android application using the command-line tools, you will need a recent Flex SDK and the AIR 2.5 SDK available on the AIR for Android prerelease website. You'll use the Android SDK tools to install the application on a device or Android emulator. I don't have a device that runs Android 2.1 or 2.2. So I'm going to install the AIR runtime in an emulator. First make sure that your Android SDK is updated so that it supports Android 2.1. Then go to the tools directory of you Android SDK installation and run: android list targets This will give you a list of target ids , at least one of which should correspond to Android 2.1. Once you have the correct target id for Android 2.1 you can then create the AVD with this command: First we need to make sure the we have a AVD (Android virtual device) that supports android 2.1. using this command: android create avd -n avd2.1 -t 11 You should see something like: Created AVD 'avd2.1' based on Google APIs (Google Inc.), with the following hardware config:hw.lcd.density=160 To see the list of available avds use the command: android list avds To start the emulator with our AVD we use the following command: emulator -avd avd2.1 Once we have the emulator running with the correct AVD we can now try and install the AIR runtime!! adb install \ From this you should see the following output: 950 KB/s (6784204 bytes in 6.968s) pkg: /data/local/tmp/Runtime_Emulator_Eclair_20100517.apk Success You can also check on your emulator that the adobe AIR runtime is installed by going to Menu->Settings-Manage Applications and view the installed applications. You should see something like this: OK so that's the runtime installed now we just need to write a quick application and check that it runs. I'll try and do this soon, before I go on holidays this weekend hopefully. :) Android 2.2 Froyo0 comments
I've haven't had much chance to post lately, there's been a lot going on in my life. I won't go into details but honestly I've been really busy! I'd also just like to say thanks to all the people who have got in touch with me, making suggestions and giving such positive feedback, it really is great to know that what I've written here is useful to loads of people out there.
OK so now onto the promised info' in this post, Froyo and Android 2.2. It looks like Google I/O tomorrow is going to be the day when we finally see the new Version of Android 2.2. It's been a while since 2.1 so it's going to be really interesting to see what is going to be new in Android 2.2. Some of the main new feature that are being touted are:
But for me the first two features on the list are the most interesting. The performance improvements achieved by the Dalvik JIT compiler simply sound astounding, with reports that there are improvements of 450%. Android Police have a a great article here detailing how they have used Linpack to benchmark the performance of Froyo. Flash is also another big feature. Yes, there's been so much talk about this, and how well flash will run, but this has gotta to be a huge feature. I'm really looking forward to checking this out, hopefully it will be possible to compile and run flash apps within the new version of the Android emulator. Also if Flash 10.1 is going to be running on the likes of Symbain, Web OS and Blackberries this will give developers so much more scope to reach across platforms. Once I've checked a few things out here I hoping to give some tutorials on writing flash applications for Android, so keep reading this blog! Yes, Android Froyo 2.2 really does sound like it's going to be a great release. I can't wait to see how it really flies. Now I just need to get my hands on a Android phone running 2.2. Anyone know how I can get hold of a nexus one in the UK? ;) Coverflow in PicPush0 comments
Here's an example of the Coverflow widget in a commercial application called PicPush. PicPush is a multimedia syndication application for Android. It allows you to automatically share your pics and videos to Shutterfly, PhotoBucket, SmugMug, Facebook, Gallery, Picasa and Flickr.
It's developed by the guys at 350 Nice. There's some more on how easy they found the integration here. I think they've done a fantastic job, the Coverflow widget looks great! Android Coverflow Widget V276 comments![]() It's been a while now and I've been pretty busy with life, work, and other things, but at last here's a new version of the coverflow widget that I released a while back. There's not really much new functionality here, but I have cut down the amount of code needed by a lot. Gone are the CoverAbsSpinner and the CoverAdapterView classes and now we are left with just the CoverFlow class weighing in at less than 200 lines of code. Of course, you still have to instantiate it and populate it using an ImageAdapter so I'll also include the activity class for doing those tasks as well. Background The basic idea is that the Coverflow widget works very much like the standard Android Gallery widget, but with the addition of the rotation of the images. For more background information on the Coverflow widget see my original post here. How to use In your Android project create a package called com.example.coverflow, and in this place the CoverFlow class. Now all you need is an activity to instantiate the Coverflow widget in this example the activity class is called CoverflowExample. CoverFlowExample simply extends Activity and instantiates a Coverflow class in it's onCreate method. It also has an internal Class, ImageAdapter which we use as the adapter for the Coverflow widget, much as we would for a Gallery widget. It is also possible to use the Coverflow widget when it is specified in an XML layout file. The CoverFlow and CoverflowExample classes are here: /*
* Copyright (C) 2010 Neil Davies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This code is base on the Android Gallery widget and was Created
* by Neil Davies neild001 'at' gmail dot com to be a Coverflow widget
*
* @author Neil Davies
*/
package com.example.coverflow;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;
import android.widget.ImageView;
public class CoverFlow extends Gallery {
/**
* Graphics Camera used for transforming the matrix of ImageViews
*/
private Camera mCamera = new Camera();
/**
* The maximum angle the Child ImageView will be rotated by
*/
private int mMaxRotationAngle = 60;
/**
* The maximum zoom on the centre Child
*/
private int mMaxZoom = -120;
/**
* The Centre of the Coverflow
*/
private int mCoveflowCenter;
public CoverFlow(Context context) {
super(context);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs) {
super(context, attrs);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setStaticTransformationsEnabled(true);
}
/**
* Get the max rotational angle of the image
* @return the mMaxRotationAngle
*/
public int getMaxRotationAngle() {
return mMaxRotationAngle;
}
/**
* Set the max rotational angle of each image
* @param maxRotationAngle the mMaxRotationAngle to set
*/
public void setMaxRotationAngle(int maxRotationAngle) {
mMaxRotationAngle = maxRotationAngle;
}
/**
* Get the Max zoom of the centre image
* @return the mMaxZoom
*/
public int getMaxZoom() {
return mMaxZoom;
}
/**
* Set the max zoom of the centre image
* @param maxZoom the mMaxZoom to set
*/
public void setMaxZoom(int maxZoom) {
mMaxZoom = maxZoom;
}
/**
* Get the Centre of the Coverflow
* @return The centre of this Coverflow.
*/
private int getCenterOfCoverflow() {
return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();
}
/**
* Get the Centre of the View
* @return The centre of the given view.
*/
private static int getCenterOfView(View view) {
return view.getLeft() + view.getWidth() / 2;
}
/**
* {@inheritDoc}
*
* @see #setStaticTransformationsEnabled(boolean)
*/
protected boolean getChildStaticTransformation(View child, Transformation t) {
final int childCenter = getCenterOfView(child);
final int childWidth = child.getWidth() ;
int rotationAngle = 0;
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
if (childCenter == mCoveflowCenter) {
transformImageBitmap((ImageView) child, t, 0);
} else {
rotationAngle = (int) (((float) (mCoveflowCenter - childCenter)/ childWidth) * mMaxRotationAngle);
if (Math.abs(rotationAngle) > mMaxRotationAngle) {
rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;
}
transformImageBitmap((ImageView) child, t, rotationAngle);
}
return true;
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* @param w Current width of this view.
* @param h Current height of this view.
* @param oldw Old width of this view.
* @param oldh Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCoveflowCenter = getCenterOfCoverflow();
super.onSizeChanged(w, h, oldw, oldh);
}
/**
* Transform the Image Bitmap by the Angle passed
*
* @param imageView ImageView the ImageView whose bitmap we want to rotate
* @param t transformation
* @param rotationAngle the Angle by which to rotate the Bitmap
*/
private void transformImageBitmap(ImageView child, Transformation t, int rotationAngle) {
mCamera.save();
final Matrix imageMatrix = t.getMatrix();;
final int imageHeight = child.getLayoutParams().height;;
final int imageWidth = child.getLayoutParams().width;
final int rotation = Math.abs(rotationAngle);
mCamera.translate(0.0f, 0.0f, 100.0f);
//As the angle of the view gets less, zoom in
if ( rotation < mMaxRotationAngle ) {
float zoomAmount = (float) (mMaxZoom + (rotation * 1.5));
mCamera.translate(0.0f, 0.0f, zoomAmount);
}
mCamera.rotateY(rotationAngle);
mCamera.getMatrix(imageMatrix);
imageMatrix.preTranslate(-(imageWidth/2), -(imageHeight/2));
imageMatrix.postTranslate((imageWidth/2), (imageHeight/2));
mCamera.restore();
}
}
/*
* Copyright (C) 2010 Neil Davies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This code is base on the Android Gallery widget and was Created
* by Neil Davies neild001 'at' gmail dot com to be a Coverflow widget
*
* @author Neil Davies
*/
package com.example.coverflow;
import java.io.FileInputStream;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
public class CoverFlowExample extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoverFlow coverFlow;
coverFlow = new CoverFlow(this);
coverFlow.setAdapter(new ImageAdapter(this));
ImageAdapter coverImageAdapter = new ImageAdapter(this);
//coverImageAdapter.createReflectedImages();
coverFlow.setAdapter(coverImageAdapter);
coverFlow.setSpacing(-25);
coverFlow.setSelection(4, true);
coverFlow.setAnimationDuration(1000);
setContentView(coverFlow);
}
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private FileInputStream fis;
private Integer[] mImageIds = {
R.drawable.kasabian_kasabian,
R.drawable.starssailor_silence_is_easy,
R.drawable.killers_day_and_age,
R.drawable.garbage_bleed_like_me,
R.drawable.death_cub_for_cutie_the_photo_album,
R.drawable.kasabian_kasabian,
R.drawable.massive_attack_collected,
R.drawable.muse_the_resistance,
R.drawable.starssailor_silence_is_easy
};
private ImageView[] mImages;
public ImageAdapter(Context c) {
mContext = c;
mImages = new ImageView[mImageIds.length];
}
public boolean createReflectedImages() {
//The gap we want between the reflection and the original image
final int reflectionGap = 4;
int index = 0;
for (int imageId : mImageIds) {
Bitmap originalImage = BitmapFactory.decodeResource(getResources(),
imageId);
int width = originalImage.getWidth();
int height = originalImage.getHeight();
//This will not scale but will flip on the Y axis
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
//Create a Bitmap with the flip matrix applied to it.
//We only want the bottom half of the image
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height/2, width, height/2, matrix, false);
//Create a new bitmap with same width but taller to fit reflection
Bitmap bitmapWithReflection = Bitmap.createBitmap(width
, (height + height/2), Config.ARGB_8888);
//Create a new Canvas with the bitmap that's big enough for
//the image plus gap plus reflection
Canvas canvas = new Canvas(bitmapWithReflection);
//Draw in the original image
canvas.drawBitmap(originalImage, 0, 0, null);
//Draw in the gap
Paint deafaultPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap, deafaultPaint);
//Draw in the reflection
canvas.drawBitmap(reflectionImage,0, height + reflectionGap, null);
//Create a shader that is a linear gradient that covers the reflection
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff,
TileMode.CLAMP);
//Set the paint to use this shader (linear gradient)
paint.setShader(shader);
//Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
//Draw a rectangle using the paint with our linear gradient
canvas.drawRect(0, height, width,
bitmapWithReflection.getHeight() + reflectionGap, paint);
ImageView imageView = new ImageView(mContext);
imageView.setImageBitmap(bitmapWithReflection);
imageView.setLayoutParams(new CoverFlow.LayoutParams(120, 180));
imageView.setScaleType(ScaleType.MATRIX);
mImages[index++] = imageView;
}
return true;
}
public int getCount() {
return mImageIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
//Use this code if you want to load from resources
ImageView i = new ImageView(mContext);
i.setImageResource(mImageIds[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(130, 130));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
//Make sure we set anti-aliasing otherwise we get jaggies
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
return i;
//return mImages[position];
}
/** Returns the size (0.0f to 1.0f) of the views
* depending on the 'offset' to the center. */
public float getScale(boolean focused, int offset) {
/* Formula: 1 / (2 ^ offset) */
return Math.max(0, 1.0f / (float)Math.pow(2, Math.abs(offset)));
}
}
}
Code explanation The CoverflowExample Code contains some extra code for creating reflections of the images, but apart from this is a standard activity class of the kind used with the Gallery widget.The CoverFlow class extends the Gallery widget , but now we override a few methods to allow us to transform the images before they are displayed. The most important method that we override is the getChildStaticTransformation method. Here I have to say thank you to a user called Nerdrow on the Android developers group who first showed me an example of overriding this method. By setting setStaticTransformationsEnabled in the constructors, we are telling the parent ViewGroup of the Coverflow class to invoke getChildStaticTransformation every time one of our images in drawn. In getChildStaticTransformation we simply calculate and apply a rotation and scale to the ImageView depending on it's position relative to the centre of the Coverflow widget. We also override the onSizeChanged method. This method is called every time the widget changes size e.g. when we change the orientation of the phone from portrait to landscape. In the onSizeChanged method we just get the centre position of the coverflow widget. There are also a few parameters that we can set, mZoomMax controls the maximum zoom of the central image and mMaxRotationAngle sets the maximum rotation angle of each image.
Thanks Lastly, I just wanted to say thanks to everyone who has commented and given me feedback on the coverflow widget. It's be really positive and great to hear that people have got it working successfully on a range of devices such as the HTC magic , Motorolla droid and Nexus 1. There's still more to do for the coverflow widget and I'll release new version as and when I make updates. If anyone has anymore suggests for cool widgets or enhancements to this Coverflow widget then please do get in touch.
Subscribe to:
Posts (Atom)
About
My Name is Neil Davies and this is my blog. I live in the UK in the beautiful city of Bath. I've worked as a software engineer for a number of years in a variety of roles and with a wide range of technologies and programming languages. My special interests are in UI design and UX. Currently I'm enjoying programming on the Android platform. When I'm not programming I enjoy messing about on the water, which includes sailing, kayaking and surfing. If you'd like to get in touch email me at interfuser at googlemail dot com or just leave a comment.
Labels
Blog Archive |





.png)



