Is there a simple way to just set the transparency of the content (children) of a ViewGroup?
Background: I got a custom FrameLayout with a bunch of different views in it. Now I want to fade out only the content/children:
The backgroundColor of the layout should always be visible - so I cannot just setAlpha() on the whole ViewGroup
I do not want to loop through all the children and call child.setAlpha() on every one of them manually
Current Approach
It's working but I'm not completely satisfied with it - I think there is a simpler solution.
Override dispatchDraw() and just draw a rectangle above all content:
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
// Draw the alpha overlay above the children.
if (mGridAlpha < 1.0f) {
mOverlayPaint.setColor(adjustAlpha(mOverlayPaint.getColor(), 1 - mGridAlpha));
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mOverlayPaint);
}
}
public static int adjustAlpha(int color, float factor) {
int alpha = Math.round(255 * factor);
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(alpha, red, green, blue);
}
Fading in/out is done manually with a ValueAnimator:
public void fadeContent(final boolean in) {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mGridAlpha = in ? fraction : 1 - fraction;
// Key here is to call invalidate() which will cause a dispatchDraw() call
invalidate();
}
});
animator.start();
}
Related
I have a library that shows a camera preview. I want to add a rectangle overlay on top of the preview. I tried 2 different approaches. But both of them display the rectangle shortly then disappear.
approach (using view.getOverlay)
mPreview.setZOrderMediaOverlay(true);
mPreview.setZOrderOnTop(true);
ViewGroup rootView = (ViewGroup)mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
rootView.addView(mPreview);
final ViewOverlay overlay = mPreview.getOverlay();
final Bracket bracket = new Bracket();
mPreview.post(new Runnable() {
#Override
public void run() {
bracket.setBounds(0, 0, mPreview.getWidth(), mPreview.getHeight());
overlay.add(bracket);
}
});
approach (overriding draw function in surfaceview)
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.GREEN);
paint.setStrokeWidth(10);
//center
int x0 = canvas.getWidth()/2;
int y0 = canvas.getHeight()/2;
int dx = canvas.getHeight()/3;
int dy = canvas.getHeight()/3;
//draw guide box
canvas.drawRect(x0-dx, y0-dy, x0+dx, y0+dy, paint);
}
mPreview extends SurfaceView
Bracket extends Drawable
UPDATE
If I use setContentView instead of rootView.addView it works as expected. But in this case I can not remove the view.
I used a new Activity and setContentView. That solved the problem.
To remove the view I finished the action.
In android 5.0 i am trying to work with circular reveal animation
Problem
When i click on button to start reveal animation, on first click animation doesn't start
Second Click onwards it works normally
My Code
public class MainActivity extends ActionBarActivity {
Animator a;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View cardType = findViewById(R.id.cardtype);
cardType.setVisibility(View.GONE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
a = ViewAnimationUtils.createCircularReveal(cardType,
cardType.getWidth(),
cardType.getHeight(),
0,
cardType.getHeight() * 2)
.setDuration(2500);
a.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
cardType.setVisibility(View.VISIBLE);
}
});
a.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
cardType.setVisibility(View.GONE);
}
});
findViewById(R.id.icon_first_activity).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
a.start();
}
});
}
}}
I haven't tried your code, but I think you have a small ordering problem. I think you just need to set the cardType visible before you start the animation.
Edited to add:
... and you should be setting your button View.INVISIBLE, not View.GONE.
Here: This code works.
Edited once more to add:
Yes. Your problem is that you set the view GONE initially. That means it has 0 size. Then you use cardType.getHeight and cardType.getWidth as reveal coordinates. They are 0. You are going to want to set the view INVISIBLE, initially, and then use width/2 and height/2 as the center of the reveal.
Basically what others answers say, it's correct, but the problem is if you want visibility GONE (because your layout requires it GONE!) you have to set visibility INVISIBLE in the xml with height 0dp (and/or width 0dp as well) and programmatically set the correct LayoutParams even inside the click event it will work. For example my code:
...
expandButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//To not have empty scroll, the container is INVISIBLE with 0dp height.
//Otherwise the Reveal effect will not work at the first click.
//Here I set the parameters programmatically.
viewContainer.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
if (viewContainer.getVisibility() == View.VISIBLE) {
expandButton.animate().rotation(0f).setDuration(duration).start();
Utils.unReveal(viewContainer, 0, 0);
} else {
expandButton.animate().rotation(180f).setDuration(duration).start();
Utils.reveal(viewContainer, viewContainer.getWidth(), 0);
}
}
});
...
#TargetApi(VERSION_CODES.LOLLIPOP)
public static void reveal(final View view, int cx, int cy) {
if (!hasLollipop()) {
view.setVisibility(View.VISIBLE);
return;
}
//Get the final radius for the clipping circle
int finalRadius = Math.max(view.getWidth(), view.getHeight());
//Create the animator for this view (the start radius is zero)
Animator animator =
ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
//Make the view VISIBLE and start the animation
view.setVisibility(View.VISIBLE);
animator.start();
}
#TargetApi(VERSION_CODES.LOLLIPOP)
public static void unReveal(final View view, int cx, int cy) {
if (!hasLollipop()) {
view.setVisibility(View.GONE);
return;
}
//Get the initial radius for the clipping circle
int initialRadius = view.getWidth();
//Create the animation (the final radius is zero)
Animator animator =
ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius, 0);
//Make the view GONE when the animation is done
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setVisibility(View.GONE);
}
});
//Start the animation
animator.start();
}
If you set only GONE in the xml, the first time will never work because height/width/x/y/etc.. are 0. Also, if you just set INVISIBLE before the call to the animation it will not work as well, but if you start with visibility INVISIBLE it will initialize the layout params.
what i did is, Like i have two view with same height,As we now visibility gone returns 0 {height and width} than i am giving visible view height every time and its work for me.
The solution is don't get values directly into code
Either put the animation code on click and the values outside onclick
or get the values from other activity
By values i mean cardType.getWidth() and cardType.getHeight()
Every time onDraw() is called I am drawing a series of points on the canvas. How do I animate one point so that it changes colour or fades in and out? So essentially goes from orange->red and back or opaque->transparent and back?
I am doing the following:
public void onDraw(Canvas canvas) {
drawDots();
}
private void drawDots() {
canvas.drawCircle(xcoord, ycoord, 20, getPaintObj(param));
}
private Paint getPaintObj(int param) {
if (param % 2 == 0) {
ObjectAnimator colorFade = ObjectAnimator.ofObject(paintObj, "color", new ArgbEvaluator(), 0xff00ff00, 0xffff0000, 0xff0000ff);
colorFade.setDuration(2000);
colorFade.setInterpolator(new LinearInterpolator());
colorFade.setRepeatCount(ValueAnimator.INFINITE);
colorFade.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
invalidate();
}
});
colorFade.start();
return paintObj;
} else {
return otherPaintObj;
}
}
The problem is that it doesn't animate. It sticks to the first colour (0xff00ff00). What am I doing wrong?
ObjectAnimator doesn't work very well with the canvas as I was trying to animate a circle drawn on the canvas itself. Works better for actual objects (TextView, ImageView, etc.) rather than points drawn on canvases.
The solution I used is redrawing the canvas every once in a while and modifying the radius of the circle drawn to simulate an animation.
I have an ActionBar icon (the main one on the left, not an action item) that I would like to animate.
I am setting my ActionBar's icon in my Activity like this:
getSupportActionBar().setIcon(icon)
where icon is a Drawable produced by a library that converts a custom XML view into a bitmap. This XML view is a RelativeLayout with a background image and a TextView on top.
Today, when I have to update the TextView I simply re-generate the icon and call setIcon again. Instead, I would like to get a hold of my TextView and apply some animation effect on it, like fade-out and then fade-in after updating it (maybe never having to call setIcon, just re-use the same one).
Not sure how to go about this. Can someone recommend an approach?
EDIT: trying this approach:
In MyActivity:
Drawable myDrawable = new MyDrawable();
supportActionBar.setIcon(myDrawable);
and:
public class MyDrawable extends Drawable {
private Paint paint;
private RectF rect;
public MyDrawable() {
this.paint = new Paint();
this.rect = new RectF();
}
#Override
public void draw(Canvas canvas) {
paint.setARGB(255, 0, 255, 0);
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.FILL);
rect.right = 20f;
rect.bottom = 20f;
canvas.drawRoundRect(rect, 0.5f, 0.5f, paint);
}
#Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}
Nothing shows up. I verified that onDraw gets called. Something suspicious to me is that canvas has both height & width set to 1.
A proper approach for it would be to forget the XML layout and create a custom Drawable.
An instance of this custom drawable will be set to the icon on the ActionBar and call invalidateSelf() whenever necessary to redraw (due to animation, for example).
The drawable can hold reference to other drawables (e.g. BitmapDrawable to have something from the /res/ folder or a Color or Gradient drawable for a background shade) and call (for example) bgDraw.draw(canvas) during the onDraw callback.
It can also draw stuff directly on the canvas that is given to it during onDraw callback. With the canvas you can draw circle, lines, areas, path and text directly on it.
edit:
very simple animation example (didn't check the code, likely typos):
private long animationTime;
public void doAnimation(){
animationTime = System.currentTimeMilis() + 3000; // 3 seconds
invalidateSelf();
}
public void onDraw(Canvas canvas){
// do your drawing.
// You can use difference between
// currentTimeMilis and animationTime for status/position
...
// at the end
if(animationTime > System.currentTimeMilis())
invalidateSelf();
}
Is there any way to animate a cropping of an ImageView?
Say for example, the ImageView is 720 x 480. I want to chop off the bottom rows of pixels with an animation until the ImageView is completely gone. I have only been able to move the image up when the onclicklistener is enabled, and make it transparent, which is ok, but not what the designer asked for.
ValueAnimator anim = ValueAnimator.ofInt(myImageView.getMeasuredHeight(), 0);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = myImageView.getLayoutParams();
layoutParams.height = val;
myImageView.setLayoutParams(layoutParams);
}
});
anim.setDuration(1000);
anim.start();
I got code from here and just changed it to height: ObjectAnimator animate LinearLayout width
Assuming you only want to chop off the height, using ObjectAnimator will work out to create such a custom cropped-height animation,
First of all you need to specify getHeight() and setHeight() methods and then create a custom animation with ObjectAnimator.
public float getHeight() { ... }
public void setHeight(float h) { ... }
ObjectAnimator heightAnim= ObjectAnimator.ofFloat(yourImageView, "height", heightBegin, heightEnd);