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 example in 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.
Reference: | Applying decorator pattern to decorate views on Android from our JCG partner Ricardo Ferreira at the Clean Code Development – Quality Seal blog. |
I was wondering if you could give some images to show what you just made…