GestureOverlayView: changing gesture color while swiping - java

I have only a simple MyGestureOverlayView extends GestureOverlayView in my layout; I want that when the user swipes and the GestureOverlayView recognizes the gesture, it doesn't change color. I want that the color will change only when I call overlay.setGestureColor(int color), but in fact this method changes the color that will have the next gesture when recognized by the super class, not while the user is swiping.
So my question is: how can I change the color of the gesture while the user is performing the gesture?
public class MyGestureOverlayView extends GestureOverlayView implements
GestureOverlayView.OnGesturePerformedListener,
GestureOverlayView.OnGestureListener {
private static final String TAG = MyGestureOverlayView.class.getName();
private GiocoActivity mContext;
private boolean mRecognized;
private GestureLibrary gestures;
public MyGestureOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
if (context instanceof GiocoActivity)
mContext = (GiocoActivity) context;
gestures = GestureLibraries.fromRawResource(mContext, R.raw.gesture);
if (!gestures.load())
mContext.finish();
addOnGesturePerformedListener(this);
addOnGestureListener(this);
}
#Override
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> predictions = gestures.recognize(gesture);
Log.d(TAG, "Gesture riconosciuta!");
// Sorry for my English :)
if (predictions.size() > 0 && predictions.get(0).score > 3.0) {
overlay.setGestureColor(Color.GREEN);
// The color doesn't change here, but it will change in the next gesture
String action = predictions.get(0).name;
mRecognized = true;
Toast.makeText(mContext, action, Toast.LENGTH_SHORT).show();
}
else {
overlay.setGestureColor(Color.RED);
// The color doesn't change here, but it will change in the next gesture
mRecognized = false;
}
Log.d(TAG, "Fine riconoscimento Gesture; esito: "+ mRecognized);
}
#Override
public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
Log.d(TAG, "Gesture iniziata!");
}
#Override
public void onGesture(GestureOverlayView overlay, MotionEvent event) {
Log.d(TAG, "Gesture in corso!");
if (mRecognized) // Always false for the first gesture
overlay.setGestureColor(Color.GREEN); // Same as before
else
overlay.setGestureColor(Color.RED); // Same as before
}
#Override
public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
Log.d(TAG, "Gesture finita!");
}
#Override
public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
Log.d(TAG, "Gesture cancellata!");
}
}
Besides, the callback method onGesturePerformed is called after onGestureEnded, so the boolean mRecognized in onGesture() is always false whem I perform a gesture for the first time and when it'll be recognized from the super class it'll be displayed red in any case (both recognized or not by my onGesturePerformedListener).
Sorry if I was complicate, this is my first question, and I found only one question similar to this, but it has no answers.
Thanks for your time.

Related

How can one avoid losing OpenGL ES context when switching between game states after a key event?

I am trying to make a 3D app for Android using OpenGL ES 3.0 but I have an issue when going from one state to another (in a state machine). When switching state, the OpenGL context seems to be lost in computer memory. This only happens when I push a button that will trigger state switching.
I think it is due to thread creation when switching state in Android (even though in a desktop java application, I don't recall seeing such an issue). Correct me if I'm wrong.
The exact message that appears (in red) in the debugger is :
E/libEGL: call to OpenGL ES API with no current context (logged once
per thread)
I made it so that if I don't press any key, the state will be switched after 20 sec. In that case the message does not appear (i.e. no red message if the user is not the one triggering the switch). But after the automatic switch, if I press a button I get a (normally colored) message in the debugger that states the following :
D/CompatibilityChangeReporter: Compat change id reported: 147798919;
UID 10060; state: ENABLED
Not sure if it's an issue. All I know is that if a use a workaround to track which keys are pressed (array of boolean describing each key state, with each index of the element of the table being used to represent the keycode), the keys being pressed after the switch of state get stuck on pressed (values in the table for the keys that are pressed are true).
Apologies in advance for the (probably) long code that I have to post. I tried my best to strip away and test so that the issue remains as much as I could. That's also why I wrote the whole explaination in the beginning
code-listing 1: The GLSurfaceView
public class OpenGLView extends GLSurfaceView
{
private OpenGLRenderer renderer;
public OpenGLView(Context context)
{
super(context);
init(context);
}
public OpenGLView(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context);
}
private void init(Context context)
{
setEGLContextClientVersion(3);
setPreserveEGLContextOnPause(true);
renderer = new OpenGLRenderer();
setRenderer(renderer);
Display.getInstance().setContext(context);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
renderer.onKeyDown(keyCode, event);
return super.onKeyDown(keyCode, event);
}
#Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
renderer.onKeyUp(keyCode, event);
return super.onKeyUp(keyCode, event);
}
}
code-listing 2: the GLSurfaceView.Renderer
public class OpenGLRenderer implements GLSurfaceView.Renderer
{
private final StateMachine gameStateMachine = StateMachine.getInstance();
public void onSurfaceCreated (GL10 glUnused, EGLConfig config ) {/* empty */}
public void onSurfaceChanged ( GL10 glUnused, int width, int height )
{
gameStateMachine.pushState(new HomeState(gameStateMachine));
}
public void onDrawFrame ( GL10 glUnused )
{
gameStateMachine.update();
gameStateMachine.render();
}
public void onKeyDown(int keyCode, KeyEvent event)
{
gameStateMachine.handleEvent(EventType.KEY_PRESSED, keyCode);
}
public void onKeyUp(int keyCode, KeyEvent event)
{
gameStateMachine.handleEvent(EventType.KEY_PRESSED, keyCode);
}
}
code-listing 3: HomeState.java (first state in the game state machine)
public class HomeState extends State {
public HomeState(StateMachine gsm) { super(gsm); }
#Override
public void onEnter() {/* empty */}
#Override
public void onExit() {/* empty */}
#Override
public void handleEvent(EventType type, int keyCode)
{
if(type == EventType.KEY_PRESSED)
{
switch (keyCode)
{
case KeyEvent.KEYCODE_DPAD_LEFT:
gameStateMachine.pushState(new InGameState(gameStateMachine));
}
}
}
long sometime = SystemClock.uptimeMillis();
#Override
public void update()
{
Log.d("DEBUG", ""+(SystemClock.uptimeMillis() - sometime));
if(SystemClock.uptimeMillis() - sometime > 20000)
{
gameStateMachine.pushState(new InGameState(gameStateMachine));
}
}
#Override
public void render() {/* empty */}
}
code-listing 4: inGameState.java (state one is switching to)
public class InGameState extends State {
public InGameState(StateMachine gsm) { super(gsm); }
#Override
public void onEnter()
{
glCreateShader(GL_VERTEX_SHADER);
}
#Override
public void onExit() {/* empty */}
#Override
public void handleEvent(EventType type, int keyCode)
{
if(type == EventType.KEY_PRESSED)
{
Toast.makeText(Display.getInstance().getContext(), "key pressed in InGameState!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void update() {/* empty */}
#Override
public void render() {/* empty */}
}
Note that when there is no OpenGL call in the second game state, the red message doesn't appear. But as soon as I make any OpenGL call in the second game state, the red message appears when the user triggers the switch.
Also note that, like I mentioned previously, the red message only appears when I directly check the keyCode/KeyEvent given by Android-Java. If I do something like the following code, the red message will not appear but key pressed will be stuck (through software).
code-listing 5: workaround GLSurfaceView.Renderer
public class OpenGLRenderer implements GLSurfaceView.Renderer
{
private final StateMachine gameStateMachine = StateMachine.getInstance();
private final boolean[] keyStates = new boolean[1024];
public void onSurfaceCreated (GL10 glUnused, EGLConfig config )
{
for(boolean b : keyStates)
{
b = false;
}
}
/* see code-listing 2 */
public void onDrawFrame ( GL10 glUnused )
{
gameStateMachine.handleEvent(EventType.KEY_PRESSED, keyStates);
gameStateMachine.update();
gameStateMachine.render();
}
public void onKeyDown(int keyCode, KeyEvent event)
{
keyStates[keyCode] = true;
}
public void onKeyUp(int keyCode, KeyEvent event)
{
keyStates[keyCode] = false;
}
}
When you're using GLSurfaceView, all of your interactions with OpenGLES should be downstream from OpenGLRenderer's onDrawFrame, as that's called on the thread with the OpenGLES context bound.
onKeyDown is called from some other Android thread with no OpenGLES context. If I'm following your code correctly, then it eventually invokes InGameState's onEnter function which calls glCreateShader which fails because there's no OpenGLES context.
Your workaround seems like the right sort of idea. I would recommend that you have onKeyDown and onKeyUp append to a queue of input events, which can be processed in your onDrawFrame. I think that's a more robust approach than your array of boolean keyStates as you can process input in order, and easily extend the system to have touch events when you need to.

Android - How to call method in custom View from Main Activity?

I have created a custom View that will display a circle (the idea is that the user will be able to interact with this "ball" in various ways)
From my main activity class, I want to adjust some of the "ball's" properties, in this case change its color.
My problem is that nothing happens (no errors either, the app runs but doesn't do what I want) when I try to call the various methods from my MainActivity class, but if I do it from CircleView class, it works (for example changing the color upon touch)
Here is my custom View class (CircleView.java):
public class CircleView extends View {
private int circleColor = Color.GREEN;
private Paint paint;
public CircleView(Context context) {
super(context);
init(context, null);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL:
this.circleColor = setRandomColor();
invalidate();
break;
case MotionEvent.ACTION_DOWN:
this.circleColor = setRandomColor();
invalidate();
break;
}
return super.onTouchEvent(event);
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
paint = new Paint();
paint.setAntiAlias(true);
}
public void setCircleColor(int circleColor) {
this.circleColor = circleColor;
invalidate();
}
public int setRandomColor() {
Random random = new Random();
int randomColor = Color.argb(255, random.nextInt(), random.nextInt(), random.nextInt());
return randomColor;
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//...
//someXvalue, someYvalue, someRadius are being set here
//...
paint.setColor(circleColor);
canvas.drawCircle(someXvalue, someYvalue, someRadius, paint);
}
}
And here is my MainActivity.java class:
public class MainActivity extends Activity implements
GestureDetector.OnGestureListener {
private GestureDetectorCompat mDetector;
CircleView circle;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDetector = new GestureDetectorCompat(this, this);
circle = new CircleView(this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
this.mDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
#Override
public boolean onDown(MotionEvent event) {
return true;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
circle.setCircleColor(circle.setRandomColor(0));
circle.invalidate();
return true;
}
}
I am new to Android development, and Java as well. I realize it could be something with the Context, which is something I have not fully understood yet. Could also be something with the TouchEvents. I am sure that someone out there can see my mistake. Any help is appreciated.
your circle view is not a part of activity's layout , it's just a object in memory which has no link to your activity screen so solutions
1.) Either set circle as Activity's view
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDetector = new GestureDetectorCompat(this, this);
circle = new CircleView(this);
setContentView(circle);
}
2.) you can create your <yourpackagename.CircleView ...attributes .../> tag in your activity_main.xml and then use findViewById to initialize it in your activity.
1)If all you want to do with gestures is on tap, just implement an onClickListener on your View instead.
2)You aren't actually using the GestureDetector anywhere. The way it works is you set an onTouchListener for the view you want to get gestures on, and send the events to the gesture detector. You aren't ever sending data for any view to the detector, so it will never do anything.
3)Not a bug just an oddness- why circle.setColor(circle.setRandomColor())? I would expect a function named setXXX to actually set XXX, rather than having to do it yourself later. Not following that convention will work, but make debugging and maintenance hard.
Edit: Also what #Pavneet_Singh said- your circle isn't in your layout anywhere, so it won't be on screen.

CordovaWebView messes up with onBackPressed method in android

As title says CordovaWebView and onBackPressed in android in combination are giving weird results.
I have hybrid app. My main activity has DrawerLayout and CordovaWebView.
My onBackPressed:
#Override
public void onBackPressed(){
if(drawerIsOpen){
//close drawer
}else if(webviewIsIn){
//hide webview
}else{
super.onBackPressed();
}
}
When I use android's WebView the overridden method is called as expected. And when I change to CordovaWebView the method wouldn't even get called, instead native onBackPressed would be called instead.
I have tried overriding onKeyDown and onKeyUp but it gives me the same result, the methods are just not being called.
I'm using Cordova 2.9.0 and testing device is Galaxy Note 2, Android jellybean 4.2.2
DrawerLayout has the close on back pressed functionality I've just disabled it.
I hope you guys can understand the problem.
I encountered the same issue. My solution was to derive from CordovaWebView and override public boolean onKeyUp(int keyCode, KeyEvent event) with something like this (for Cordova 3.4.0, the code is a part of the CordovaWebView.onKeyUp(int, KeyEvent)):
public class CustomCordovaWebView extends CordovaWebView {
protected View mCustomView;
protected boolean bound;
public CustomCordovaWebView(final Context context) {
super(context);
}
public CustomCordovaWebView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public CustomCordovaWebView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
#TargetApi(11)
public CustomCordovaWebView(final Context context, final AttributeSet attrs, final int defStyle, final boolean privateBrowsing) {
super(context, attrs, defStyle, privateBrowsing);
}
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// If back key
if (keyCode == KeyEvent.KEYCODE_BACK) {
// A custom view is currently displayed (e.g. playing a video)
if (mCustomView!=null){
this.hideCustomView();
}else{
// The webview is currently displayed
// If back key is bound, then send event to JavaScript
if (this.bound) {
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
return true;
} else {
// If not bound
// Go to previous page in webview if it is possible to go back
if (this.backHistory()) {
return true;
}
// If not, then invoke default behavior
else {
//this.activityState = ACTIVITY_EXITING;
//return false;
// If they hit back button when app is initializing, app should exit instead of hang until initialization (CB2-458)
// this.cordova.getActivity().finish();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this thing is closing your activity in CordovaWebView
}
}
}
} else {
return super.onKeyUp(keyCode, event);
}
return false;
}
#Override
public void hideCustomView() {
mCustomView = null;
super.hideCustomView();
}
#Override
public void showCustomView(final View view, final WebChromeClient.CustomViewCallback callback) {
mCustomView = view;
super.showCustomView(view, callback);
}
#Override
public void bindButton(final boolean override) {
bound = override;
super.bindButton(override);
}
}
If there is a better solution, I would be interested in it.

Is there any method to find out the display-changing events of android smartphone?

I'm a student of Yonsei graduate school, Korea.
I want to make a simple application that measures an time interval - between
touching the screen and display-updating.
I found that following method catch the touching event.
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if(event.getAction() == MotionEvent.ACTION_DOWN ){
...
Now I'm searching the Android APIs, but I coudn't find the method which catches
display-updating event. If you have any information about this problem,
please show mercy to me. Thank you.
Try adding a ViewTreeObserver.onDrawListener to a view in your Activity's content view. You can make a class that implements the that interface. When you get the touch event, call a method on your draw listener to record the time of the next draw event.
private MyDrawListener myDrawListener = new MyDrawListener();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
findViewById(...).getViewTreeObserver().addOnDrawListener(myDrawListener);
}
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
myDrawListener.recordNextDrawTime();
}
}
public static class MyDrawListener implements ViewTreeObserver.OnDrawListener {
private boolean recordNextDrawTime;
public void recordNextDrawTime() {
recordNextDrawTime = true;
}
#Override
public void onDraw() {
if (recordNextDrawTime) {
Log.d("MyDrawListener", "Draw time = " + System.currentTimeMillis());
recordNextDrawTime = false;
}
}
}

Disable scrolling in Osmdroid map

I have an Osmdroid MapView. Even though I have set
mapView.setClickable(false);
mapView.setFocusable(false);
the map can still be moved around. Is there a simple way to disable all interactions with the map view?
A simple solution is to do like #Schrieveslaach but with the mapView:
mapView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
My solution is similar to #schrieveslaach and #sagix, but I just extend base MapView class and add new functionality:
class DisabledMapView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : MapView(context, attrs) {
private var isUserInteractionEnabled = true
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
if (isUserInteractionEnabled.not()) {
return false
}
return super.dispatchTouchEvent(event)
}
fun setUserInteractionEnabled(isUserInteractionEnabled: Boolean) {
this.isUserInteractionEnabled = isUserInteractionEnabled
}
}
I've found a solution. You need to handle the touch events directly by setting a OnTouchListener. For example,
public class MapViewLayout extends RelativeLayout {
private MapView mapView;
/**
* #see #setDetachedMode(boolean)
*/
private boolean detachedMode;
// implement initialization of your layout...
private void setUpMapView() {
mapView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (detachedMode) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// if you want to fire another event
}
// Is detached mode is active all other touch handler
// should not be invoked, so just return true
return true;
}
return false;
}
});
}
/**
* Sets the detached mode. In detached mode no interactions will be passed to the map, the map
* will be static (no movement, no zooming, etc).
*
* #param detachedMode
*/
public void setDetachedMode(boolean detachedMode) {
this.detachedMode = detachedMode;
}
}
You could try:
mapView.setEnabled(false);
Which should disable all interactions with the map view

Categories