I would like to apply the same alphaanimation for button A, B and C, and therefore implemented the following codes:
public void onCreate(Bundle savedInstanceState)
{
animation = new MutableAlphaAnimation();
animation.setAnimationListener(this);
btn_A.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
animation.setResetBlocked(true);
btn_A.setAnimation(animation);
animation.setResetBlocked(false);
animation.start(0.0f, 0.5f, FADE_IN_DURATION);
btn_A.invalidate();
}
});
}
public class MutableAlphaAnimation extends Animation
{
private float mFromAlpha;
private float mToAlpha;
private boolean resetBlocked;
public MutableAlphaAnimation()
{
}
public void start(float fromAlpha, float toAlpha, long duration)
{
mFromAlpha = fromAlpha;
mToAlpha = toAlpha;
setDuration(duration);
setStartTime(START_ON_FIRST_FRAME);
}
public void setResetBlocked(boolean resetBlocked)
{
this.resetBlocked = resetBlocked;
}
#Override
public void reset()
{
if (! resetBlocked) super.reset();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
final float alpha = mFromAlpha;
t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}
#Override
public boolean willChangeTransformationMatrix()
{
return false;
}
#Override
public boolean willChangeBounds()
{
return false;
}
}
Question:
btn_A, B and C are implemented with the same setOnClickListener. However, when they are pressed, nothing happen. Why? How can the above be modified?
Thanks!
You aren't applying your transformation to btn_home. Yes you set the it to the animation, but your animation class never touches btn_home. Your t.setAlpha... should really be btn_home.setAlpha..., so pass in the view using the constructor and then you can use it with any view in the future.
Related
I have two custom views (canvas views) and both of them have an observer which triggers an event on the screen and draw something on canvas. I am reusing the container and rendering the canvas in the same parent layout by removing other views. Right now, no matter what my both view observer values.
I have tried an dirty hack where I am passing a boolan to both views as true being one and other being false on switch which kinda work but I don't want that. Because it is just a hack. Can anyone tell me an android way to do it?
public class FakeViewOne extends View {
private EventViewModel eventViewModel;
private float l = 0.0f;
public FakeViewOne(Context context, View ParentView) {
super(context);
eventViewModel = new ViewModelProvider((ViewModelStoreOwner) getContext()).get(EventViewModel.class);
eventViewModel.getTrigger().observe((LifecycleOwner) getContext(), new Observer<Float>() {
#Override
public void onChanged(Float val) {
l = val;
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
* if l > 50 change background of a linearlayout
* draw a line on a canvas
* */
invalidate();
}
}
public class FakeViewTwo extends View {
private EventViewModel eventViewModel;
private float l = 0.0f;
public FakeViewTwo(Context context, View ParentView) {
super(context);
eventViewModel = new ViewModelProvider((ViewModelStoreOwner) getContext()).get(EventViewModel.class);
eventViewModel.getTrigger().observe((LifecycleOwner) getContext(), new Observer<Float>() {
#Override
public void onChanged(Float val) {
l = val;
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/*
* if l > 50 change background of a tile
*
* */
invalidate();
}
}
CODE Inside a Fragment where I am switch between those two FakeViews. How can I make those not observe values when they are not-rendered/inactive/removed from the view. I am using mycanvas.removeAllViews();.
final RelativeLayout mycanvas = view.findViewById(R.id.myCanvas);
gestureViewModel.getGestureType().observe(getActivity(), new Observer<String>() {
#Override
public void onChanged(#Nullable String s) {
assert s != null;
mycanvas.removeAllViews();
switch (s){
case "shake":
ShakeControl(view,myView,mycanvas);
break;
case "move":
MoveControl(view,myView,mycanvas);
break;
}
}
});
I have solved the problem with removing the observers on window detach in each FakeView.
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.d("Detached","Head");
if (eventViewModel != null && eventViewModel.getTrigger().hasObservers()) {
eventViewModel.getTrigger().removeObservers((LifecycleOwner) ctx);
}
}
I wrote a little STT-functionality, with a floating button that is pulsating after being clicked on to notify that the app is listening. This works quite well so far with the one annoying behavior that my floating button does not return to its original size in some cases.
The animation increases and decreases the size of the button, and I guess it gets stuck in the increased state, hence the randomness of this behavior. I just can't figure out how to catch that and set the size to the original one.
Action Listener of my Button:
private View.OnTouchListener setVoiceButtonOnClick()
{
return new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
if(!voiceButton.isInitialized())
voiceButton.initAnimationValues();
voiceButton.setPressed(true);
listen();
}
return true;
}
};
}
My Button extends FloatingActionButton, and does the following:
public class FloatingVoiceButton extends FloatingActionButton
{
public static final float DEFAULT_ANIMATION_FACTOR = 1.2f;
private boolean isInitialized = false;
private int originalHeight;
private int originalWidth;
private boolean isAnimationRunning;
private ObjectAnimator animator;
public FloatingVoiceButton(Context context)
{
super(context);
}
public void initAnimationValues()
{
isInitialized = true;
isAnimationRunning = false;
originalHeight = getMeasuredHeight();
originalWidth = getMeasuredWidth();
animator = ObjectAnimator.ofPropertyValuesHolder(
this,
PropertyValuesHolder.ofFloat("scaleX", DEFAULT_ANIMATION_FACTOR),
PropertyValuesHolder.ofFloat("scaleY", DEFAULT_ANIMATION_FACTOR));
animator.setDuration(200);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.setRepeatMode(ObjectAnimator.REVERSE);
}
public boolean isInitialized()
{
return isInitialized;
}
public void resetButtonSize()
{
setMeasuredDimension(originalWidth, originalHeight);
}
public boolean isAnimationRunning()
{
return isAnimationRunning;
}
public void animate(boolean doAnimation)
{
isAnimationRunning = doAnimation;
if(doAnimation)
animator.start();
else
{
animator.end();
setPressed(false);
resetButtonSize();
//destroyDrawingCache(); tried these without success
//postInvalidate();
}
}
}
Finally I am controlling the button the start and end of the animation with my RecognitionListener:
public class InputVoiceRecognitionListener implements RecognitionListener
{
private EditText targetEditText;
private String originalContent;
private final String DELIMITER = "\n\n";
private FloatingVoiceButton button;
public InputVoiceRecognitionListener(EditText editText, FloatingVoiceButton button)
{
targetEditText = editText;
originalContent = editText.getText().toString();
this.button = button;
}
#Override
public void onReadyForSpeech(Bundle params)
{
button.animate(true);
}
#Override
public void onBeginningOfSpeech()
{
originalContent = targetEditText.getText().toString();
}
#Override
public void onRmsChanged(float rmsdB)
{}
#Override
public void onBufferReceived(byte[] buffer)
{}
#Override
public void onEndOfSpeech()
{
if(button.isAnimationRunning())
button.animate(false);
}
#Override
public void onError(int error)
{
if(button.isAnimationRunning())
button.animate(false);
}
#Override
public void onResults(Bundle results)
{
setRecognizedText(results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION));
}
#Override
public void onPartialResults(Bundle partialResults)
{
setRecognizedText(partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION));
}
#Override
public void onEvent(int eventType, Bundle params)
{
}
private void setRecognizedText(ArrayList<String> matches)
{
String result = "";
if(matches != null)
result = matches.get(0);
if((originalContent.trim()).length() > 0)
{
if(!originalContent.endsWith("\n\n"))
result = originalContent + DELIMITER + result;
else result = originalContent + result;
}
targetEditText.setText(result);
targetEditText.setSelection(result.length());
}
}
EDIT
This did it for me:
resettingAnimator = ObjectAnimator.ofPropertyValuesHolder(
this,
PropertyValuesHolder.ofFloat("scaleX", 1.0f),
PropertyValuesHolder.ofFloat("scaleY", 1.0f));
resettingAnimator.setDuration(0);
resettingAnimator.setRepeatCount(1);
and calling resettingAnimator.start(); when I finish my main animation.
Simple solution to this problem is that you define another animation after stopping your repeating one.
I just can't figure out how to catch that and set the size to the original one.
You, that is View, does know what is the "original" size, its the size of the scale factor 1f. So after stopping repeating animation just make another animations to set scale to 1f
PropertyValuesHolder.ofFloat("scaleX", 1f)
PropertyValuesHolder.ofFloat("scaleY", 1f))
This animation will run always, but will not be visible if your button is already at "normal" size.
With this in mind I would recommend that you use some other flag than isAnimationRunning(), either by some state (ex. selected) of your Fab, or some manually set arbitrary boolean.
I want to change my Relative layout's background every 10 seconds with fade in/fade out animation.
So I found
//Transitiondrawable
TransitionDrawable transition = (TransitionDrawable) viewObj.getBackground();
transition.startTransition(transitionTime);
But it supports only 2 Drawable and I want to add more
Is there any way to do this?
First implement MyAnim.java class as below:
public class MyAnim extends Animation {
private final RelativeLayout view;
private int targetBackGround;
public MyAnim(RelativeLayout view, int tagetBackGroundColor) {
this.view = view;
this.targetBackGround = tagetBackGroundColor;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
view.setBackgroundColor(targetBackGround);
}
public void setColor(int color) {
this.targetBackGround = color;
}
}
Then add below code to your activity and call that animateBackground() method wherever you want:
private MyAnim backgroundAnim;
private int i;
private void animateBackground(){
final RelativeLayout animLay = (RelativeLayout) findViewById(R.id.animLay);
final int colors[] = new int[]{Color.RED, Color.CYAN, Color.DKGRAY, Color.GREEN, Color.MAGENTA};
backgroundAnim = new MyAnim(animLay, colors[i]);
backgroundAnim.setDuration(1000);
animLay.startAnimation(backgroundAnim);
backgroundAnim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
if (i == colors.length - 1) {
i = 0;
} else {
i++;
}
backgroundAnim.setColor(colors[i]);
animLay.startAnimation(backgroundAnim);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
You can create your own loop, something like:
int delayBetweenAnimations = 10000;
for (int i = 0; i < yourImagesArray.length ; i++) {
int delay = i * delayBetweenAnimations;
yourImageview.postDelayed(new Runnable() {
#Override
public void run() {
//set your image and animation here
}
}, delay);
}
Another way is to use recursive animation:
#Override
public void onAnimationEnd(Animator animation) {
if(check_if_you_Still_want to_loop){
//rerun your animation
}
}
Am trying to do a simple animation in order scale a relative layout's width. I managed to do it using this code but the animation is done very quickly even with 5000ms duration. I cant see the intermidiate results. Any suggestion?
RelativeLayout baseLayer;
RelativeLayout fillLayer;
public void animate(int duration){
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
fillLayer.getLayoutParams().width = (int)(baseLayer.getLayoutParams().width * interpolatedTime);
fillLayer.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
a.setInterpolator(new LinearInterpolator());
fillLayer.startAnimation(a);
}
I have found the solution. The problem was that i was calling the animation method from OnStart(). Now am calling it from onWindowFocusChanged().
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
testBar.animate(2000,0.8);
}
RelativeLayout baseLayer;
RelativeLayout fillLayer;
public void animate(int duration,float fillPercentage){
final int targetWidth = (int)(baseLayer.getMeasuredWidth() * fillPercentage);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
fillLayer.getLayoutParams().width = (int)(targetWidth * interpolatedTime);
fillLayer.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(duration);
a.setInterpolator(new LinearInterpolator());
fillLayer.startAnimation(a);
}
And throughout this procedure I have found from other post an alternative way to do the same job. But again it the method need to be called form the onWindowFocusChanged().
ValueAnimator.ofObject(new WidthEvaluator(fillLayer), fillLayer.getLayoutParams().width, targetWidth).setDuration(2000).start();
class WidthEvaluator extends IntEvaluator {
private View v;
public WidthEvaluator(View v) {
this.v = v;
}
#Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int num = (Integer)super.evaluate(fraction, (Integer)startValue, (Integer)endValue);
ViewGroup.LayoutParams params = v.getLayoutParams();
params.width = num;
v.setLayoutParams(params);
return num;
}
}
[Update Solution]
Following to that I have added my custom views into a ListView and then the problems started again. ListView should be getting ready before views get their actual size. So the animation wasn't able to be done because getMeasuredWidth() was returning always 0 (this was and the problem from the beginning).
So what i did is i used this code snippet in OnCreate().
ViewTreeObserver vto = listBars.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!HackClass.GlobalDrawnFlag) {
adapter.notifyDataSetChanged();
HackClass.GlobalDrawnFlag = true;
}
}
});
And this inside my list adapter class
if (item.getMeasuredWidth==0)
HackClass.GlobalDrawnFlag = false;
else
HackClass.GlobalDrawnFlag = true;
This approach solved all my problems apparently. However is that a good solution ? Any other suggestion is welcome.
I want to create a custom animation for fragment transition. The animation is in form of a circle that increases his radius from a specific size until it is equal to window height. I can use ScaleAnimation, but then I'll lose drawable quality. Does someone have any ideas ho to do this. Thanks in advance.
Ok. I'll answer my own question :). To animate the fragment transition with my custom transition (a colored circle that increases his size) I have done the following:
- create a custom view that draws a circle and add the possibility to increase his radius;
- place this view in my FragmentActivity xml layout;
- when the target button is clicked and fragment should change I've called ValueAnimator to change circle radius;
Final code will look something like this:
public class MainScreenActivity extends AppCompatActivity {
private Button targetButton;
private CircleView circleView;
public void targetButtonClick(View view) {
AnimationHandler handler = new AnimationHandler(view.getX(),view.getY());
handler.animate();
}
private class AnimationHandler implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
private static final int TRANSITION_ANIM_DURATION = 500;
private final float centerX;
private final float centerY;
private ValueAnimator valueAnimator;
public AnimationHandler(float x, float y) {
this.centerX = x;
this.centerY = y;
init();
}
private void init() {
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
valueAnimator = ValueAnimator.ofFloat(0, point.y);
valueAnimator.setDuration(TRANSITION_ANIM_DURATION);
valueAnimator.addUpdateListener(this);
valueAnimator.addListener(this);
}
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
circleView.setRadius((float) animation.getAnimatedValue());
}
public void animate() {
circleView.setXCenter(centerX);
circleView.setYCenter(centerY);
int color = getResources().getColor(android.R.color.dark_red);
circleView.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.trans_circle_width));
valueAnimator.start();
}
}
}