DominicM
DominicM

Reputation: 2196

LinearGradient Center Animation

I want to animate the center of a linear gradient, so that at the beginning the whole drawable is color1 and at the end the whole drawable is color2 and in between the center of the gradient moves from left to right.

GradientDrawable gd = new GradientDrawable(
                              GradientDrawable.Orientation.LEFT_RIGHT,
                              new int[] {color1, color2});
gd.setCornerRadius(0f);
gd.setGradientCenter(x, 0);
view.setBackgroundDrawable(gd);

The problem is that setGradientCenter doesn't make any difference. According to this answer https://stackoverflow.com/a/14383974/1395697 there is an issue with setGradientCenter() but the solution in this answer doesn't work for me because I change the gradient in onTouch() when the user swipes his finger over the view, so it needs to be really fast.

Is there a way to do this?

I want to do something like this (all the touch stuff works great but not the animated gradient):

enter image description here

Upvotes: 9

Views: 4147

Answers (2)

Raanan
Raanan

Reputation: 4785

What you ask for isn't a pure linear gradient - a linear gradient between two colors on a fixed width has only one math function defining it and that's why you can't change the center. What I think you are looking for is a fixed color beginning and end, and a linear gradient area between them.

Try this solution:

    sf = new ShapeDrawable.ShaderFactory() {
     @Override
     public Shader resize(int width, int height) {
        LinearGradient lg = new LinearGradient(0, 0, view.getWidth(), 0, new int[] {Color.WHITE, Color.BLACK}, //substitute the correct colors for these
                 new float[] {center - 0.3f, center + 0.3f}, Shader.TileMode.REPEAT);
        return lg;
     }
  };
  p = new PaintDrawable();
  rectShape = new RectShape();
  p.setShape(rectShape);
  p.setShaderFactory(sf);
  view.setBackgroundDrawable((Drawable) p);
  view.setOnTouchListener(new View.OnTouchListener() {

     @Override
     public boolean onTouch(View v, MotionEvent event) {
        center = event.getX() / view.getWidth(); // calculate the center
        p.setShape(rectShape); // this makes the shader recreate the lineargradient
        return true;
     }
  });

This code seems to reacts fast enough to touch, but does create multiple instances of LinearGradient.

Upvotes: 4

Tristan Burnside
Tristan Burnside

Reputation: 2566

You can do something like this with a seekbar.

You will need to set a custom progressDrawable attribute and remove the position marker (thumb) something like this.

<SeekBar 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:progressDrawable="@drawable/gradient_progress"  
    android:thumb="@null"/>

gradient_progress.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background"
            android:drawable="@drawable/right_colour" />
    <item android:id="@android:id/secondaryProgress">
        <scale android:scaleWidth="100%"
                android:drawable="@drawable/left_colour" />
    </item>
    <item android:id="@android:id/progress">
        <scale android:scaleWidth="100%"
                android:drawable="@drawable/left_colour" />
    </item>
</layer-list>

where right_colour is a drawable or colour resource of the right-hand colour (blue) and the left_colour is a 9-patch drawable of the left-hand colour (green) fading to transparent that will stretch only the leftmost edge. Mine looks like this

left_colour.9.png (It is white fading to transparent so hard to see here)

This gives a solution that is fast and doesn't require any touch coding on your part but it does have a drawback, it doesn't quite go right to the far end so the 'linear gradient' portion of the image is still visible even when the user has moved all the way across. If that's not a big problem then this will work well.

Upvotes: 0

Related Questions