Android: Change center of linear GradientDrawable via code - android

EDIT: LOOK AT SOLUTION ABOVE
i m freaking out. all i just want to do, is setting a linear GradientDrawable, which changes the vertical center of the gradient... drawing the gradient works fine, but how can i change the center of it?!?
RelativeLayout bgScreen = (RelativeLayout) findViewById(R.id.player_screen);
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[] {startColor,endColor});
gd.setCornerRadius(0f);
gd.setAlpha(200);
bgScreen.setBackground(gd);
public void redrawOrChangeBackgroundGradient(){
//??? either change center of existing
gd.setGradientCenter(float x, float y) //ONLY works in RADIAL_GRADIENT or SWEEP_GRADIENT.
//??? or complete redraw Gradient with different center
}
here s a picture example of how i want to change the gradient via code
cannot be that hard, can it?

The lacking ability to set the center programmatically for linear GradientDrawables is a registered issue already.
But there is a workaround described here. Basically, you should create a PaintDrawable from a LinearGradient and set it as your view's background drawable. Following this solution, you can set the center in your LinearGradient constructor by mapping the colors to the positions array:
float[] positions
May be null. The relative positions [0..1] of each corresponding color
in the colors array. If this is null, the colors are distributed
evenly along the gradient line.
(not tested, but it should do the trick for you)

Related

Drawing a gradient on a path

I am using a library for a circular seek bar.
What I want to achieve is attached in an image.
I was trying to -
1. Draw the colors with a LinearGradient. The colors are not distributed properly.
// The positions are from the circle rect and hardcoded for this sample.
protected int[] mGradientColors = {
0xFFF15155, // red
0xFFFBB846,// yellow
0xFF19B6F1, // blue
};
protected LinearGradient mLineargradient = new LinearGradient(-320.5F
, -320.25F
, 320.5F
, 320.25F
, mGradientColors, null, Shader.TileMode.CLAMP);
I also have a separate image (PNG) of the gradient.
Tried extracting Colors from the image using the Palette API, but I get individual colors from the Swatch objects. Not sure how to convert them to a gradient.

setCornerRadius not working on Android 4.2.2

I have implemented pinch to zoom on a ViewGroup whose child Views have background drawables. Upon onScaleEnd, I redraw child Views at the new scale so they're more crisp. However, after re-drawing at smaller scales the rounded corners radii are too big, giving the rectangle shape the wrong appearance. I have code that works for changing the corner radius for all the shapes that works fine on my Kindle Fire HD (based on 4.0.3), but does nothing on my Galaxy Nexus (4.2.2) phone.
Note: I do NOT mutate the drawable because I do want all the drawables based on that resource to change their appearance when I do this.
I've looked at: change corner radius of drawable programmatically
and several other posts, but nothing even hints at different behaviors across devices.
Here's the code:
final LayerDrawable layerDrawable = (LayerDrawable) context.getResources().getDrawable(drawableId);
final Drawable drawable = layerDrawable.findDrawableByLayerId(drawableLayerId);
if (drawable instanceof GradientDrawable) {
final float newRadius = nodeScale > SMALL_TREE_NODE_LAYOUT_THRESHOLD ? normalRadius : smallRadius;
// this doesn't work
((GradientDrawable) drawable).setCornerRadius(newRadius);
// this doesn't work either
((GradientDrawable) drawable).setCornerRadii(new float[] {newRadius, newRadius, newRadius, newRadius, newRadius, newRadius, newRadius, newRadius });
}
Is this a bug in 4.2.2?
To get around this, I finally just wrote the code to re-create the Drawables when I need them. This was needed anyway to change all the corner radii, shrink/grow number of shadow rects, etc.

Setting a gradient background color for surfaceview

At the moment i have a static plain blue background color on my surfaceview, I wanted to know a way to get a blue gradient from deep blue to light blue vertically. Currently I set the background color like this
canvas.drawRGB(15,03,175);
Any tips or advice will be appreciated, thank you.
Found my answer in the form of paint's set shader, this works nicely for the moment
Paint gradPaint = new Paint();
gradPaint.setShader(new LinearGradient(0,0,0,getHeight(),Color.BLUE,Color.CYAN,Shader.TileMode.CLAMP));
canvas.drawPaint(gradPaint);
//Where getHeight() is the height of the canvas

How to set combined color for overlapped area of two different color objects?

I am creating different custom views with two different colors. According to my app features user will drag those objects on the screen, when dragging objects will overlap each other. I want to differentiated the overlapped area, how to set the combined color for overlapped Area.
Look at the below image. Here I am using canvas for creating those custom views two circles are two different views.
EDIT: If I use opacity 128 I am able to see the background color, but I want the combination color of overlapped objects colors.
The Color mixing you are looking for is sometimes called Intuitive Color Mixing, or the RYB Color System:
RYB:
CC license
A quote from this paper by Nathan Gossett and Baoquan Chen on algorithms for intuitive color mixing summarizes how the intuitive color system works:
"In this model, Red, Yellow and Blue are used as pure primary colors. Red and Yellow mix to form Orange, Yellow and Blue mix to form Green, and Blue and Red mix to form Purple [...]. These are the colors an untrained viewer would expect to obtain using children's paint [...]. In addition, many people do not think of White as the mixture of all colors, but instead as the absence of color (a blank canvas). A more common assumption would be that mixing many colors together would result in a muddy dark brown color."
RYB is not implemented in Android's blend modes and can't really be simulated by mixing alpha/different blend modes.
Most computer graphics applications make use of the RGB or CMYK color spaces:
CMYK:
CC license
CMY is based on subtractive color. Subtractive color mixing means that, starting with white, as we add color, the result gets darker. CMYK is used for mixing colors in images intended for printing in e.g. Photoshop and Illustrator.
RGB:
CC license
RGB is based on Additive Color. The colors on a computer screen are created with light using the additive color method. Additive color mixing begins with black and as more color is added, the result gets lighter and ends in white.
This site discusses CMYK and RGB in more detail.
In neither RGB nor CMYK does mixing blue and yellow produce green, or generally speaking, intuitive color mixes. To implement a RYB color system on Android would be quite involved. The paper by Nathan Gossett and Baoquan Chen quoted above proposes a solution with an algorithm implemented in C at the very end of the paper. This algorithm could be implemented in a custom blend on Android. Drawable.setColorFilter() uses PorterDuffColorfilter which extends ColorFilter. Subclassing ColorFilter as discussed in this SO question would have to be done in native code using the NDK.
CMYK Color Mixing Workaround
In case you are interested in using a CMYK color mixing as a workaround, I've included a basic example of how it can be done below. In this example, A cyan-color circle and a yellow-color circle will be blended to produce a green intersection.
Starting with these .png image files created in Adobe Illustrator:
with hex color value 0x00ffff (cyan) and 0xffff00 (yellow).
Add them to your drawable folder with names cyancircle.png and yellowcircle.png.
Configure your main.xml layout as follows:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#ffffff"
android:padding="30dp">
<ImageView
android:id="#+id/bluecircle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/cyancircle">
</ImageView>
<ImageView
android:id="#+id/yellowcircle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/yellowcircle"
android:layout_marginTop="30dp">
</ImageView>
</RelativeLayout>
Create your Activity:
import android.app.Activity;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.widget.ImageView;
public class PorterDuffTestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView yellowCircle = (ImageView)findViewById(R.id.yellowcircle);
yellowCircle.getDrawable().setColorFilter(0x88ffff00, PorterDuff.Mode.MULTIPLY);
}
}
Output:
The limitation to this method is that the top shape's alpha has to be set to 50% (the "88" in "0x88ffff00"). For yellow this works reasonable well but for other colors the alpha effect may not be acceptable (the color may appear to be another color, e.g. red becomes pink with low alpha values on white background). Which blend mode is ultimately acceptable for you depends on the set of colors you are going to use and will take some experimentation. Also note the background color may affect the circles' colors in blend mode. In this example the background is set to white.
I have done another example for 6 objects.
Key Points:
onDraw method will not be override for Object Views, and background also will set to transparence
setBackgroundColor(Color.TRANSPARENT);
But, the onDraw method will be renamed as onDrawEx will be called from Overlay View.
public void onDrawEx(Canvas canvas) {
Overlay view will pass a custom canvas to draw into. before pass to object view it will do necessary translate.
mOverlayView = new View(this){
#Override
protected void onDraw(Canvas canvas) {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
Canvas canvasBitmap = new Canvas(bitmap);
ViewGroup viewGroup = (ViewGroup)getParent();
for(int i = 0 ; i < viewGroup.getChildCount()-1;i++){
ObjectView objectView = (ObjectView) viewGroup.getChildAt(i);
canvasBitmap.save();
canvasBitmap.translate(objectView.getTranslationX(), objectView.getTranslationY());
objectView.onDrawEx(canvasBitmap);
canvasBitmap.restore();
}
canvas.drawBitmap(bitmap, 0, 0, new Paint());
}
};
use the mPaint.setXfermode(new PorterDuffXfermode(Mode.ADD)); to add colors. But all objects should use colors like 0xFF000030,0xFF0000C0,0xFF003000,0xFF00C000,0xFF300000,0xC00000 then only for all posible overlapping we can get different colors. this is depends on you max number of object.
int k = 0 ;
for(int i = 0 ; i < 2;i++,k++){
int color = 0xFF000000|(0x000030<<i*2);
frameLayout.addView(new ObjectView(this,color,k*50,k*50,k), new FrameLayout.LayoutParams(50, 50));
}
for(int i = 0 ; i < 2;i++,k++){
int color = 0xFF000000|(0x003000<<i*2);
frameLayout.addView(new ObjectView(this,color,k*50,k*50,k), new FrameLayout.LayoutParams(50, 50));
}
for(int i = 0 ; i < 2;i++,k++){
int color = 0xFF000000|(0x300000<<i*2);
frameLayout.addView(new ObjectView(this,color,k*50,k*50,k), new FrameLayout.LayoutParams(50, 50));
}
Here I have modified to support version 8
use
mPaint.setXfermode(new PixelXorXfermode(0x00000000));
for
mPaint.setXfermode(new PorterDuffXfermode(Mode.ADD));
I used layout parameter for translation.
The simplest solution I can think would be to simply use the alpha channel by setting each to 0.5 opacity, either in code or xml. Then the colours should fade into each other when they overlap. This does the mean the colours in the non-overlapping sections will be a little faded, though, and depending on what background you have behind the shapes it may not look good to have them be at all transparent.
I have created a sample Activity with two views
DemoDrawShapeActivity where in the view2 i use canvas.clipPath and canvas.translate
to work this i set minimum sdk version 4
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
path2.addCircle(getWidth()/2, getHeight()/2, getWidth()/2, Direction.CCW);
canvas.drawPath(path2, paint);
canvas.clipPath(path2);
canvas.save();
canvas.translate(-getTranslationX()+view1.getTranslationX(), -getTranslationY()+view1.getTranslationY());
paint.setColor(Color.BLUE|Color.RED);
canvas.drawPath(path1, paint);
canvas.restore();
}
you can edit paint.setColor(Color.BLUE|Color.RED); to get necessary color according to your logic.
i am using the setTranslationX setTranslationY to move the views.
public boolean onTouchEvent(MotionEvent event) {
switch(event.getActionMasked()){
case MotionEvent.ACTION_UP:
touched[0]=touched[1]=false;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if(touched[1]){
view2.setTranslationX(event.getX()-view2.getWidth()/2);
view2.setTranslationY(event.getY()-view2.getHeight()/2);
}else if(touched[0]){
view1.setTranslationX(event.getX()-view1.getWidth()/2);
view1.setTranslationY(event.getY()-view1.getHeight()/2);
view2.invalidate();
}
}
return true;
}
I think you may be looking to draw in a blend mode.
Android will let you do this, just look at this first link.
Composite operations in Android Canvas
Here are all the compositing options
http://developer.android.com/reference/android/graphics/PorterDuffXfermode.html
And their explanations from Mozilla
https://developer.mozilla.org/en/Canvas_tutorial/Compositing
You may take the help from this -
http://www.learnopengles.com/android-lesson-five-an-introduction-to-blending/
and
www.pushing-pixels.org/category/android/page/6

Using a gradient along a path

I'm trying to create a 'glow' effect using the Android Path class. However, the gradient is not being warped to fit around the path. Instead, it is simply being display 'above' it and clipped to the path's stroke. Using a square path, the image below shows what I mean:
Instead, that should look more like this:
In other words, the gradient follows the path, and in particular wraps around the corners according to the radius set in the CornerPathEffect.
Here is the relevant part of the code:
paint = new Paint();
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(20);
paint.setAntiAlias(true);
LinearGradient gradient = new LinearGradient(30, 0, 50, 0,
new int[] {0x00000000, 0xFF0000FF, 0x00000000}, null, Shader.TileMode.MIRROR);
paint.setShader(gradient);
PathEffect cornerEffect = new CornerPathEffect(10);
paint.setPathEffect(cornerEffect);
canvas.drawPath(boxPath, paint);
Any ideas?
Another alternative is to get a 'soft-edged brush' effect when defining the stroke width. I've experimented with BlurMaskFilters, but those give a uniform blur rather than a transition from opaque to transparent. Does anyone know if that's possible?
How about drawing with a soft brush bitmap? Make a soft circular brush with opacity decreasing radially outward using image editing software like Photoshop. Save as drawable, load it in a bitmap and draw it evenly spaced along your path. Make the bitmap with white coloured brush. This way you can simply multiply the given colour(Here blue) to your bitmap using PorterDuffColorFilter.
brush1=BitmapFactory.decodeResource(getResources(), R.drawable.brush_custom_one);
//This contains radially decreasing opacity brush
porter_paint.setColorFilter(new PorterDuffColorFilter(paint.getColor(), Mode.MULTIPLY));
for (int i=1;i<matrix.size();i++) {
//matrix contains evenly spaced points along path
Point point = matrix.get(matrix.get(i));
canvas.drawBitmap(brush1, point.x,point.y, porter_paint);}
The brush used is (It's there):
The final result is:
Turns out there was a stupidly obvious way of doing this. Simply re-use the same path, and adjust the stroke width and alpha on each drawing pass. Example code:
float numberOfPasses = 20;
float maxWidth = 15;
for (float i = 0; i <= numberOfPasses; i++){
int alpha = (int) (i / numberOfPasses * 255f);
float width = maxWidth * (1 - i / numberOfPasses);
paint.setARGB(alpha, 0, 0, 255);
paint.setStrokeWidth(width);
canvas.drawPath(path, paint);
}
See below for an example of the result. The left path was drawn using this method, the right path, for comparison, is drawn in a single stroke with maxWidth and 255 alpha.
This mainly works. There are two problems:
The gradient isn't as smooth as it could be. This is because each pass being drawn over the previous one results in the alpha building up too quickly, reaching 255 before the final strokes. Experimenting a bit with the line int alpha = (int) (i / numberOfPasses * 125f); (note the change to 125f rather than 255f) helps.
The path looks like it has been 'cut' on the insides of the corners. Probably some result of the CornerPathEffect applied.
What you're wanting to do, if I understand it right, is to have the gradient effectively form a "brush" for the stroke.
This is exactly what I also was trying to achieve recently, but as far as I can tell the API doesn't provide any straightforward means to do it. I have recently created an SVG to Android Canvas converter class and so I am working a lot in Inkscape lately, too. So, when I was looking into it, I wondered if it's even possible to do it in Inkscape. However, even in Inkscape it's a very non-trivial thing to do. After some searching I eventually came across this image of a gradient being applied along the course of a path, together with a download link for a tutorial beneath:
http://www.flickr.com/photos/35772571#N03/3312087295/
What I was personally trying to do at the time was to create some semi-circles where the path is a kind of neon glow as opposed to a flat colour. Talking in terms of both the Android API and the SVG standard, it seems that the only way to to do this is to create a radial gradient that's centred perfectly on the circle, and position a series of color stops in exactly the right places. Pretty tricky to do, and I certainly don't know how you'd do it to a shape like a square.
Sorry that this is a bit of a 'I couldn't do it either' rather than a useful answer! I'll follow this with interest as I'm eager to know a solution for a kind of 'soft brush' effect too.
Can be very complicated to draw a gradient than follow a path.
So I suggest you to use some library already done than make it for you.
One can be Sc-Gauges.
Have some usefully classe than you can use for your goal.
For first include the library:
dependencies {
...
compile 'com.github.paroca72:sc-gauges:3.0.7'
}
After create an image or what you want with a canvas where draw:
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Now the code:
// Dimensions
int padding = 24;
Rect drawArea = new Rect(padding, padding, 700 - padding, 500 - padding);
// Get the main layout
ImageView imageContainer = (ImageView) this.findViewById(R.id.image);
assert imageContainer != null;
// Create a bitmap and link a canvas
Bitmap bitmap = Bitmap.createBitmap(
drawArea.width() + padding * 2, drawArea.height() + padding * 2,
Bitmap.Config.ARGB_8888
);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.parseColor("#f5f5f5"));
// Create the path building a bezier curve from the left-top to the right-bottom angles of
// the drawing area.
Path path = new Path();
path.moveTo(drawArea.left, drawArea.top);
path.quadTo(drawArea.centerX(), drawArea.top, drawArea.centerX(), drawArea.centerY());
path.quadTo(drawArea.centerX(), drawArea.bottom, drawArea.right, drawArea.bottom);
// Feature
ScCopier copier = new ScCopier();
copier.setPath(path);
copier.setColors(Color.RED, Color.GREEN, Color.BLUE);
copier.setWidths(20);
copier.draw(canvas);
// Add the bitmap to the container
imageContainer.setImageBitmap(bitmap);
And this the result:
The first part of the code is just for create a bitmap where draw.
What you interest is the second part where use ScCopier.
Just give the path, the color and the with.
Note than is you are inside a view you can use onDraw for draw directly on the view canvas.
This library can used to create gauge of every kind.
If you want take a look to this site ScComponents have some free and not gauges components.

Categories

Resources