Search This Blog

28 December 2015

Shadows for Android applications. Pre Lollipop and post Lollipop (with all API levels)

ShadowViewDecorator:

https://github.com/ektodorov/ShadowViewDecorator

is a project that provides drop shadows for views in Android. It does not use the new APIs introduced in Android Lollipop, which means it can be used in an Android project regardless of the minimum API level used by the application.
There are methods with extensive parameters in the ShadowViewDecorator. We have taken that decision so that the shadow is completely configurable and not just a single implementation of a shadow that we think is appropriate. Every user can make the adjustments that are most appropriate in a use case.
We have provided methods that use CSS box shadow style parameters and we expect these methods to be the most used. This way we don't have to work with all the parameters of a shadow every time. We can use one of the many available online tools for previewing a CSS box shadow and then apply the same settings in an Android application. This also provides us with a quick way to change settings during development without the need to fiddle with graphic assets, because with ShadowViewDecorator the change will be only in code (a few parameters).

16 February 2015

Drop shadows in Android pre Lollipop

Android Lollipop arrived and with it so did "Material" design. One of the new things is that views can cast shadows according to their elevation property. We wanted to simulate this in previous Android OS versions and we went ahead and made a Shadow View Decorator. It draws shadows and glow around views. It also has a compatibility methods which will use the elevation property if the application is running on Android 5+.

Take a look on github:

https://github.com/ektodorov/ShadowViewDecorator

08 February 2015

Java Box Blur (code for Android)

We will quickly cover how to do box blur in Java.
To create a blur effect on an image we have to average a pixel with the data from its neighboring pixels. How much pixels we have to average depends on how big of a blur we want. Most of the time the size of the blur is called a radius, because we think of the area of pixels we want to cover as a circle.
For example if we want a blur with size 1:

1 1 1
1 2 1
1 1 1

"2" marks the location of the current pixel and "1"s mark the position of its neighbors.
One way to get the average of all these pixels is to add their values and then divide that value to the count of the pixels:

(1 + 1 + 1 + 1 + 2 + 1 + 1 + 1 + 1) / 9 = averageValue

Then we set pixel "2" to the "averageValue".

Another way is to use what is called a convolution kernel. For our blur, the kernel would be:

1/9 1/9 1/9
1/9 1/9 1/9
1/9 1/9 1/9

It is a matrix with the size of our blur, and we can imagine we are moving it over every pixel of our image and multiplying the pixel color values with the corresponding kernel value (here they are all the same, and this is called a box blur. In Gaussian blur, the center of the kernel has more weight than the other parts).
For our example we will multiply all the pixels with 1/9 and will assign as color value for pixel at position marked with "2":
1*1/9 + 1*1/9 + 1*1/9 + 1*1/9 + 2*1/9 + 1*1/9 + 1*1/9 + 1*1/9 + 1*1/9

If we use a bigger radius for the blur we need a bigger kernel. What stays the same is that when we add all values of the kernel together they must add up to 1.
1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 = 9/9 = 1

Kernel with radius 3, which gives us a 7x7 matrix:
1/49 + 1/49 ... = 49/49 = 1

Instead of doing a 2D calculation we can split the kernel into two 1D kernels
1/9 1/9 1/9 and 1/9 and multiply each pixel with these kernels separately.
                        1/9
                        1/9










This way we would gain a little bit of speed.
Another way of speeding up is scaling down the image before we apply blur. And then scale it back up or just draw it in a bigger rectangle.
If we are blurring a grey scale image we don't need to multiply each color channel, because they all are the same. Just multiply the alpha and one of the color channels. (we have a note on that in the code).

And here is our code:
1:  public static Bitmap boxBlur(Bitmap aBitmap, int aSize, boolean aCreateNewBitmap) 
2:       { 
3:            float kernel = 1.0f / ((aSize * 2) + 1); 
4:            Bitmap bitmap = null; 
5:            if(aCreateNewBitmap) { 
6:                 bitmap = Bitmap.createBitmap(aBitmap.getWidth(), aBitmap.getHeight(), Config.ARGB_8888); 
7:            } else { 
8:                 bitmap = aBitmap; 
9:            } 
10:             
11:            Rect rectSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
12:            Rect rectDest = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
13:             
14:            Canvas canvas = new Canvas(bitmap); 
15:             
16:            //box blur 
17:            int colsCount = bitmap.getWidth(); 
18:            int rowsCount = bitmap.getHeight(); 
19:             
20:            for(int row = 0; row < rowsCount; row++) { 
21:                 for(int col = 0; col < colsCount; col++) { 
22:                      float sumAlpha = 0; 
23:                      float sumRed = 0; 
24:                      float sumGreen = 0; 
25:                      float sumBlue = 0; 
26:                      for(int k = col - aSize; k <= col + aSize; k++) { 
27:                           int x = k; 
28:                           if(x < 0) {x = Math.abs(x);} 
29:                           if(x >= colsCount) {x = (colsCount - 1) - (x - colsCount);} 
30:                            
31:                           int pixel = bitmap.getPixel(x, row); 
32:                            
33:                           int alpha = Color.alpha(pixel); 
34:                           int red = Color.red(pixel); 
35:                           int green = Color.green(pixel); 
36:                           int blue = Color.blue(pixel); 
37:                                 
38:                           float resultAlpha = (float)alpha * kernel; 
39:                           float resultRed = (float)red * kernel; 
40:                           float resultGreen = (float)green * kernel; 
41:                           float resultBlue = (float)blue * kernel; 
42:                                 
43:                           sumAlpha += resultAlpha; 
44:                           sumRed += resultRed; 
45:                           sumGreen += resultGreen; 
46:                           sumBlue += resultBlue; 
47:                      } 
48:                      bitmap.setPixel(col, row, Color.argb((int)sumAlpha, (int)sumRed, (int)sumGreen, (int)sumBlue)); 
49:                 } 
50:            } 
51:             
52:            for(int col = 0; col < colsCount; col++) { 
53:                 for(int row = 0; row < rowsCount; row++) { 
54:                      float sumAlpha = 0; 
55:                      float sumRed = 0; 
56:                      float sumGreen = 0; 
57:                      float sumBlue = 0; 
58:                      for(int k = row - aSize; k <= row + aSize; k++) { 
59:                           int y = k; 
60:                           if(y < 0) {y = Math.abs(y);} 
61:                           if(y >= rowsCount) {y = (rowsCount - 1) - (y - rowsCount);} 
62:                            
63:                           int pixel = bitmap.getPixel(col, y); 
64:                            
65:                           int alpha = Color.alpha(pixel); 
66:                           int red = Color.red(pixel); 
67:                           int green = Color.green(pixel); 
68:                           int blue = Color.blue(pixel); 
69:                                 
70:                           float resultAlpha = (float)alpha * kernel; 
71:                           float resultRed = (float)red * kernel; 
72:                           float resultGreen = (float)green * kernel; 
73:                           float resultBlue = (float)blue * kernel; 
74:                                 
75:                           sumAlpha += resultAlpha; 
76:                           sumRed += resultRed; 
77:                           sumGreen += resultGreen; 
78:                           sumBlue += resultBlue; 
79:                      } 
80:                      bitmap.setPixel(col, row, Color.argb((int)sumAlpha, (int)sumRed, (int)sumGreen, (int)sumBlue)); 
81:                 } 
82:            } 
83:                       
84:            canvas.drawBitmap(bitmap, rectSrc, rectDest, null); 
85:            return bitmap; 
86:       } 


01 February 2015

Hybrid application

For most of us a hybrid application is equal to a bad thing. You end up getting neither a native snappy experience, nor the comfort of changing content and style overnight like when you are using a web application.
I say it doesn't have to be that way.
If we can answer a few questions we would  know if we need a web application, native application or a hybrid application.

Do we have a web application/site which is responsive/touch friendly. (touch friendly is a term I like to use and which I will explain in a separate post, but in short by "touch friendly" I mean a web page that is easy to use on a device which uses a touch interface/screen) ?
Do we want a presence in the relevant app store of the target platform ?
Do we have the time and resources to develop a native application ?

My opinion is that hybrid frameworks like PhoneGap and even Xamarin which produces a real native code are inferior to native programming. But if you already have the content ready and made in a responsive/touch friendly web pages why not reuse some of it. And you can do that with the approach of Hybrid Framework for HTML
(available for iOS and Android. The android version intentionally does not use Java injection just to be compatible with the iOS version and to support a single API for both platforms).
With this framework you can have an application developed in native code and take advantage of the content that you already have developed in HTML/JavaScript. You don't have to develop this content again in native code/interface development tool of the platform. We use our hybrid framework as the glue which connects Java/Objective-C code to our JavaScript code. We save time and not double or triple the work for creating a content if it is already available in HTML pages. But we do take the advantage that native developers have for the platform we are developing. Lets face it HTML/JavaScript is code that is run in a browser, that is the platform of that technology.
It doesn't matter even if a great part of the application is developed in native code, there still is that bit that doesn't need to be native and makes sense to reuse or just use HTML formatting. It is good, proven technology for defining content and presenting it in a beautiful way.