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.
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.
I need to pause and start a line graph in the middle while running.I am using GraphView library. My line graph is showing the graph correctly.I need to implement pause and start from the point where it has paused.How to do that?
Thank you in advance..
I have added my code below.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.fhrlistitemgraph);
String data = getIntent().getExtras().getString("data");
GraphView graph = (GraphView) findViewById(R.id.llgraphview);
series = new LineGraphSeries<DataPoint>();
graph.addSeries(series);
// customize a little bit viewport
Viewport viewport = graph.getViewport();
viewport.setYAxisBoundsManual(true);
viewport.setMinY(0);
viewport.setMaxY(200);
//viewport.setYAxisBoundsManual(true);
viewport.setScalable(true);
viewport.setScrollable(true);
}
#Override
protected void onResume() {
super.onResume();
// we're going to simulate real time with thread that append data to the
// graph
new Thread(new Runnable() {
#Override
public void run() {
String value = null;
String[] data = data.replaceAll("\\[", "").replaceAll(" ", "").replaceAll("\\]", "").split(",");
// we add 100 new entries
for (int i = 0; i < data.length; i++) {
final int j;
j=i;
value = data[i];
final String values;
values = value;
runOnUiThread(new Runnable() {
#Override
public void run() {
addEntry(j,values);
}
});
// sleep to slow down the add of entries
try {
Thread.sleep(600);
} catch (InterruptedException e) {
// manage error ...
}
}
}
}).start();
}
private void addEntry(int i,String value) {
// here, we choose to display max 10 points on the viewport and we
// scroll to end
series.appendData(new DataPoint(i, Integer.valueOf(value)), true, 5);
}
If you stop the thread you've stopped even graph, and when you restart the thread also the graph restart to screen data.
You can use a button in UI to stop the thread.
In a my case, i have insert a boolean to stop the update of graph because the thread was not only that but also other calculations they needed, when the value is true the graph is updated when the value is false the graph is stopped. The value of boolean change whe the button in UI is pressed.
boolean updateGraph = true;
if(updateGraph=true){
series.appendData(new DataPoint(countx++, usedramd), true, 60);
}
And with the button you change the value of boolean updateGraph.
I am using metaio sdk 6.0.2. i am working on metaio INSTANT_2D_GRAVITY tracking and was able to display 3d model. I want to display same 3d model when tracking is lost.but I am failing to do so. I tried by adding trackingValuesVector in onTrackingEvent of MetaioSDKCallbackHandler with no success. can anyone tell me where am I going wrong?
private TrackingValues mTrackingValues;// declared globally
private IGeometry mModel; // declared globally
private boolean mPreview=true;// declared globally
// start INSTANT_2D_GRAVITY tracking
public void onTakePicture(View v)
{
captureTrackingValues = true;
metaioSDK.startInstantTracking("INSTANT_2D_GRAVITY", new File(""), mPreview);
mPreview = !mPreview;
}
final class MetaioSDKCallbackHandler extends IMetaioSDKCallback
{
#Override
public void onInstantTrackingEvent(final boolean success,final File filePath) {
super.onInstantTrackingEvent(success, filePath);
if(mSurfaceView != null)
{
mSurfaceView.queueEvent(new Runnable() {
#Override
public void run() {
if(success)
{
if(captureTrackingValues == true)
{
metaioSDK.setTrackingConfiguration(filePath);
Log.i("Tracking value success","good");
}
}
else
{
Log.i("Tracking value failure","bad");
}
}
});
}
}
#Override
public void onTrackingEvent(TrackingValuesVector trackingValuesVector) {
super.onTrackingEvent(trackingValuesVector);
if (!trackingValuesVector.isEmpty())
{
for(int i =0;i< trackingValuesVector.size();i++)
{
if(trackingValuesVector.get(i).isTrackingState() && mModel!=null)
{
mTrackingValues = metaioSDK.getTrackingValues(i);
mModel.setCoordinateSystemID(trackingValuesVector.get(i).getCoordinateSystemID());
}
else {
if(mModel!= null && mTrackingValues != null) {
metaioSDK.setCosOffset(1, mTrackingValues);
//mChairModel.setCoordinateSystemID(0);
Log.e("TestAR","isTrackingState is null");
}
}
}
}
else{
if(mModel!= null && mTrackingValues != null) {
metaioSDK.setCosOffset(1, mTrackingValues);
//mModel.setCoordinateSystemID(0);
Log.e("TestAR","trackingValuesVector is null");
}
}
}
}
loading 3d model:
private void loadModel()
{
if (mSurfaceView != null) {
mSurfaceView.queueEvent(new Runnable() {
#Override
public void run() {
File chairModel = AssetsManager.getAssetPathAsFile(getApplicationContext(),"chair.obj");
if (chairModel != null) {
mModel = metaioSDK.createGeometry(chairModel);
mModel.setScale(3f);
mModel.setTranslation(new Vector3d(0f,0f,-60f));
mGestureHandler.addObject(mModel, 1);
mModel.setRotation(new Rotation(0f, 0.5f, 0f));
mModel.setCoordinateSystemID(1);
}
}
});
}
else
{
Log.e("exception", "msurfaceview is null");
}
}
I see that you also tried setting the model to COS 0. This should actually work, if the tracking is lost.
If you do not see the model, you would have to play around with the scale value (i.e. set a low value like 0.01) and with the Z translation value. Set a negative Z value in order to move the model away from the camera clipping plane.
Im making a jumper game. The objetive is stay alive jumping platforms.
I start the game with my character and 3 platforms. The next platforms I generate randomly with a timerhandler.
Its almost ready but Im facing some problems with a timerhandler.
Im using it to spawn platforms (static bodys) once every second.
private void addPlat(int x, int y)
{
platform = new Platform(x, y, vbom, physicsWorld);
attachChild(platform);
}
private void createPlatSpawn()
{
float mEffectSpawnDelay = 1f;
platSpawnTimerHandler = new TimerHandler(mEffectSpawnDelay, true, new ITimerCallback()
{
#Override
public void onTimePassed(TimerHandler pTimerHandler) {
platformList.add(platform);
if (platformList.getLast().getY() >= 240 && platformList.getLast().getY() < 400)
{
yCoord = (int) (platform.getY() - addNumber.nextInt(160));
}
else if (platformList.getLast().getY() < 240 && platformList.getLast().getY() >= 50)
{
yCoord = (int) (platform.getY() + addNumber.nextInt(160));
}
xCoord = (int) (platform.getX() + 200);
addPlat(xCoord, yCoord);
}
});
resourcesManager.engine.registerUpdateHandler(platSpawnTimerHandler);
}
To jump I use a variable footContacts, when its positive it can jump, otherwise it cant
Here the contact listener
private ContactListener contactListener()
{
ContactListener contactListener = new ContactListener()
{
public void beginContact(Contact contact)
{
final Fixture x1 = contact.getFixtureA();
final Fixture x2 = contact.getFixtureB();
if (x1.getBody().getUserData() != null && x2.getBody().getUserData() != null)
{
if (x2.getBody().getUserData().equals("player"))
{
player.increaseFootContacts();
}
}
}
public void endContact(Contact contact)
{
final Fixture x1 = contact.getFixtureA();
final Fixture x2 = contact.getFixtureB();
if (x1.getBody().getUserData() != null && x2.getBody().getUserData() != null)
{
if (x2.getBody().getUserData().equals("player"))
{
player.decreaseFootContacts();
}
}
}
public void preSolve(Contact contact, Manifold oldManifold)
{
}
public void postSolve(Contact contact, ContactImpulse impulse)
{
}
};
return contactListener;
}
Here is the method inside the class Player, to make him jump
public void jump()
{
if (footContacts < 1)
{
return;
}
body.setLinearVelocity(new Vector2(body.getLinearVelocity().x, 15));
}
The first 3 platforms the jump action works perfectly but when he interact with the platforms created by the timerhandler, it doesnt work. Anyone can help me?
Adding the method to create the first 3 platforms:
private void createFirstPlat()
{
addPlat(200, 200);
addPlat(400, 300);
addPlat(600, 150);
}
Class Platform
public class Platform extends Sprite{
private Body body;
public Platform(float pX, float pY, VertexBufferObjectManager vbo, PhysicsWorld physicsWorld)
{
super(pX, pY, ResourcesManager.getInstance().platform_region, vbo);
createPhysics(physicsWorld);
// TODO Auto-generated constructor stub
}
private void createPhysics (PhysicsWorld physicsWorld)
{
body = PhysicsFactory.createBoxBody(physicsWorld, this, BodyType.StaticBody, PhysicsFactory.createFixtureDef(0, 0, 0.5f));
body.setUserData("platform");
physicsWorld.registerPhysicsConnector(new PhysicsConnector(this, body, true, false)
{
#Override
public void onUpdate(float pSecondsElapsed)
{
super.onUpdate(pSecondsElapsed);
}
});
}
}
You should have post more code where you manually crate those 3 working platforms. It's possible that the problem is in .setUserData which I can't see because you haven't posted your Platform class as well.
Another possible problem is that you don't attach it to right entity or there is a problem with physiscsConnector which you maybe don't initialize. Try to create
public Entity foregroundLayer = new Entity();
and attach it to the scene. Then in addPlat use
foregroundLayer.attachChild(platform)
If that won't help please paste your Platform class and how you create them for first three times.
PS By the way, it's great solution when you want to control layers. For example you want some sprites to be on top and some sprites to be on background while overlapping. The important thing is attaching order so you first attach backgroundLayer and then foregroundLayer. Finally you can attach other entites for those two layers at any time and it will be shown as you expect.
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
}
}