Get current RotationAngle while Animation is running on Android - android

I'm rotating an ImageView by using an Animation defined by a XML file.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<rotate
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="90"
android:fillAfter="true" />
</set>
Although ImageView has the getRotation() method , it only returns the first value that has been set to the image object.
Is there anyway to get the current rotation degrees by using a XML animation? If not, what should be the best way to go with it?

public class CustomImageView extends ImageView {
private ObjectAnimator rotationAnimator;
public CustomImageView(Context context) {
super(context);
}
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setRotation(float rotation) {
super.setRotation(rotation);
// Do something
}
public synchronized void startAnimation(int animationDuration) {
if (rotationAnimator == null || !rotationAnimator.isRunning()) {
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf2 = Keyframe.ofFloat(0.5f, 180f);
Keyframe kf1 = Keyframe.ofFloat(1f, 360f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
rotationAnimator = ObjectAnimator.ofPropertyValuesHolder(this, pvhRotation);
rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE);
rotationAnimator.setInterpolator(new LinearInterpolator());
rotationAnimator.setDuration(animationDuration);
rotationAnimator.start();
}
else {
// Already running
}
}
Version 2, since you are having problems
public class CustomImageView extends ImageView {
private ObjectAnimator rotationAnimator;
private float myRotation;
public CustomImageView(Context context) {
super(context);
}
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setMyRotation(float rotation) {
this.myRotation = rotation;
Log.d("CustomImage", "Rotation: " + rotation);
invalidate();
}
public synchronized void startAnimation(int animationDuration) {
if (rotationAnimator == null || !rotationAnimator.isRunning()) {
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf2 = Keyframe.ofFloat(0.5f, 180f);
Keyframe kf1 = Keyframe.ofFloat(1f, 360f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("myRotation", kf0, kf1, kf2);
rotationAnimator = ObjectAnimator.ofPropertyValuesHolder(this, pvhRotation);
rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE);
rotationAnimator.setInterpolator(new LinearInterpolator());
rotationAnimator.setDuration(animationDuration);
rotationAnimator.start();
}
else {
// Already running
}
}
public synchronized void stopAnimation() {
if (rotationAnimator != null) {
rotationAnimator.cancel();
rotationAnimator = null;
}
}
public synchronized boolean getAnimationRunning() {
return rotationAnimator != null && rotationAnimator.isRunning();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.rotate(myRotation, getWidth() / 2.0f, getHeight() / 2.0f);
super.onDraw(canvas);
}
}
Sample logcat output:
04-22 22:48:55.475 16341-16341/animation.example.com.animationdemo D/CustomImage﹕ Rotation: 358.44
04-22 22:48:55.490 16341-16341/animation.example.com.animationdemo D/CustomImage﹕ Rotation: 0.48000813
04-22 22:48:55.505 16341-16341/animation.example.com.animationdemo D/CustomImage﹕ Rotation: 2.4
04-22 22:48:55.525 16341-16341/animation.example.com.animationdemo D/CustomImage﹕ Rotation: 4.44
I have packed the code into a simple project: LINK

Related

How to start an AnimationDrawable from class extending View

I'm making a custom View that displays a spinner to show that the camera is focusing.
public class ApertureAnimation extends View
{
private final static String TAG = "ApertureAnimation";
private AnimationDrawable animationDrawable;
public ApertureAnimation(Context context)
{
super(context);
init(null, 0);
}
public ApertureAnimation(Context context, AttributeSet attrs)
{
super(context, attrs);
init(attrs, 0);
}
public ApertureAnimation(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle)
{
animationDrawable = (AnimationDrawable) getResources().getDrawable(R.drawable.apperature_focus, null);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int contentWidth = getWidth() - paddingLeft - paddingRight;
int contentHeight = getHeight() - paddingTop - paddingBottom;
if(animationDrawable != null)
{
animationDrawable.setBounds(paddingLeft, paddingTop, paddingLeft + contentWidth, paddingTop + contentHeight);
animationDrawable.start();
animationDrawable.draw(canvas);
}
}
public void start()
{
if(animationDrawable != null)
animationDrawable.start();
}
public void stop()
{
if(animationDrawable != null)
animationDrawable.stop();
}
}
apperature_focus.xml
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="#drawable/apperature_0" android:duration="200" />
<item android:drawable="#drawable/apperature_30" android:duration="200" />
<item android:drawable="#drawable/apperature_60" android:duration="200" />
</animation-list>
The issue I'm having is that only the first frame shows when I call start(). I am able to get the animation to work if I use an ImageView outside of a class extending View, like this https://developer.android.com/guide/topics/graphics/drawable-animation, but when I try to bring that code into this custom UI component it doesn't work.

Cannot resolve method 'super()' error in android studio

I am getting the error: "Cannot resolve method 'super()'" in my code and I am not sure how to resolve this, do you have any clues?
The code is as follows:
public GeoView( double left, double top, double width )
{
super();
this.left = left;
this.top = top;
this.width = width;
this.transform = null;
this.backing_store = null;
this.sink = false;
this.last_size = new Rect(0, 0, 200, 200 );
this.do_tracking = false;
Drawable background = new Drawable() {
#Override
public void draw(Canvas canvas) {
}
#Override
public void setAlpha(int i) {
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return 0;
}
};
What is GeoView class? You extended it from another class?
If yes. Check that parent class have empty constructor, because you call it.
If it's your own class and it didn't extended from any other class. Then you don't need to call super().
View doesn't have empty constructor.
You should Have at least one of this constructor.
public GeoView(Context context) {
super(context);
}
public GeoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GeoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
If you want to create view by yourself, you need first constructor. Then you have to
GeoView geoView = new GeoView(YourActivity.this);
Or you can simply add your view to layout. Then You second will be called constructor. I think you should add all of them.
<com.example.yourapp.GeoView
android:layout_height="300dp"
android:layout_width="match_parent"/>

How to stop animationset in Android?

I have following class. Depends on something I should start/stop animation dynamically. I'm able to run the animation by calling its startAnimation() method however, animation does not stop when I call stopAnimation(). The not interesting thing is even ivSonar1 and ivSonar2 are still visible after calling stopAnimation() method.
Any idea would be appreciated. thanks.
public class SonarView extends RelativeLayout
{
private static final int ANIM_LENGTH_IN_MS = 1500;
private ImageView ivSonar1;
private ImageView ivSonar2;
private AnimationSet animationSet1;
private AnimationSet animationSet2;
public SonarView(Context context)
{
super(context);
}
public SonarView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public SonarView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SonarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// try to make it 25% smaller than its real size to make enough room for items of biding list
super.onMeasure(widthMeasureSpec * 3 / 4, heightMeasureSpec * 3 / 4);
ivSonar1 = (ImageView) findViewById(R.id.ivSonar1);
ivSonar1.setVisibility(INVISIBLE);
ivSonar2 = (ImageView) findViewById(R.id.ivSonar2);
ivSonar2.setVisibility(INVISIBLE);
float maxWidth = findViewById(R.id.ivBackground).getMeasuredWidth();
animationSet1 = createAnimationSet(ivSonar1, maxWidth / ivSonar1.getMeasuredWidth());
animationSet2 = createAnimationSet(ivSonar2, maxWidth / ivSonar2.getMeasuredWidth());
animationSet2.setStartOffset(150);
}
#Override
protected void onFinishInflate()
{
super.onFinishInflate();
LayoutInflater.from(this.getContext()).inflate(R.layout.widget_sonar_view, this, true);
}
private AnimationSet createAnimationSet(final View v, final float toScale)
{
// Set animation
final AnimationSet animationSet = new AnimationSet(true);
animationSet.setInterpolator(new AccelerateDecelerateInterpolator());
animationSet.setDuration(SonarView.ANIM_LENGTH_IN_MS);
animationSet.setAnimationListener(new Animation.AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
v.setAlpha(1.0f);
}
#Override
public void onAnimationEnd(Animation animation)
{
v.setAlpha(0.0f);
animationSet.setStartOffset(0);
v.startAnimation(animationSet);
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
});
// TODO The scale animation should ideally scaled to the size of the outer ring, hacking it here instead to use alpha so that I don't have to the calculation right now. Will come back to this.
// Alpha animation
animationSet.addAnimation(new AlphaAnimation(1.0f, 0.0f));
// Scale animation
animationSet.addAnimation(new ScaleAnimation(0.0f, toScale, 0.0f, toScale, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f));
return animationSet;
}
public void stopAnimation()
{
if (ivSonar1 == null || ivSonar2 == null)
{
return;
}
ivSonar1.setVisibility(INVISIBLE);
ivSonar2.setVisibility(INVISIBLE);
animationSet1.cancel();
animationSet2.cancel();
ivSonar1.clearAnimation();
ivSonar2.clearAnimation();
}
public void startAnimation()
{
if (ivSonar1 == null || ivSonar2 == null)
{
return;
}
ivSonar1.setVisibility(VISIBLE);
ivSonar2.setVisibility(VISIBLE);
animationSet1.reset();
animationSet2.reset();
animationSet2.setStartOffset(150);
ivSonar1.startAnimation(animationSet1);
ivSonar2.startAnimation(animationSet2);
}
}
To stop the AnimationSet you may need to also remove any listeners:
animationSet.setAnimationListener(null);
animationSet.cancel();

Facebook Fresco using wrap_content

I got a bunch of drawables that I want to load using fresco, I want to use wrap_content size for those images, how can I do it in xml with fresco? Or if xml is not possible how do you do it in code?
<com.facebook.drawee.view.SimpleDraweeView
android:id="#+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="#mipmap/myImage"/>
The above code is not working unless I set a fixed size.
I am part of the Fresco team and I was the one who made the design decision to not support wrap-content. The rationale is explained in the documentation. But in short, the problem is that you can't guarantee that the image will be available immediately (you may need to fetch it first) and that means that the view size would have to change once the image arrives. This is in most cases not desirable and you should probably rethink your UI.
Anyways, if you really really need/want to do that, you can do it like this:
void updateViewSize(#Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
draweeView.getLayoutParams().width = imageInfo.getWidth();
draweeView.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
ControllerListener listener = new BaseControllerListener {
#Override
public void onIntermediateImageSet(String id, #Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
#Override
public void onFinalImageSet(String id, #Nullable ImageInfo imageInfo, #Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
DraweeController controller = draweeControllerBuilder
.setUri(uri)
.setControllerListener(listener)
.build();
draweeView.setController(controller);
I wrote this code from the top of my head, I haven't actually tested it. But the idea should be clear, and it should work with minor adjustments.
Based on #plamenko's answer, I made a custom view as follows:
/**
* Works when either height or width is set to wrap_content
* The view is resized based on the image fetched
*/
public class WrapContentDraweeView extends SimpleDraweeView {
// we set a listener and update the view's aspect ratio depending on the loaded image
private final ControllerListener listener = new BaseControllerListener<ImageInfo>() {
#Override
public void onIntermediateImageSet(String id, #Nullable ImageInfo imageInfo) {
updateViewSize(imageInfo);
}
#Override
public void onFinalImageSet(String id, #Nullable ImageInfo imageInfo, #Nullable Animatable animatable) {
updateViewSize(imageInfo);
}
};
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void setImageURI(Uri uri, Object callerContext) {
DraweeController controller = ((PipelineDraweeControllerBuilder)getControllerBuilder())
.setControllerListener(listener)
.setCallerContext(callerContext)
.setUri(uri)
.setOldController(getController())
.build();
setController(controller);
}
void updateViewSize(#Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}
You can include this class in the XML, an example usage:
<com.example.ui.views.WrapContentDraweeView
android:id="#+id/simple_drawee_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
In Kotlin you can try something like this :
val listener = object : BaseControllerListener<ImageInfo>() {
override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) {
super.onFinalImageSet(id, imageInfo, animatable)
itemView.draweeGif.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.draweeGif.aspectRatio = (imageInfo?.width?.toFloat() ?: 0.toFloat()) / (imageInfo?.height?.toFloat() ?: 0.toFloat())
}
}
val controller = Fresco.newDraweeControllerBuilder()
.setUri(uriGif)
.setControllerListener(listener)
.setAutoPlayAnimations(true)
.build()
itemView.draweeGif.controller = controller
For me it was a solution in my RecyclerView because I was looking to set the layoutParams directly in my ViewHolder.
Found a solution by extending SimpleDraweeView, it allows me to use wrap_content and it works just fine! How ever I prevent you from setting the size in setContentView and the preview is not working, I be glad if could edit this answer to fix those.
Usage
<com.gazman.WrapContentDraweeView
android:id="#+id/myImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fresco:placeholderImage="#mipmap/myImage"/>
Source code
public class WrapContentDraweeView extends SimpleDraweeView {
private int outWidth;
private int outHeight;
public WrapContentDraweeView(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
}
public WrapContentDraweeView(Context context) {
super(context);
}
public WrapContentDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public WrapContentDraweeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.GenericDraweeView);
try {
int placeholderId = gdhAttrs.getResourceId(
R.styleable.GenericDraweeView_placeholderImage,
0);
if(placeholderId != 0){
if(isInEditMode()){
setImageResource(placeholderId);
}
else {
loadSize(placeholderId, context.getResources());
}
}
} finally {
gdhAttrs.recycle();
}
}
private void loadSize(int placeholderId, Resources resources) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, placeholderId, options);
outWidth = options.outWidth;
outHeight = options.outHeight;
}
#Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
params.width = outWidth;
params.height = outHeight;
super.setLayoutParams(params);
}
}
invoke updateWrapSize in onFinalImageSet
void updateWrapSize(#Nullable ImageInfo imageInfo) {
if (imageInfo != null) {
boolean wrapH = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
boolean wrapW = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT;
if (wrapH || wrapW) {
if (wrapW && !wrapH) {
getLayoutParams().width = (int) (imageInfo.getWidth() * (float) getLayoutParams().height / imageInfo.getHeight());
} else if (wrapH && !wrapW) {
getLayoutParams().height = (int) (imageInfo.getHeight() * (float) getLayoutParams().width / imageInfo.getWidth());
} else {
getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight());
}
}
}

how to rotate an imageview in android on center point

I wanted to rotate an imageview on center point with a duration(ex: 5 sec).
is it possible ? how ?
like this picture :
public class RotateImageView extends ImageView {
private Animation mRotation;
public bool isAnimating = false;
public RotateImageView(Context context) {
super(context);
Init(null);
}
public RotateImageView(Context context, AttributeSet attrs) {
super(context, attrs);
Init(attrs);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RotateImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Init(attrs);
}
private void Init(AttributeSet attrs) {
startAnimation();
}
public void startAnimation() {
if (mRotation == null) {
mRotation = AnimationUtils.loadAnimation(getContext(), R.anim.rotate);
mRotation.setRepeatCount(Animation.INFINITE);
}
this.startAnimation(mRotation);
isAnimating = true;
}
public void stopAnimation() {
if (mRotation != null)
clearAnimation();
isAnimating = false;
}
#Override
public void setVisibility(int visibility) {
if (visibility == GONE || visibility == INVISIBLE) {
clearAnimation();
} else if (visibility == VISIBLE) {
startAnimation(mRotation);
}
super.setVisibility(visibility);
}
}
rotate.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="5000"
android:fromDegrees="0"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="0"
android:toDegrees="360" />
EDITED
RotateImageView image = new RotateImageView(Context);
image.addOnClickListener(new View.OnClickListener(){
if(image.isAnimating)
image.stopAnimating();
else
image.startAnimating();
}
Use this animation.xml:
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:duration="5000" />

Categories

Resources