My (OpenGL ES 2.0) app has a splashscreen which is basically an Android View displayed over my GLSurfaceView.
I do all my initial setup on an AsyncTask and then (out of necessity), load my bitmaps/textures on the GLRendering thread in onSurfaceCreated. Once everything has loaded, I dismiss my splashscreen.
Everything works as expected apart from one thing. The splashscreen still accepts input and if the screen is touched just after the app is launched, I (obviously) get an null pointer exception.
I simply create my view like so:
splashscreen = new SplashScreen(getApplication(), width, height); //Where SplashScreen is a custom class that extends View
I then add it to my layout like so:
layout.add(myView); //My GLSurfaceView
layout.add(splashscreen); //The splashscreen View
setContectView(layout);
Once everything has loaded, I simply remove the (splashscreen) View to reveal my GLSurfaceView underneath:
layout.removeView(splashscreen);
splashscreen=null; //Null it
currently, in my onTouch method, to get around the issue, I have put a check in place like so:
public boolean onTouchEvent(MotionEvent event) {
if(splashscreen==null){
//Do touch related stuff here
}
return true;
}
This does work, however, I'm sure it's not the cleanest way to achieve what I'm after.
I've tried this:
splashscreensetClickable(false);
and
layout.setClickable(false);
as well as setEnabled(false)
Nothing seems to work, but I'm sure there must be a way of doing this.......
Generally, you will want the splash screen to prevent the user from doing anything while it is present. You can actually do this, because views in android, as in other UI frameworks, have a hierarchy in which touch events cascade.
Even though you don't want the splash screen to be interactive, you can still steal all of the touch events that occur on it, and just do nothing with them. The answer itself is to override onTouchEvent in your SplashScreen class with:
#Override
public boolean onTouchEvent(MotionEvent event) {
return true; // Splash screen is consuming all touch events on it.
}
Returning true in this basically says that nothing under this view should receive a touch event. As long as it is visible and over top of the surface view, your surface view will not get any touch events. See here for details.
Related
I have an app where I animate some views on click.
However while the animation is going on it's still possible to initiate other on click events, which in the end cause multiple problems with the animations. I would need to make it so that no new animation starts while a certain one is going on.
Let's say the animation shuffles clickable views on screen. How do I make it so that the user can't initiate a new click command for x amount of time, where x could be the duration of the animation, or some other solution with similar results.
I'm just not even sure what I'm looking for when it comes to terminology. What areas should I look into to manipulate user inputs like this? Any good source on the topic would also be welcome.
You can always disable the click event with a flag and enable it again when the animation has finished. It isn't a perfect solution, but it does the trick quite well in most cases.
For example, if you are using an Animator for your animation, you can do something like this:
private boolean isClickEnabled = true;
...
#Override
public synchronized void onClick(View v) {
if (isClickEnabled) {
// We disable the click, which will be enabled again when the animation ends
isClickEnabled = false;
...
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
// We enable the click again now that the animation has finished
isClickEnabled = true;
}
});
animator.start();
}
}
The same applies for when you use the Animation class, but with the AnimationListener instead.
Please note that, instead of saving a local boolean, you could just disable and enable the click on the view using view.setClickable(true/false) – it really depends on what you need and on your implementation, but the basic idea remains the same.
if you want to block the touch for some time try to Implement Runnable or runonuithread and than whatever view you want to block use mathod called view.setClickable(false).
I'm using MaterialShowcaseView:
https://github.com/deano2390/MaterialShowcaseView
How can I detect if a specific ShowcaseView has been touched (dismissed). In my activity, I have 3 ShowcaseViews.
I may have misunderstood your question, but here goes.
If you make a custom View that extends ShowcaseView, you might be able to override onTouchEvent(MotionEvent event) and then put whatever functionality you need inside that method.
See this page about Views: https://developer.android.com/reference/android/view/View.html
What is the proper way of completely disposing of a screen in Libgdx? Currently If I click where a button was on my previous screen the button still does what it would of done if I were on that screen. Should I be .dispose()-ing everything I can in the dispose() method? or is there a simpler way to dispose of everything on screen?
Unfortunately there is no easier way. These classes do not share any kind of common "Disposable" interface, or anything like that, to do it automatically. Everything that has a dispose() method needs to be disposed manually when it's not needed anymore.
This is also valid for the Screens themselves. When switching Screens they do not get disposed automatically, but you need to do that yourself (before calling Game.setScreen()).
On the other hand, this is not a big deal. Just look through everything in your Screen and check whether it has to be disposed or not. If there is a dispose method, call it in dispose() of the Screen.
BUT this does not explain your behaviour about invisible buttons from the last Screen. I suppose you use a Stage and used Gdx.input.setInputProcessor(stage);. This setting will not be changed when you change the screen and you have to set the input processor to the Stage of your current Screen, or to whatever that handles the input in your current Screen. That way the "old" stage will not catch any inputs anymore.
I can confirm that this issue is not passing the inpur processor a new stage. this will result in "ghost" buttons as described.
Unfortunately LibGDX API documentation says
Note that dispose() is not called automatically.
So what I do is disposing all disposables (such as Stage, Skin, Texture ... etc) inside the hide() method in the Screen because hide() is called automatically and it works very well!
example:
public class GameScreen implements Screen {
...
#Override
public void hide() {
mainStage.dispose();
playGroundStage.dispose();
controller.dispose();
labelActor.dispose();
}
...
}
In one of my activities, I want my UI to be dimmed at all times. When the used presses a navigation button (back, home, recent apps) it should act like normal, but when focus is back to my activity, it should go back to lights-out. Currently, I have lights-out working, but after I press Recent Apps, the lights stay on forever.
How do I make my app always stay in lights-out?
Figured out something that worked.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus) {
lightsOut();
}
}
}
Basically, every time my app gets focus, it forces lights out (possibly redundantly, but it doesn't really matter).
Works like a charm!
In pure Java there is MouseInputListener which I can use to work with those 2 events.
How do I do it with Android?
If I implement both events then only one is fired (onClickListener) and not the other.
Updated:
The question is not about detecting the finger movement.
I have a View (ImageView for example). I need to detect the click on this view which is onClickListener() and finger movement on this view (i.e. press, move then release the finger).
The problem here is that only onClickListener() is called and MotionEvent handler is not caught.
I need to be able to differentiate those 2 events as the main event should be finger movement and onClickListener() should just say "Don't click this view. Spin this view."
Hopefully this is more clear.
OnClickListener and OnTouchListener kinda obstruct each other, since they both consume the MotionEvents that get caught on the View.
Basically you can write a single OnTouchListener that checks for both things. You'll get supplied with the MotionEvent as an argument. Check it's action via MotionEvent.getAction(), e.g. if it equals MotionEvent.ACTION_DOWN (the user put the finger on the display). If the user releases the finger at approx. the same position (ACTION_UP), you may want to interpret that as a click. Otherwise interpret the positions that you get with the ACTION_MOVE event as a gesture.
But the framework already has some classes that do this interpreting work for you, check out the SimpleGestureDetector class and it's SimpleOnGestureListener. That has some callbacks for common events, e.g. onSingleTapConfirmed() and onFling(). All you need to do is supply the MotionEvents from your OnTouchListener to the GestureDetector.