Hello I was trying to Pause Animation Drawable but could not succeeded. With the help of stack overflow I atleast got the help of animationDrawable end listener here is the code for it . IS there any possible way where I can pause the animation Drawable and Start it from where it has paused ...
public abstract class CustomAnimationDrawable extends AnimationDrawable{
/** Handles the animation callback. */
Handler mAnimationHandler;
Runnable r= new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
onAnimationFinish();
}
};
public CustomAnimationDrawable(AnimationDrawable aniDrawable) {
/* Add each frame to our animation drawable */
for (int i = 0; i < aniDrawable.getNumberOfFrames(); i++) {
this.addFrame(aniDrawable.getFrame(i), aniDrawable.getDuration(i));
}
}
#Override
public void start() {
super.start();
/*
* Call super.start() to call the base class start animation method.
* Then add a handler to call onAnimationFinish() when the total
* duration for the animation has passed
*/
mAnimationHandler = new Handler();
mAnimationHandler.postDelayed(r, getTotalDuration());
}
/**
* Gets the total duration of all frames.
*
* #return The total duration.
*/
public int getTotalDuration() {
int iDuration = 0;
for (int i = 0; i < this.getNumberOfFrames(); i++) {
iDuration += this.getDuration(i);
}
return iDuration;
}
/**
* Called when the animation finishes.
*/
abstract void onAnimationFinish();
public void destroy()
{
mAnimationHandler.removeCallbacksAndMessages(r);
mAnimationHandler.removeCallbacksAndMessages(null);
}
}
Kindly please help is there any way i can pause ?
I've been looking around, and it doesn't seem that there's a way to directly do it. All of the variables and methods for setting the frame in AnimationDrawable are private. However, you can get the index of the frame the animation is on when you hit pause and then create a new animation by iterating through the original animation from the index of the last frame played (and then start over and add the rest if the animation is cyclical).
I started out with an XML based animation loop, and then just added another one for pause and resume. Here's what I ended up doing, and it's working fine so far, with relevant variables listed first.
private ImageView sphere;
private AnimationDrawable sphereAnimation;
private AnimationDrawable sphereResume;
private AnimationDrawable activeAnimation;
private Drawable currentFrame;
private Drawable checkFrame;
private int frameIndex;
private void pause()
{
looping = false;
sphereResume = new AnimationDrawable();
activeAnimation.stop();
currentFrame = activeAnimation.getCurrent();
frameLoop:
for(int i = 0; i < sphereAnimation.getNumberOfFrames(); i++)
{
checkFrame = activeAnimation.getFrame(i);
if(checkFrame == currentFrame)
{
frameIndex = i;
for(int k = frameIndex; k < activeAnimation.getNumberOfFrames(); k++)
{
Drawable frame = activeAnimation.getFrame(k);
sphereResume.addFrame(frame, 50);
}
for(int k = 0; k < frameIndex; k++)
{
Drawable frame = activeAnimation.getFrame(k);
sphereResume.addFrame(frame, 50);
}
activeAnimation = sphereResume;
sphere.setImageDrawable(activeAnimation);
sphere.invalidate();
break frameLoop;
}
}
}
private void play()
{
looping = false;
activeAnimation.setOneShot(true);
activeAnimation.start();
}
private void stop()
{
looping = false;
activeAnimation.stop();
activeAnimation = sphereAnimation;
sphere.setImageDrawable(activeAnimation);
}
private void loop()
{
looping = true;
stopSoundEffect();
activeAnimation.setOneShot(false);
activeAnimation.start();
}
The method by Zorbert Crenshaw is Nice, but it's complicated.
I have another solution.(Need surround with try/catch because reflection is unsafe).
you can call getCurFrameFromAnimationDrawable to save the current frame before stop Animation, and call setCurFrameToAnimationDrawable to start from any frame.
'30L' should be replaced your param of duration.
private int getCurFrameFromAnimationDrawable(AnimationDrawable drawable){
try {
Field mCurFrame = drawable.getClass().getDeclaredField("mCurFrame");
mCurFrame.setAccessible(true);
return mCurFrame.getInt(drawable);
}catch (Exception ex){
Log.e(AnimTool.class.getName(), "getCurFrameFromAnimationDrawable: Exception");
return 0;
}
}
private void setCurFrameToAnimationDrawable(AnimationDrawable drawable, int curFrame){
try {
Field mCurFrame = drawable.getClass().getDeclaredField("mCurFrame");
mCurFrame.setAccessible(true);
mCurFrame.setInt(drawable, curFrame);
Class[] param = new Class[]{Runnable.class, long.class};
Method method = Drawable.class.getDeclaredMethod("scheduleSelf", param);
method.setAccessible(true);
method.invoke(drawable, drawable, 30L);
}catch (Exception ex){
Log.e(AnimTool.class.getName(), "setCurFrameToAnimationDrawable: Exception");
}
}
This is a late answer to the question. But it might help with the other one. To pause and resume AnimationDrawable, simply customize it and handle with only one variable.
class CustomAnimationDrawable1() : AnimationDrawable() {
private var finished = false
private var animationFinishListener: AnimationFinishListener? = null
private var isPause = false
private var currentFrame = 0
fun setFinishListener(animationFinishListener: AnimationFinishListener?) {
this.animationFinishListener = animationFinishListener
}
interface AnimationFinishListener {
fun onAnimationFinished()
}
override fun selectDrawable(index: Int): Boolean {
val ret = if (isPause) {
super.selectDrawable(currentFrame)
} else {
super.selectDrawable(index)
}
if (!isPause) {
currentFrame = index
if (index == numberOfFrames - 1) {
if (!finished && ret && !isOneShot) {
finished = true
if (animationFinishListener != null) animationFinishListener!!.onAnimationFinished()
}
}
}
return ret
}
fun pause() {
isPause = true
}
fun resume() {
isPause = false
}
}
Related
I redesigned my project by simplifying the code programming. i placed all the images that are static through the xml layout.
i get only 2 results when i run the program.
1) running with no problems
2) running with problems
i get the following error:
Connected to process 10651 on device 4.7_WXGA_API_22 [emulator-5554]
I/art: Not late-enabling -Xcheck:jni (already on)
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
I believe the problem lies somewhere in this inner class, i think?!
private class MatchCardGame{
private Game mMatchGame;
private List<Drawable> revealImagesOfCards;
private List<Integer> revealCards;
private Drawable hiddenCard;
private List<Integer> cardPoints;
private List< Boolean> isHidden;
public MatchCardGame(int numOfCards){
mMatchGame = new Game(numOfCards);
revealImagesOfCards = new ArrayList<>();
revealCards = new ArrayList<>();
cardPoints = new ArrayList<>();
isHidden = new ArrayList<>();
setCoverCard();
for(int i = 1; i <= numOfCards; i++)
setMatchImageCard(i);
}
public void setMatchImageCard( int cardLoc){
int drawableLoc = mMatchGame.findImageOfCard(cardLoc);
Drawable drawable = ResourcesCompat.getDrawable(getResources(), drawableLoc, null);
Integer revealCard = mMatchGame.findContentsOfCard(cardLoc);
revealImagesOfCards.add(drawable);
revealCards.add(revealCard);
cardPoints.add(Integer.valueOf(20));
Boolean hideCard = true;
isHidden.add(hideCard);
}
private void setCoverCard(){
hiddenCard = ResourcesCompat.getDrawable(getResources(), R.drawable.black_card, null);
}
public Drawable getImage(int loc, boolean statReveal){
loc--;
if(!statReveal){
Boolean hideCard = isHidden.get(loc);
hideCard = true;
return hiddenCard;
}
else {
Boolean hideCard = isHidden.get(loc);
hideCard = false;
return revealImagesOfCards.get(loc);
}
}
public boolean getHiddenStat(int loc){
loc--;
Boolean hideCard = isHidden.get(loc);
return hideCard;
}
public boolean compareCards(int loc1, int loc2){
loc1--;
loc2--;
Integer card1 = revealCards.get(loc1);
Integer card2 = revealCards.get(loc2);
Integer cardPts1 = cardPoints.get(loc1);
Integer cardPts2 = cardPoints.get(loc2);
if(card1 == card2){
int num = Integer.valueOf( scoreText.getText().toString());
Log.i("TAGG","Score Points: " + (cardPts1 + cardPts2));
new AdjustScore().execute(Integer.valueOf(cardPts1 + cardPts2));
return true;
}
else{
cardPts1 -= 5;
cardPts2 -= 5;
if(cardPts1 < 0)
cardPts1 = 0;
if(cardPts2 < 0)
cardPts2 = 0;
cardPoints.set(loc1, cardPts1);
cardPoints.set(loc2,cardPts2);
return false;
}
}
private class AdjustScore extends AsyncTask<Integer,Integer,Void>{
private TextView scoreText;
private int currentScore;
#Override
protected void onPreExecute() {
super.onPreExecute();
scoreText = (TextView) findViewById(R.id.score_txt);
currentScore = Integer.valueOf( scoreText.getText().toString());
}
#Override
protected Void doInBackground(Integer... integers) {
final int num = integers[0];
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int x = 1; x <= num; x++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(Integer.valueOf(currentScore + x));
}
}
};
new Thread(runnable).start();
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
Message msg = scoreHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("myPoints",String.valueOf(values[0]));
msg.setData(bundle);
scoreHandler.sendMessage(msg);
}
}
}
I call this in onCreate of activity
private MatchCardGame myGame;
private List<Integer> selectCards;
private TextView scoreText;
private Handler scoreHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
String stat1 = bundle.getString("myPoints");
int num = Integer.valueOf(stat1);
scoreText.setText(String.valueOf(num));
}
};
#Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card_game);
myGame = new MatchCardGame(12);
}
I have another inner class derived from AsynTask.
it is mainly used to setup the card views and set clicklisteners on imageviews. but i don't think it is the problem.
First off, those aren't errors. They're the garbage collector. That's perfectly normal, and shouldn't give you any concern unless you have a performance issue at the same time. Doing something in code rather than in xml to avoid those is unnecessary, and will work or not based on pure luck of when the garbage collector is needed.
Secondly, you're loading all your images in an AsyncTask. Sometimes thats good (it stops you from pausing the main thread to load images), but if you don't have a default image in place, then until that task is done it won't actually be able to display any images. So you have a race condition between drawing and the task finishing.
Solution: do it in xml, or put up a loading screen. I suggest the first, because your task isn't actually doing anything useful if the images are static images from the app- those were loaded when the app launched.
The script I have is for to pause and unpause the gameobject . I have two of the same errors (31,16): error CS1525: Unexpected symbol (', expecting)', ,',;', [', or='
I am just trying to pause and unpause the gameobject . Maybe pause and unpause more in my scene . I need the gameobject pause for a couple of seconds and unpause . Dont what to pause the gameobject by a key. Here is my script :
using UnityEngine;
using System.Collections;
public class star : MonoBehaviour {
GameObject[] pauseObjects;
void Start () {
pauseObjects = GameObject.FindGameObjectsWithTag("Player");
}
void Pause(){
StartCoroutine(waitToUnpause);
}
IENumerator waitToUnpause(){
//do the thing to pause game
Time.timeScale = 7f;//or some other method
yield return new WaitForSeconds(7);///or any duration you want
Time.timeScale = 1f;//or some other method
}
void pauseGameobject()
{
timeLeft -= Time.deltaTime;
if(timeLeft < 0)
gameObject.SetActive(false);
{
start coroutine("wait");
}
}
public ienumenator wait()
{
time.timescale = 0;
yield return new waitforsceonds(7);
time.timesale = 1;
}
void pauseGameobject()
{
if(timerleft < 0)
{
start coroutine("wait");
}
}
public ienumenator wait()
{
time.timescale = 0;
yield return new waitforsceonds(7);
time.timesale = 1;
}
}
This code is compiling without errors (yours is pretty ... unclean). It will definitely not work though. Also I changed some naming to match naming conventions (lower/upper case).
using UnityEngine;
using System.Collections;
public class Star : MonoBehaviour {
GameObject[] pauseObjects;
// I guess this variable is meant to be a field
// it is never set to any initial value!
float timeLeft;
void Start () {
pauseObjects = GameObject.FindGameObjectsWithTag("Player");
}
void Pause(){
StartCoroutine(WaitToUnpause());
}
IEnumerator WaitToUnpause(){
//do the thing to pause game
Time.timeScale = 7f;//or some other method
yield return new WaitForSeconds(7);///or any duration you want
Time.timeScale = 1f;//or some other method
}
void PauseGameobject()
{
timeLeft -= Time.deltaTime;
if(timeLeft < 0)
{
gameObject.SetActive(false);
StartCoroutine(Wait());
}
}
public IEnumerator Wait()
{
Time.timeScale = 0;
yield return new WaitForSeconds(7);
Time.timeScale = 1;
}
// The following code is nearly a duplicate of the above two functions
// void PauseGameobject()
//
// {
//
// if(timeleft < 0)
//
// {
//
// StartCoroutine(Wait());
//
// }
//
// }
//
// public IEnumerator Wait()
//
// {
//
// Time.timeScale = 0;
//
// yield return new WaitForSeconds(7);
//
// Time.timeScale = 1;
//
// }
}
I am trying to generate a depth map from the point cloud. I know that I can project the point cloud to the image plane, however there is already a function (ScreenCoordinateToWorldNearestNeighbor) in the TangoSupport script that finds the XYZ point given a screen coordinate.
I am unable to get this support function to work, and it seems that one or more of my inputs are invalid. I am updating my depthmap texture in the OnTangoDepthAvailable event.
public void OnTangoDepthAvailable(TangoUnityDepth tangoDepth)
{
_depthAvailable = true;
Matrix4x4 ccWorld = _Camera.transform.localToWorldMatrix;
bool isValid = false;
Vector3 colorCameraPoint = new Vector3();
for (int i = 0; i < _depthMapSize; i++)
{
for (int j = 0; j < _depthMapSize; j++)
{
if (TangoSupport.ScreenCoordinateToWorldNearestNeighbor(
_PointCloud.m_points, _PointCloud.m_pointsCount,
tangoDepth.m_timestamp,
_ccIntrinsics,
ref ccWorld,
new Vector2(i / (float)_depthMapSize, j / (float)_depthMapSize),
out colorCameraPoint, out isValid) == Common.ErrorType.TANGO_INVALID)
{
_depthTexture.SetPixel(i, j, Color.red);
continue;
}
if (isValid)
{
//_depthTexture.SetPixel(i, j, new Color(colorCameraPoint.z, colorCameraPoint.z, colorCameraPoint.z));
_depthTexture.SetPixel(i, j,
new Color(0,UnityEngine.Random.value,0));
}
else
{
_depthTexture.SetPixel(i, j, Color.white);
}
}
}
_depthTexture.Apply();
_DepthMapQuad.material.mainTexture = _depthTexture;
}
If I had to guess, I would say that I am passing in the wrong matrix (ccWorld). Here is what it says in the documents for the matrix param:
Transformation matrix of the color camera with respect to the Unity
world frame.
The result is a white depth map, which means that the function is returning successfully, however the isValid is false meaning that it couldn't find any nearby point cloud point after projection.
Any ideas? Also I noticed that the performance is pretty bad, even when my depth map is 8x8. Should I not be updating the depthmap when ever new depth data is available (inside OnTangoDepthAvailable)?
Edit:
I was able to make the function return successfully, however now it doesn't find a point cloud point nearby after projection. The resulting depth map is always white. I am printing out all the arguments, and it all looks correct, so I think I am passing in the wrong matrix.
You should update your SDK and Project Tango Dev Kit. Here is an example of getting depth map on Android, perhaps you get a hint for unity:
public class MainActivity extends AppCompatActivity {
private Tango mTango;
private TangoConfig mTangoConfig;
private TangoPointCloudManager mPointCloudManager;
private AtomicBoolean tConnected = new AtomicBoolean(false);
Random rand = new Random();
private ImageView imageDepthMap;
private static final ArrayList<TangoCoordinateFramePair> framePairs = new ArrayList<TangoCoordinateFramePair>();
{
framePairs.add(new TangoCoordinateFramePair(
TangoPoseData.COORDINATE_FRAME_CAMERA_DEPTH,
TangoPoseData.COORDINATE_FRAME_DEVICE));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//initialize the imageView
imageDepthMap = (ImageView)findViewById(R.id.imageView);
//initialize pointCloudManager
mPointCloudManager = new TangoPointCloudManager();
}
#Override
protected void onResume(){
super.onResume();
//obtain the tango configuration
if(tConnected.compareAndSet(false, true)) {
try {
setTango();
} catch (TangoOutOfDateException tE) {
tE.printStackTrace();
}
}
}
#Override
protected void onPause(){
super.onPause();
if(tConnected.compareAndSet(true, false)) {
try {
//disconnect Tango service so other applications can use it
mTango.disconnect();
} catch (TangoException e) {
e.printStackTrace();
}
}
}
private void setTango(){
mTango = new Tango(MainActivity.this, new Runnable() {
#Override
public void run() {
TangoSupport.initialize();
mTangoConfig = new TangoConfig();
mTangoConfig = mTango.getConfig(TangoConfig.CONFIG_TYPE_CURRENT);
mTangoConfig.putBoolean(TangoConfig.KEY_BOOLEAN_DEPTH, true); //activate depth sensing
mTango.connect(mTangoConfig);
mTango.connectListener(framePairs, new Tango.OnTangoUpdateListener() {
#Override
public void onPoseAvailable(TangoPoseData tangoPoseData) {
}
#Override
public void onXyzIjAvailable(TangoXyzIjData pointCloud) {
// Log.d("gDebug", "xyZAvailable");
//TangoXyzIjData pointCloud = mPointCloudManager.getLatestXyzIj();
// Update current camera pose
if (pointCloud.ijRows * pointCloud.ijCols > 0){
try {
// Calculate the last camera color pose.
TangoPoseData lastFramePose = TangoSupport.getPoseAtTime(0,
TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
TangoPoseData.COORDINATE_FRAME_CAMERA_COLOR,
TangoSupport.TANGO_SUPPORT_ENGINE_OPENGL, 0);
if (pointCloud != null) {
//obtain depth info per pixel
TangoSupport.DepthBuffer depthBuf = TangoSupport.upsampleImageNearestNeighbor(pointCloud, mTango.getCameraIntrinsics(TangoCameraIntrinsics.TANGO_CAMERA_COLOR), lastFramePose);
//create Depth map
int[] intBuff = convertToInt(depthBuf.depths, depthBuf.width, depthBuf.height);
final Bitmap Image = Bitmap.createBitmap(intBuff, depthBuf.width, depthBuf.height, Bitmap.Config.ARGB_8888);
runOnUiThread(new Runnable() {
#Override
public void run() {
imageDepthMap.setImageBitmap(Image);
}
});
}
} catch (TangoErrorException e) {
Log.e("gDebug", "Could not get valid transform");
}
}
}
#Override
public void onFrameAvailable(int i) {
//Log.d("gDebug", "Frame Available from " + i);
}
#Override
public void onTangoEvent(TangoEvent tangoEvent) {
}
});
}
});
}
private int[] convertToInt(FloatBuffer pointCloudData, int width, int height){
double mulFact = 255.0/5.0;
int byteArrayCapacity = width * height;
int[] depthMap = new int[byteArrayCapacity];
int grayPixVal = 0;
pointCloudData.rewind();
for(int i =0; i < byteArrayCapacity; i++){
//obtain grayscale representation
grayPixVal = (int)(mulFact * (5.0- pointCloudData.get(i)));
depthMap[i] = Color.rgb(grayPixVal, grayPixVal, grayPixVal);
}
return depthMap;
}
}
I extracted this code from my already working version. Try to fix any config related errors. The code assumes depth sensing range of 0.4m - 5m in depth estimation. Mapping zero to 255 allows regions which were not estimated (value of zero) to be white.
I have custom ImageView for animated GIF image. i want to show GIF image, I tried but in this case it is contain url in Async instead I want to show GIF image from raw folder without using Glide. Anyone have any idea how to show image? Please guyz help to solve this problem!!!
I tried this for set raw file
new GifStaticData() {
#Override
protected void onPostExecute(Resource drawable) {
super.onPostExecute(drawable);
gifImageView.setImageResource(R.raw.earth_tilt_animation);
// Log.d(TAG, "GIF width is " + gifImageView.getGifWidth());
// Log.d(TAG, "GIF height is " + gifImageView.getGifHeight());
}
}.execute(R.raw.earth_tilt_animation);
GifStaticData.java
public class GifStaticData extends AsyncTask<Resource, Void, Resource> {
private static final String TAG = "GifDataDownloader";
#Override protected Resource doInBackground(final Resource... params) {
final Resource gifUrl = params[0];
if (gifUrl == null)
return null;
try {
// return ByteArrayHttpClient.get(gifUrl);
return gifUrl;
} catch (OutOfMemoryError e) {
Log.e(TAG, "GifDecode OOM: " + gifUrl, e);
return null;
}
}
}
GifImageView.java
public class GifImageView extends ImageView implements Runnable {
private static final String TAG = "GifDecoderView";
private GifDecoder gifDecoder;
private Bitmap tmpBitmap;
private final Handler handler = new Handler(Looper.getMainLooper());
private boolean animating;
private boolean shouldClear;
private Thread animationThread;
private OnFrameAvailable frameCallback = null;
private long framesDisplayDuration = -1L;
private OnAnimationStop animationStopCallback = null;
private final Runnable updateResults = new Runnable() {
#Override
public void run() {
if (tmpBitmap != null && !tmpBitmap.isRecycled()) {
setImageBitmap(tmpBitmap);
}
}
};
private final Runnable cleanupRunnable = new Runnable() {
#Override
public void run() {
tmpBitmap = null;
gifDecoder = null;
animationThread = null;
shouldClear = false;
}
};
public GifImageView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public GifImageView(final Context context) {
super(context);
}
public void setBytes(final byte[] bytes) {
gifDecoder = new GifDecoder();
try {
gifDecoder.read(bytes);
gifDecoder.advance();
} catch (final OutOfMemoryError e) {
gifDecoder = null;
Log.e(TAG, e.getMessage(), e);
return;
}
if (canStart()) {
animationThread = new Thread(this);
animationThread.start();
}
}
public long getFramesDisplayDuration() {
return framesDisplayDuration;
}
/**
* Sets custom display duration in milliseconds for the all frames. Should be called before {#link
* #startAnimation()}
*
* #param framesDisplayDuration Duration in milliseconds. Default value = -1, this property will
* be ignored and default delay from gif file will be used.
*/
public void setFramesDisplayDuration(long framesDisplayDuration) {
this.framesDisplayDuration = framesDisplayDuration;
}
public void startAnimation() {
animating = true;
if (canStart()) {
animationThread = new Thread(this);
animationThread.start();
}
}
public boolean isAnimating() {
return animating;
}
public void stopAnimation() {
animating = false;
if (animationThread != null) {
animationThread.interrupt();
animationThread = null;
}
}
public void clear() {
animating = false;
shouldClear = true;
stopAnimation();
handler.post(cleanupRunnable);
}
private boolean canStart() {
return animating && gifDecoder != null && animationThread == null;
}
public int getGifWidth() {
return gifDecoder.getWidth();
}
public int getGifHeight() {
return gifDecoder.getHeight();
}
#Override public void run() {
if (shouldClear) {
handler.post(cleanupRunnable);
return;
}
final int n = gifDecoder.getFrameCount();
do {
for (int i = 0; i < n; i++) {
if (!animating) {
break;
}
//milliseconds spent on frame decode
long frameDecodeTime = 0;
try {
long before = System.nanoTime();
tmpBitmap = gifDecoder.getNextFrame();
frameDecodeTime = (System.nanoTime() - before) / 1000000;
if (frameCallback != null) {
tmpBitmap = frameCallback.onFrameAvailable(tmpBitmap);
}
if (!animating) {
break;
}
handler.post(updateResults);
} catch (final ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
Log.w(TAG, e);
}
if (!animating) {
break;
}
gifDecoder.advance();
try {
int delay = gifDecoder.getNextDelay();
// Sleep for frame duration minus time already spent on frame decode
// Actually we need next frame decode duration here,
// but I use previous frame time to make code more readable
delay -= frameDecodeTime;
if (delay > 0) {
Thread.sleep(framesDisplayDuration > 0 ? framesDisplayDuration : delay);
}
} catch (final Exception e) {
// suppress any exception
// it can be InterruptedException or IllegalArgumentException
}
}
} while (animating);
if (animationStopCallback != null) {
animationStopCallback.onAnimationStop();
}
}
public OnFrameAvailable getOnFrameAvailable() {
return frameCallback;
}
public void setOnFrameAvailable(OnFrameAvailable frameProcessor) {
this.frameCallback = frameProcessor;
}
public interface OnFrameAvailable {
Bitmap onFrameAvailable(Bitmap bitmap);
}
public OnAnimationStop getOnAnimationStop() {
return animationStopCallback;
}
public void setOnAnimationStop(OnAnimationStop animationStop) {
this.animationStopCallback = animationStop;
}
public interface OnAnimationStop {
void onAnimationStop();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
clear();
}
}
I had to play and pause the Gif image Glide - Cannot stop gif onClick- Getting TransitionDrawable instead of Animate/GifDrawable
The idea is to get drawable from view,checking if it is an instance of Gifdrawable and playing and pausing it.(Hoping the gif image is already playing)
Add this In OnClick of GifImageView
Drawable drawable = ((ImageView) v).getDrawable();
if (drawable instanceof GifDrawable) {
GifDrawable animatable = (GifDrawable) drawable;
if (animatable.isRunning()) {
animatable.stop();
} else {
animatable.start();
}
}
I found the solution of above problem using GifMovieView!!!
GifMovieViewer.java
public class GifMovieViewer extends Activity {
private Button btnStart;
private GifMovieView gif1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gif_movie_viewer);
gif1 = (GifMovieView) findViewById(R.id.gif1);
btnStart = (Button) findViewById(R.id.btnStart);
btnStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
gif1.setMovieResource(R.drawable.earth_tilt_animation);
//for pause
// gif1.setPaused(gif1.isPaused());
}
});
}
public void onGifClick(View v) {
GifMovieView gif = (GifMovieView) v;
gif.setPaused(!gif.isPaused());
}
}
I have a HashMap of Sound objects
private HashMap<Integer, Sound> sounds;
over which I'm trying to iterate to turn off all the sounds. I used
this answer to create an Iterator, but I'm still getting ConcurrentModificationException, though I'm sure there's no other code calling this at the same time.
public synchronized final void stopAll() {
Iterator<Entry<Integer, Sound>> soundEntries = sounds.entrySet().iterator();
while(soundEntries.hasNext())
{
Entry<Integer, Sound> s = soundEntries.next();
s.getValue().myOnCompletionListener = null;
s.getValue().fadeYourself();
}
sounds.clear();
}
In what way should I rewrite this to keep the ConcurrentModificationException from happening?
This is inside my Sound class:
private class soundFader extends AsyncTask<Sound, Void, Void>
{
#Override
protected Void doInBackground(Sound... arg0) {
arg0[0].fadeOut();
return null;
}
}
private void fadeOut()
{
float STEP_DOWN = (float) 0.10;
float currentVol = myVolume;
float targetVol = 0;
if(isSoundEnabled())
{
while(currentVol > targetVol)
{
currentVol -= STEP_DOWN;
mp.setVolume(currentVol, currentVol);
try {
Thread.sleep(70);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
mp.setVolume(0, 0);
onCompletion(mp);
sounds.remove(resource); // THIS LINE WAS MY ERROR
mp.seekTo(0);
nowPlaying = false;
}
public void fadeYourself()
{
soundFader fader = new soundFader();
fader.execute(this);
}
It is not permissible for one thread to modify a Collection while another thread is iterating over it.
If you want to modify only values (not keys) there is no need to use iterators here.
public synchronized final void stopAll() {
for(Sound s: sounds.values())
{
s.myOnCompletionListener = null;
s.fadeYourself();
}
sounds.clear();
}
Ninja edit:
You are removing items from the Collection while iterating. Hence the CoMo exception.
Since you are doing sounds.clear(); towards the end, you can remove the sounds.remove(resource); line.