Follow treslines by email clicking Here!

Tuesday, April 8, 2014

Applying decorator pattern to decorate views on Android

Hi there!

Today i'm gonna show how to apply the design pattern decorator to android's views making use of its dependency injection's concept. I thought it was a nice, clear and simple way to decorate views and that's my motivator to share it with you. I'm assuming you know the decorator pattern already and are looking for real examples involving android. If you are trying to understand the concept behind it, this is also a good article but you may need to learn the fundamentals first. There are a bunch of good books out there. I personally recommend head first.

In this example i'll try to show how to decorate a view with icons dynamically. Something similar to eclipse's tree decoration while some compile error or warning appears on the package structure. We will end up with something like that:

Error/warning tree 
decoration in eclipse


Our view decoration 
examplein Android

Let's visualize the structure by defining the UML-diagram

 

First create a blank android's project

Put some icons of your choice in the folder drawable-yourChoice. In my example i took the drawable-xhdpi. I took the icons directly from my eclipse package. You may use your own icons.

Layouting the view

Ok, now copy this code into your activity_main.xml layout:
< RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    < ImageView
        android:id="@+id/middle"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:src="@drawable/ic_launcher" / >

    < ImageView
        android:id="@+id/leftTop"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignLeft="@+id/middle"
        android:layout_alignTop="@+id/middle"
         / >

    < ImageView
        android:id="@+id/rightTop"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignRight="@+id/middle"
        android:layout_alignTop="@+id/middle"
         / >

    < ImageView
        android:id="@+id/leftBottom"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignBottom="@+id/middle"
        android:layout_alignLeft="@+id/middle"
         / >

    < ImageView
        android:id="@+id/rightBottom"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignBottom="@+id/middle"
        android:layout_alignRight="@+id/middle"
         / >

    < ImageView
        android:id="@+id/middleTop"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignTop="@+id/middle"
        android:layout_centerHorizontal="true"
         / >

< / RelativeLayout >

The common decorator interface

Well ok, let's start by defining the common decorator interface:
/**
 * The common decorable interface to be used while implementing decorators.
 * @author Ricardo Ferreira
 * @version 1.0
 * @since 07/04/2014
 */
public interface Decorable {
    /** implements the concrete decorator's behavior */
    void decorate();
}

Defining the decorators contract

This class defines what kind of object we want to decorate. In our case an android's view which belongs to the Android's API itself:
import java.util.Arrays;
import android.content.Context;
import android.view.View;
/**
 * Used to define the decorator's contract. In this case to decorate a view with icons
 * @author Ricardo Ferreira
 * @version 1.0
 * @since 07/04/2014
 */
public abstract class AbstractIconDecorator implements Decorable{
    
    protected View view;
    protected View[] views;
    protected int drawableId;
    protected Context context;
    /**
     * Concrete classes must call this constructor to conform the decorator's contract.
     * @param context the app's context
     * @param view the view to be decorated
     * @param drawableId the drawable id dependency over R.drawable.yourId to be set 
     * as background to the view of this constructor.
     */
    public AbstractIconDecorator(Context context, View view, int drawableId){
        super();
        this.view = view;
        this.context = context;
        this.drawableId = drawableId;
        decorate();
    }
    /**
     * Concrete classes must call this constructor to conform the undo decorator's contract.
     * @param context context the app's context
     * @param views the views to be undone. 
     */
    public AbstractIconDecorator(Context context,View[] views){
        super();
        this.context = context;
        this.views = Arrays.copyOf(views, views.length);
        decorate();
    }
}

Implementing a master icon decorator

This is a very nice implementation that takes advantage of android's concept of dependency injection over the resource class R.drawable.myId. Using it, we don't have to implement a lot of decorators. Instead of it, we implement just one and inject our dependencies according to our needs as you will see.
import android.content.Context;
import android.view.View;
/**
 * Use this class to decorate views with icons thru dependency injection passing R.drawable.yourId
 * @author Ricardo Ferreira
 * @version 1.0
 * @since 07/04/2014
 */
public class IconDecorator extends AbstractIconDecorator{

    /**
     * Creates an universal icon decorator to be used while dealing with view's decoration.
     * @param context the app's context
     * @param view the view to be decorated
     * @param drawableId the drawable id dependency over R.drawable.yourId to be set 
     * as background to the view of this constructor.
     */
    public IconDecorator(Context context,View view, int drawableId){
        super(context, view, drawableId);
    }
    @Override
    public void decorate() {
        view.setBackground(context.getResources().getDrawable(drawableId));
    } 
} 

Implementing an undo decorator to revert decorations

This is a special kind of decoration. I will decorate the main icon(the android's green robot icon) with "nothing" making all decorations disappear.
/**
 * Use this decorator to undo decoration by passing the view to be undone.
 * @author Ricardo Ferreira
 * @version 1.0
 * @since 07/04/2014
 */
public class ClearIconDecorator extends AbstractIconDecorator{

    /**
     * Creates an undo decorator.
     * @param context the app's context
     * @param views the views that has been decorated to be undone.
     */
    public ClearIconDecorator(Context context,View[] views){
        super(context,views);
    }
    @Override
    public void decorate() {
        for (View view : views) {
            view.setBackground(null);
        }
    } 
}

The main activity

Now the final step before running it. In this example i am simulating the decoration over clicks. But in your professional applications those decorations may occur while something go wrong or right or you may need to give some visual user feedbacks while doing something. Use your imagination ;)
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

public class MainActivity extends Activity implements OnClickListener{

    private View middle;
    private View leftTop;
    private View leftBottom;
    private View rightBottom;
    private View rightTop;
    private View middleTop;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        middle = findViewById(R.id.middle);
        leftTop = findViewById(R.id.leftTop);
        leftBottom = findViewById(R.id.leftBottom);
        rightBottom = findViewById(R.id.rightBottom);
        rightTop = findViewById(R.id.rightTop);
        middleTop= findViewById(R.id.middleTop);
        
        leftTop.setOnClickListener(this);
        leftBottom.setOnClickListener(this);
        rightBottom.setOnClickListener(this);
        rightTop.setOnClickListener(this);
        middle.setOnClickListener(this);
        middleTop.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            
        case R.id.leftTop:
            new ErrorIconDecorator(view);
            break;
            
        case R.id.leftBottom:
            new WarningIconDecorator(view);
            break;
            
        case R.id.rightTop:
            new InfoIconDecorator(view);
            break;
            
        case R.id.rightBottom:
            new QuickFixIconDecorator(view);
            break;
            
        case R.id.middleTop:
            new IconDecorator(this,view, R.drawable.newpack_wiz);
            break;
            
        case R.id.middle:
            final View[] views = new View[]{leftTop,leftBottom,rightTop,rightBottom,middleTop};
            new UndoIconDecorator(views);
            break;
        default:
            break;
        }
    }
    
    // you may define specific decorators to your convenience... 
    // ... or you may use directly the universal decorator IconDecorator(...). 
    // It is up to your. Here i'm showing both examples
    
    public class WarningIconDecorator{
        public WarningIconDecorator(View view){
            new IconDecorator(MainActivity.this, view, R.drawable.warning_obj);
        }
    }
    
    public class ErrorIconDecorator {
        public ErrorIconDecorator(View view){
            new IconDecorator(MainActivity.this, view, R.drawable.error);
        }
    }
    
    public class InfoIconDecorator {
        public InfoIconDecorator(View view){
            new IconDecorator(MainActivity.this, view, R.drawable.information);
        }
    }
    
    public class PackageDecorator {
        public PackageDecorator(View view){
            new IconDecorator(MainActivity.this, view, R.drawable.package_obj);
        }
    }
    
    public class QuickFixIconDecorator {
        public QuickFixIconDecorator(View view){
            new IconDecorator(MainActivity.this, view, R.drawable.quickfix_error_obj);
        }
    }
    
    public class ErrorWarningDecorator {
        public ErrorWarningDecorator(View view){
            new IconDecorator(MainActivity.this, view, R.drawable.errorwarning_tab);
        }
    }
    
    public class UndoIconDecorator{
        public UndoIconDecorator(View[] views){
            new ClearIconDecorator(MainActivity.this, views);
        }
    }
}
So that's all. Hope you like it.
 

Thursday, April 3, 2014

How to create an ImageLoader for a huge amount of images on Android

Hi there!

During development with a huge amount of images, most of all image loaders available out there, does not display(caches) the images smoothlty. For this reason, i have tryed to put all the code "pieces" i found in the internet together to get a almost perfect image loader for my application. The result is shown bellow. It works very well for me. It may be also interesting for other developers. Here is my solution:

The Loader interface

/**
 * Drawable loader used to create and cache thumbnails while presenting it in a gallery preview.
 * IMPORTANT: instantiate it in your main activity like this:
 * 

 * protected void onCreate(final Bundle savedInstanceState) {
 *       super.onCreate(savedInstanceState);
 *       ...
 *       DrawableLoader.getInstance(this);
 *       ...
 *   }
 * 
 * 
* @author Ricardo Ferreira * @version 1.0 * @since 03/04/2014 */ public interface Loader { /** * Clears all instance data and stops running threads */ public void Reset() ; /** * Loads a drawable into an image. Call this method inside of getView(...) from your concrete * implementation of BaseAdapter. *
     * public View getView(int position, View convertView, ViewGroup parent){
     * ...
     * String url = PATH_FILE + pathImage;// path to your storage like "/storage/..."
     * ImageView imageView = (ImageView)holder.imgView;
     * final Drawable placeholder = mContext.getResources().getDrawable(R.drawable.placeholder);
     * DrawableLoader.getInstance(mContext).loadDrawable(url, imageView, placeholder);
     * ...
     * }
     * 
* @param url file path to the storage location * @param imageView the image to be presented in a list view or grid view * @param placeholder the place holder image to be shown, while the loading is running. */ public void loadDrawable(final String url, final ImageView imageView, Drawable placeholder); }

The Loader implementation

/**
 * Drawable loader used to create and cache thumbnails while presenting it in a gallery preview.
 * IMPORTANT: instantiate it in your main activity like this:
 * 

 * protected void onCreate(final Bundle savedInstanceState) {
 *       super.onCreate(savedInstanceState);
 *       ...
 *       DrawableLoader.getInstance(this);
 *       ...
 *   }
 * 
 * 
* @author Ricardo Ferreira * @version 1.0 * @since 03/04/2014 */ @SuppressLint("HandlerLeak") public class DrawableLoader implements Loader{ private final Map < String, SoftReference < Drawable > > mCache = new HashMap < String, SoftReference < Drawable > >(); private final LinkedList < Drawable > mChacheController = new LinkedList < Drawable > (); private ExecutorService mThreadPool; private final Map < ImageView, String > mImageViews = Collections.synchronizedMap(new WeakHashMap < ImageView, String >()); public final static int MAX_CACHE_SIZE = 1024; public final int THREAD_POOL_SIZE = 5; private Context mContext; private static Loader loader; // SINGLETON private DrawableLoader(Context context) { mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE); mContext=context; } public static Loader getInstance(Context ctx){ if(loader==null){ loader = new DrawableLoader(ctx); } return loader; } public void Reset() { ExecutorService oldThreadPool = mThreadPool; mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE); oldThreadPool.shutdownNow(); mChacheController.clear(); mCache.clear(); mImageViews.clear(); } public void loadDrawable(final String url, final ImageView imageView, Drawable placeholder) { mImageViews.put(imageView, url); Drawable drawable = getDrawableFromCache(url); if (drawable != null) { imageView.setImageDrawable(drawable); } else { imageView.setImageDrawable(placeholder); queueJob(url, imageView, placeholder); } } private Drawable getDrawableFromCache(String url) { if (mCache.containsKey(url)) { return mCache.get(url).get(); } return null; } private synchronized void putDrawableInCache(String url, Drawable drawable) { int chacheControllerSize = mChacheController.size(); if (chacheControllerSize > MAX_CACHE_SIZE){ mChacheController.subList(0, MAX_CACHE_SIZE / 2).clear(); } mChacheController.addLast(drawable); mCache.put(url, new SoftReference(drawable)); } private void queueJob(final String url, final ImageView imageView, final Drawable placeholder) { /* Create handler in UI thread. */ final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { String tag = mImageViews.get(imageView); if (tag != null && tag.equals(url)) { if (imageView.isShown()) if (msg.obj != null) { imageView.setImageDrawable((Drawable) msg.obj); } else { imageView.setImageDrawable(placeholder); } } } }; mThreadPool.submit(new Runnable() { @Override public void run() { final Drawable bmp = downloadDrawable(url); // if the view is not visible anymore, //the image will be ready for next time in cache if (imageView.isShown()) { Message message = Message.obtain(); message.obj = bmp; handler.sendMessage(message); } } }); } private Drawable downloadDrawable(String url) { final Bitmap decodeBitmap = ViewHelper.decodeBitmap(url.replaceAll("file://", ""), 100); Drawable drawable = new BitmapDrawable(mContext.getResources(), decodeBitmap); putDrawableInCache(url, drawable); return drawable; } /** pass new URL(urlString) to it, while urlString is the path to the drawable like: * http://www.google.com.br/images/your_image.png */ protected Drawable downloadDrawable(URL url) { try { InputStream is = getInputStream(url); Drawable drawable = Drawable.createFromStream(is, url.getPath()); putDrawableInCache(url.getPath(), drawable); return drawable; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } private InputStream getInputStream(URL url) throws MalformedURLException, IOException { //URL url = new URL(urlString); URLConnection connection; connection = url.openConnection(); connection.setUseCaches(true); connection.connect(); InputStream response = connection.getInputStream(); return response; } }

That's all. Hope you like it! ;)

Tuesday, April 1, 2014

How to extract ARGB components from color int value

Hi there!

Today i'm gonna show how to extract the color's component Alpha, Red, Green and Blue from an integer value that presents a specific color. The code is very simple and self-explanatory.

/**
     * Returns the color components as alpha, red, green and blue parts in an array.
     * 

     * argb[0] = alpha value
     * argb[1] = red value
     * argb[2] = green value
     * argb[3] = blue value
     * 
* @param color the color value * @return the array containing the color components */ public static int[] getARGB(int color) { /** * Shift all pixels 24 bits to the right. * Do a logical and with 0x000000FF * i.e. 0000 0000 0000 0000 0000 0000 1111 1111 * You will get the alpha value for the color */ int alpha = (color >> 24) & 0x000000FF; /** * Shift all pixels 16 bits to the right. * Do a logical and with 0x000000FF * i.e. 0000 0000 0000 0000 0000 0000 1111 1111 * You will get the red value for the color */ int red = (color >> 16) & 0x000000FF; /** * Shift all pixels 8 bits to the right. * Do a logical and with 0x000000FF * i.e. 0000 0000 0000 0000 0000 0000 1111 1111 * You will get the green value for the color */ int green = (color >>8 ) & 0x000000FF; /** * Dont do any shift. * Do a logical and with 0x000000FF * i.e. 0000 0000 0000 0000 0000 0000 1111 1111 * You will get the blue value for the color */ int blue = (color) & 0x000000FF; return new int[]{alpha,red,green,blue}; } public static int manipulateAlpha(int color, int newAlpha) { final int[] argb = getARGB(color); return Color.argb(random.nextInt(argb[0]), argb[1], argb[2], argb[3]); }