VideoView Animation - android-videoview

I am using a videoview with an animation (video.setAnimation(slideinRight);) Everything works ok except that on the transition, only the layout of the videview is animating, not the video. When the translate animation occurs, i see a box move and mask over my video but the video never moves with it. I am at a loss on what to do now.

I'm currently trying to put animations on VideoView.
If you look at Android source Code, VideoView is basically a SurfaceView, coupled with a MediaPlayer, with a basic management of player's state machine.
Actually, the real 'draw' work seems to be handled by native methods in mediaPlayer (a.k.a. the real player engine implementation on your android device)
We've tested animations on different devices, and found that VideoView's underlying video player behavior/implementation isn't the same among different Android Devices :
some devices handle player's view animations correctly
others DON'T, and just display blur, black screen, or buggy display...
On top of that, VideoView seems to be written directly on memory, so any 'workaround' (like putting an opaque view in front, and setting an animation on that view) doesn't seem to work.
I'd be glad to have others feedback on this :)

Sorry for answering this rather late, but for what it's worth. and if you're looking only to use a 'slide' animation.
Try putting the videoview in a layout and animating the layout.
The way i have it set up in my code is;
AbsoluteLayout Animationlayout = (AbsoluteLayout)findViewById(R.string.frontlayout);
VideoView pvv = new VideoView(getApplicationContext());
pvv.getHolder().addCallback(this);
Animationlayout.addView(pvv);
// load video data in pvv.
Then where you want to animate your videoview to slide;
Animationlayout.animate().xBy(25).setDuration(500).setInterpolator(new BounceInterpolator());
Note that this is the 3.1 animation system.
Not sure of the classic 2.1 way of animating is going to work like this, but it should serve the same.
Stuff like rotating/scaling the layout won't work. Panning the layout around and fading it seem to be the only few things that do work.

Simply use TextureView:
More reference on TextureView here.
I have done ZoomIn - ZoomOut animation on TextureView.
Add animation xml in Res -> anim folder.
zoom_in_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale
android:duration="1000"
android:fillAfter="false"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.2"
android:toYScale="1.2" />
<set android:interpolator="#android:anim/decelerate_interpolator">
<scale
android:duration="1000"
android:fillBefore="false"
android:fromXScale="1.2"
android:fromYScale="1.2"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="500"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
</set>
texture_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextureView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/textureView"
android:layout_margin="50dp"
android:background="#android:color/darker_gray"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</TextureView>
TextureViewActivity.java:
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.io.IOException;
public class TextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
private TextureView textureView;
private MediaPlayer mMediaPlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.texture_layout);
textureView = (TextureView)findViewById(R.id.textureView);
textureView.setSurfaceTextureListener(this);
textureView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Animation zoomAnimation = AnimationUtils.loadAnimation(TextureViewActivity.this, R.anim.zoom_in_out);
textureView.startAnimation(zoomAnimation);
}
});
}
private String getVideoPath(){
return "android.resource://" + getPackageName() + "/" + R.raw.promovideo;
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Surface surface = new Surface(surfaceTexture);
try {
mMediaPlayer= new MediaPlayer();
mMediaPlayer.setDataSource(TextureViewActivity.this, Uri.parse(getVideoPath()));
mMediaPlayer.setSurface(surface);
mMediaPlayer.prepare();
mMediaPlayer.start();
mMediaPlayer.setLooping(true);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}
Done

Related

How to expand the view while zooming an image into ViewPager2

I was trying, with ViewPager2, to get default gallery-like experience of common Android SmartPhone, where you can zoom an image with pan and pinch controls along with the ability to navigate to another photo by swipe gestures.
I faced a problem in which when the zoomed image is swiped expecting it to get panned, instead of that, the ViewPager2 switched to another page.
ViewPager2 responds to the swipe event and causes page change and it doesn’t let zoomable view to respond to that event.
How do I solve the problem? Thanks.
The only solution I found is to use the old ViewPager with which it is possible to intercept events and if the photo is enlarged, it is possible to disable the page change and allow the management of the zoom.
First I defined a field, in a static class, to keep track of the state the photo is in:
public class Utilities {
....
static boolean isZoomed;
private Utilities () { }
static public void setIsZoomed (boolean z) {
isZoomed = z;
}
static public boolean getIsZoomed () {
return isZoomed;
}
....
}
Then in the custom class for managing the zoom I detect the status of the photo:
public class TouchImageView extends ImageView {
...
Utilities.setIsZoomed(normalizedScale != 1);
...
}
Finally, in the ViewPager custom class I disable or allow pagination based on the state of the photo:
public class CustomViewPager extends ViewPager {
public CustomViewPager (Context context) {
super(context);
}
public CustomViewPager (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent (MotionEvent ev) {
try {
if (Utilities.getIsZoomed())
return false;
else
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
return false;
}
}
}

Setting native resolution in JavaFX

I'd like to set the native resolution in a game that I'm working on: I'm aware this can be done in AWT. But cannot find any documentation on a way to achieve this using Glass Windowing Toolkit. I'd rather not get into AWT at all if anyone knows a better way, as I wonder how performant this solution might be.
I'm also aware of JFXPanels, and have spent some time experimenting with these. My game can scale to accommodate different screen sizes and aspects, but if a player wants to lower the amount of pixels being rendered by the GPU, I want to offer his this option by lowering the screen resolution. Rather than the player having to do this in the OS before running the game. This is fairly standard in most games.
My experiments with JFXPanels work fine but it seems I only get access to a JavaFX scene, and not to a Stage: my game often uses Stages for Modal dialogs. In this example, a JavaFX Scene object is embedded into a JFrame. My question is, how can I then obtain a way to add various modals etc into my game?
I want to work in JavaFX not Swing, and would prefer to avoid using Swing whilst coding, as it's slow firing up the AWT fullscreen. I also want to avoid using Swing layout managers to cope with my modal windows (which sounds awful).
I hesitantly post this "hack" to get JavaFX running in a AWT resolution altered
screen size, in the hope someone has better way of doing this. (The "blankingFrame" is to hide all the white screens and flickers as the screen resolution changes.)
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
//alt-F4 to exit once in full screen mode
public class DisplaysFX extends Application {
private final static boolean RUN_AS_AWT = true;
private JFrame blankingFrame;
private JFrame jFrame;
private JFXPanel fxPanel;
public static void main(String[] args) {
if (RUN_AS_AWT) {
new DisplaysFX().runTest();
} else {
Application.launch(DisplaysFX.class, args);
}
}
#Override
public void start(Stage primaryStage) {
Scene s = startFXScene();
primaryStage.setScene(s);
primaryStage.show();
}
private void initAndShowGUI() {
fxPanel = new JFXPanel();
jFrame.add(fxPanel);
Platform.runLater(() -> initFX(fxPanel));
}
private void initFX(JFXPanel fxPanel) {
Scene scene = startFXScene();
fxPanel.setScene(scene);
fxPanel.setVisible(true);
}
private Scene startFXScene() {
Group root = new Group();
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
javafx.scene.shape.Rectangle r = new javafx.scene.shape.Rectangle(0, 0, dimension.width, dimension.height);
r.setFill(javafx.scene.paint.Color.PURPLE);
root.getChildren().add(r);
Scene scene = new Scene(root);
Text text = new Text();
text.setFont(new Font(100));
text.setText("I'm some text in JavaFX");
text.setX(((dimension.width / 2) - text.getLayoutBounds().getWidth() / 2));
text.setY(((dimension.height / 2) - text.getLayoutBounds().getHeight() / 2));
root.getChildren().add(text);
return (scene);
}
private void runTest() {
new Thread(() -> createBlankJFrame()).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] devices = g.getScreenDevices();
SwingUtilities.invokeLater(() -> {
jFrame = new JFrame();
jFrame.setVisible(false);
jFrame.getContentPane().setBackground(Color.BLACK);
jFrame.setUndecorated(true);
jFrame.setResizable(false);
jFrame.setExtendedState(Frame.MAXIMIZED_BOTH);
jFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
SwingUtilities.invokeLater(() -> {
Window w = devices[0].getFullScreenWindow();
if (w != null) {
w.dispose();
}
devices[0].setFullScreenWindow(null);
jFrame.dispose();
blankingFrame.dispose();
System.exit(0);
});
}
});
devices[0].setFullScreenWindow(jFrame);
DisplayMode displayMode = new DisplayMode(1920, 1080, 32, 60); //change this if want to alter your screen to a different size
devices[0].setDisplayMode(displayMode);
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> SwingUtilities.invokeLater(() -> initAndShowGUI())).start();
}
private void createBlankJFrame() {
SwingUtilities.invokeLater(() -> {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
blankingFrame = new JFrame();
blankingFrame.getContentPane().setBackground(Color.BLACK);
blankingFrame.setMinimumSize(screenSize);
blankingFrame.setUndecorated(true);
blankingFrame.setResizable(false);
blankingFrame.setVisible(true);
});
}
}

Elements disappear after pause and resume

Testing my libGDX app in RoboVM, I have encountered a major problem. When I pause my app (by actually going to the Home screen or sending app invites via Facebook) and then return to my app, classes of my games disappear. As if it does not store data properly on the resume() method. First i though it there was a problem of my AssetLoader, but after some debugging I found that the situation is worse. Actual instances of classes and shapes disappear. As if they never existed.
After googling the issue, I found that it might be related to IOSGraphics, but I have not managed to fix the problem.
My IOSLauncher looks something like this, I have erased the Facebook & Google AdMob specific code.
protected IOSApplication createApplication() {
IOSApplicationConfiguration config = new IOSApplicationConfiguration();
config.useAccelerometer = true;
config.useCompass = true;
config.orientationPortrait = true;
config.orientationLandscape = false;
return new IOSApplication(new Game(this), config);
}
#Override
public boolean didFinishLaunching(UIApplication application,
UIApplicationLaunchOptions launchOptions) {
FBSDKApplicationDelegate.getSharedInstance().didFinishLaunching(application, launchOptions);
initialize();
return true;
}
public void initialize() {
//...
}
public static void main(String[] argv) {
NSAutoreleasePool pool = new NSAutoreleasePool();
UIApplication.main(argv, null, IOSLauncher.class);
pool.close();
}
#Override
public void showAds(boolean show) {
//...
}
#Override
public void shareOnFacebook() {
//...
}
#Override
public void inviteFriends() {
//....
}
#Override
public boolean openURL(UIApplication application, NSURL url,
String sourceApplication, NSPropertyList annotation) {
super.openURL(application, url, sourceApplication, annotation);
return FBSDKApplicationDelegate.getSharedInstance().openURL(
application, url, sourceApplication, annotation);
}
#Override
public void didBecomeActive(UIApplication application) {
super.didBecomeActive(application);
FBSDKAppEvents.activateApp();
}
#Override
public void willResignActive(UIApplication application) {
super.willResignActive(application);
}
#Override
public void willTerminate(UIApplication application) {
super.willTerminate(application);
}
}
This sounds similar to a threading bug I once encountered. I fixed it by deferring resize and resume but I'm not sure if it will help in your case. Something like this:
private boolean needResize, needResume;
private void resize (int width, int height){
needResize = true;
}
private void deferredResize ();
if (!needResize) return;
needResize = false;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
//move your resize code here
}
private void resume (){
needResume = true;
}
private void deferredResume (){
if (!needResume) return;
needResume = false;
//move your resume code here
}
public void render (){
deferredResize();
deferredResume();
//...
}
I suggest that you start looking for an alternative to RoboVM to avoid more issues in the future, as Robovm was acquired by Microsoft Xamarin (sad but true) and the framework is no longer maintained. License keys (with Libgdx) will continue to work until the 17th of April 2017, there will be no further updates to RoboVM, be it new features or bug fixes.
As far as I know, Libgdx will switch to Multi-OS engine as the default iOS backend for newly created libGDX projects in the next couple of weeks.
After a couple of days filled with headache I found the solution!
LifeCycle methods like pause & resume, hide & show are not always called When they are supposed to be called, therefore data is not stored properly. This issue can completely break your game.
This thing only occurred when testing my game on the iOS platform, mainly when I opened a 3rd party app, Facebook in this case. No such thing found on Android, though.
The only thing I changed on the iOS version was calling the mentioned methods manually to make sure it always pauses and resumes when it has to.

libgdx: Dispose Screen on Demand

Say I have a splash screen. This screen is loaded only at the beginning of the game, and afterwards I don't use it.
Is it possible to dispose this screen on demand?
I tried to dispose it right after setting the screen after the splash, and also tried to call dispose() on the hide() method.
Both of the tries rendered in an exception.
How can I dispose this screen on demand? I have there pretty heavy textures, and wanted to free the memory as soon as possible.
Example:
// SplashScreen class
class SplashScreen implements Screen {
private boolean renderingEnabled = true;
private Object lock = new Object();
#Override
public void render(float delta) {
synchronized(lock) {
if (!renderingEnabled) {
return;
}
spriteBatch.begin();
// here render the animations
spriteBatch.end();
}
}
#Override
public void dispose() {
synchronized(lock) {
renderingEnabled = false;
// here come the disposing of SpriteBatch and TextureAtlas
atlas.dispose();
atlas = null;
spriteBatch.dispose();
spriteBatch = null;
}
}
}
// The usage
game.setScreen(splashScreen);
...
// now when the splash screen animation is finished, I am calling the following from the controller:
splashScreen.dispose();
game.setScreen(mainMenuScreen);
I get the following exception:
FATAL EXCEPTION: GLThread 398
java.lang.NullPointerException
at mypackage.SplashScreen.render(SplashScreen.java:85)
at com.badlogic.gdx.Game.render(Game.java:46)
at mypackage.Game.render(MyGame.java:33)
at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:414)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1522)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)
And when I have the following dispose pattern (disposing after showing the mainMenuScreen)
// The usage
game.setScreen(splashScreen);
...
// now when the splash screen animation is finished, I am calling the following from the controller:
game.setScreen(mainMenuScreen);
splashScreen.dispose();
I get the same exception.
The exception is on spriteBatch.end() row inside the render method. The spriteBatch member turns to be null. Really confusing, since I have mutual exclusive lock on the render and the dispose methods.
What I figured out, is that LibGDX works in one thread.
What happens is the following. When the game.setScreen(mainMenuScreen) is called, and the program is in the middle of the render method of the splash screen, the thread is interrupted, and the code for setting the mainMenuScreen is executed (in the Game class), and then going back to continue the render method of the splash screen. Weird, right? After setting the new screen still to render the old screen?
Anyways, what I did is the following:
As part of the Game.setScreen(Screen screen) method, the Screen.hide() method is called on the old screen.
So what I did, I introduced a flag, hideCalled and set it to true in the hide() event inside the splash screen.
Inside the render method of the splash screen I check this flag at the beginning and at the end of the method. If the flag is true - I dispose the screen.
I was just thinking about AssetManager, maybe it would help you with this problem in a cleaner way.
Take a look here.
I see it loads assets asynchronously so maybe this problem that you described would not be happening again.
And just for some knowledge sharing, I changed screens with another approach:
I used actions attached to an image, and these actions are executed in the order they are added. First i created an action which loads the images, then, when this first action was finished, i added another action which switches the screen.
Here is my code:
#Override
public void show() {
stage.addActor(splashImage);
splashImage.addAction(Actions.sequence(Actions.alpha(0), Actions.run(new Runnable() {
#Override
public void run() {
Assets.load();
}
}), Actions.fadeIn(0.5f), Actions.delay(1), Actions.fadeOut(0.5f), Actions.run(new Runnable() {
#Override
public void run() {
Utils.changeGameScreen(new GameScreen());
// ((Game) Gdx.app.getApplicationListener()).setScreen(new
// GameScreen());
}
})));
}

How to listen for close in a JPanel

I am working with some strange legacy code. They have a custom object which implements a JPanel. This JPanel object is a secondary popup screen within the main application. The issue I'm having is to detect when the secondary popup screen is closed.
I tried to implement a WindowListener for the class, but when I try to add it, there is no JFrame associated with this object. I am assuming this is because they are using a custom object and it is an embedded popup screen.
I tried to retrieve a JFrame using:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
which fails on a NullPointerException. I have no idea why it's so difficult to detect the right hand corner "x" close button on this page! I should mention that they were able to add Mouse and Key Listeners to the table which is embedded within the JPanel. But the outside listener for the entire window is causing me troubles.
(Please bear with me, this is my first stackoverflow post and I am new to Swing.)
Thanks so very much!!
Try to call getParent() for that strange panel. It should return the parent GUI component. If this is still not your frame but some intermediate panel instead, call getParent() on it as well. The top level component returns null.
Component p = strangePanel;
while ( p != null && ! (p instanceof Window))
p = p.getParent();
((Window) p ).addWindowListener(..);
Cannot understand why you are getting "NullPointerException" at:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
In two cases this can happen:
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(null);
In your case, this is not possible as you have used this as a parameter.
Second, are you doing some other operations in above code line, like:
JFrame parentFrame = ((JFrame) SwingUtilities.getWindowAncestor(this)).someOperation();
In this case, if your this object represent the top window then you are supposed to get "NullPointerException" because ancestor of top parent is returned as "null". In other cases, I suspect you will get this exception.
Can you post a block of code where you are getting exception.
For this answer I'm making a minor assumption that the Nullpointer is not being thrown at the line that you mentioned, but rather when you attempt to add the WindowListener to the parentFrame. This is most likely because you're calling
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
before the JPanel has been added to the JFrame hierarchy.
Here's a rought code sample on how you could work around this. The thought it to wait for the panel to be notified that it has been attached to the JFrame somewhere in its hierarchy.
package test;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class HierarchyTest extends JPanel {
protected static void loadApp() {
HierarchyTest test = new HierarchyTest();
JFrame frame = new JFrame();
frame.add(test);
frame.setSize(200, 200);
frame.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
loadApp();
}
});
}
public HierarchyTest() {
this.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
// This can be optimized by checking the right flags, but I leave that up to you to look into
boolean connected = setupListenersWhenConnected();
if (connected) {
HierarchyTest.this.removeHierarchyListener(this);
}
}
});
}
protected boolean setupListenersWhenConnected() {
JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this);
if (parentFrame == null) {
return false;
}
parentFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
// Implementation here
System.out.println("This window is closing!");
}
});
return true;
}
}