i know that services can be started from Activity as below
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// Method to start the service
public void startService(View view) {
startService(new Intent(getBaseContext(), MyService.class));
}
}
as the startService( ) method is there in Activity class, i am thinking its not possible to call service from any java class which is not extending activity class...
if there any way we can start the service from a normal/Utility class, plz let me know??
EDIT: i have tried below suggestion as ,
package com.genedevelopers.shootthedevil;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.IBinder;
public class Devil {
// This are starting data.
public static final float initSpeed = 5;
public static final long initTimeBetweenDucks = 1800; // in milliseconds
public static Context dctx;
private boolean mIsBound = false;
// This is current speed that will be increased and current time that will be decreased.
public static float speed;
public static long timeBetweenDucks; // in milliseconds
public static long timeOfLastDuck;
public static boolean direction = true;
// Needed for speeding up the game
public static long timeBetweenSpeedups = 250; // in milliseconds
public static long timeOfLastSpeedup;
// Devil position on the screen.
public float x;
public float y;
// Speed and direction.
private float velocity;
//MusicService musicS;
//For background Music start
private MusicService2 mServ;
private ServiceConnection Scon =new ServiceConnection(){
public void onServiceConnected(ComponentName name, IBinder
binder) {
mServ = ((MusicService2.ServiceBinder)binder).getService();
}
public void onServiceDisconnected(ComponentName name) {
mServ = null;
}
};
void doBindService(){
dctx.bindService(new Intent(dctx,MusicService2.class),
Scon, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService()
{
if(mIsBound)
{
dctx.unbindService(Scon);
mIsBound = false;
}
}
//For background Music end
public Devil(int y){
this.y = y;
if(Devil.direction){
this.x = Game.screenWidth;
velocity = speed * -1;
} else {
this.x = 0 - Game.duckImage.getWidth();
velocity = speed;
}
doBindService();
// We change direction for a next devil.
Devil.direction = !Devil.direction;
dctx=HighScore.ctx;
}
/**
* Move the devil.
*/
public void update(){
this.x += velocity;
}
/**
* Draw the devil to a screen.
*
* #param canvas Canvas to draw on.
*/
public void draw(Canvas canvas){
// musicS=new MainMenu().getMusicServiceInstance();
if(velocity < 0)
canvas.drawBitmap(Game.devilImage, x, y, null);
else
canvas.drawBitmap(Game.devilRightImage, x, y, null);
}
/**
* Checks if the devil was touched/shoot.
*
* #param touchX X coordinate of the touch.
* #param touchY Y coordinate of the touch.
*
* #return True if touch coordinates are in the coordinates of devil rectangle, false otherwise.
*/
public boolean wasItShoot(int touchX, int touchY){
Rect devilRect = new Rect((int)this.x, (int)this.y, (int)this.x + Game.devilImage.getWidth(), (int)this.y + Game.devilImage.getHeight());
if(duckRect.equals(true)){
Intent music = new Intent();
music.setClass(dctx,MusicService2.class);
dctx.startService(music);
}
return duckRect.contains(touchX, touchY);
}
}
but it is not working please help me...
you can start it if you pass the context to the class (e.g. in constructor)
context.startService(intent))
In theory you can, but you need the Context to start a service. The Context usually is an Activity or a Service (What is 'Context' on Android?). You can pass a reference of Context to the utility class and start the service from there.
startService is a method of Context not Activity. As long as you have a context you can start service using it.
You can do as follows:
public class MyApp extends Application {
public static MyApp instance;
public void onCreate() {
super.onCreate()
instance = this;
}
}
Then from any place you can do MyApp.instance.startService(...).
If you do so make sure you register your app class in the manifest.
Hi thanku you all for ur replay...its working now...simple mistake if(duckRect.equals(true)){ } was never true so it was notcaling the service.
Related
So I have followed this tutorial and the code works perfectly. However I have some trouble understanding how OnTouchListener and OnTouch work together. I have spent a long time trauling this forum, websites and documentation to understand but still, I do not.
In this code, a OnTouchListener is set for ourSurfaceView, and then the onTouch is called for the activity?!
Can someone please explain the relationship of OnTouchListener and OnTouch across different activities and views. Many Thanks!
package com.games.michael.waterproofme;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
public class MainActivity extends Activity implements OnTouchListener{
MySurface ourSurfaceView;
float x,y,sX, sY, fX, fY;
Bitmap test, plus;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ourSurfaceView = new MySurface(this);
ourSurfaceView.setOnTouchListener(this);
x = 0;
y = 0;
sX = 0;
sY = 0;
fX = 0;
fY = 0;
test = BitmapFactory.decodeResource(getResources(), R.drawable.sportsball);//draw ball
plus = BitmapFactory.decodeResource(getResources(), R.drawable.plus);
setContentView(ourSurfaceView);
}
#Override
protected void onPause() {
super.onPause();
ourSurfaceView.pause();
}
#Override
protected void onResume() {
super.onResume();
ourSurfaceView.resume();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
x = event.getX();
y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
sX = event.getX();
sY = event.getY();
break;
case MotionEvent.ACTION_UP:
fX = event.getX();
fY = event.getY();
break;
}
return true;//false = finished dont loop through. true = loop through
}
public class MySurface extends SurfaceView implements Runnable{
SurfaceHolder ourHolder;
Thread ourThread = null;
boolean isRunning = false;
public MySurface(Context context){
super(context);
ourHolder = getHolder();
ourThread = new Thread(this);
ourThread.start();
}
public void pause(){
isRunning = false;
while(true){
try {
ourThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
ourThread = null;
}
public void resume(){
isRunning = true;
ourThread = new Thread(this);
ourThread.start();
}
public void run() {
while(isRunning){
if(!ourHolder.getSurface().isValid()) {/
continue;}
Canvas canvas = ourHolder.lockCanvas();
canvas.drawRGB(02, 02, 150);
if (x != 0 && y != 0){
canvas.drawBitmap(test, x-(test.getWidth()/2), y-(test.getHeight()/2), null);//bitmap, left, top, paint
}
if (sX != 0 && sY != 0){
canvas.drawBitmap(plus, sX-(plus.getWidth()/2), sY-(plus.getHeight()/2), null);//bitmap, left, top, paint
}
if (fX != 0 && fY != 0){
canvas.drawBitmap(plus, fX-(plus.getWidth()/2), fY-(plus.getHeight()/2), null);//bitmap, left, top, paint
}
ourHolder.unlockCanvasAndPost(canvas);
}
}
}
}
OnTouchListener is interface - class that implements it must override its methods. For android.view.View.OnTouchListener this is one method: boolean onTouch(View v, MotionEvent event)
When touch event occurs in your SurfaceView there is check if onTouchListener is set and if so its onTouch method is called
Can someone please explain the relationship of OnTouchListener and OnTouch across different activities and views.
Touch events on views are invoked if you register any callback to them. SurfaceView extends the View class.
ourSurfaceView.setOnTouchListener(this);
setContentView(ourSurfaceView);
So you just set the ourSurfaceView to the activity as their content view and registered the View.OnTouchListener. That means the abstract method onTouch is invoked on the ourSurfaceView instance and not the activity.
In this code, a OnTouchListener is set for ourSurfaceView, and then the onTouch is called for the activity?!
No, it delegates any touch event from ourSurfaceView instance as their content view to your MainActivity class because you registered the View.OnTouchListener
Just a simple Java example:
public class Main {
public static void main(String[] args){
SurfaceClass surfaceClass = new SurfaceClass();
ActivityClass activityClass = new ActivityClass();
surfaceClass.setOnFartListener(activityClass);
//Touch event :D
surfaceClass.fart();
}
public static class ActivityClass implements SurfaceClass.OnFartListener{
#Override
public void onFart(String kindOfFart) {
System.out.println(kindOfFart);
}
}
public static class SurfaceClass{
private SurfaceClass.OnFartListener onFartListener;
public void fart(){
if(onFartListener != null){
onFartListener.onFart("Huge One!!");
}
}
public void setOnFartListener(SurfaceClass.OnFartListener onFartListener){
this.onFartListener = onFartListener;
}
public interface OnFartListener{
void onFart(String kindOfFart);
}
}}
See touch event below :D
javac Main.java && java Main
I'm trying to set the camera position to the position of a body, but when I do this, the body will jump very noticeably. You can see this with the debug renderer but I have a sprite attached in my code. The jump is always in the direction that the sprite is headed. I've got a fixed time step with interpolation, and I update the sprites position to an interpolated value of the current and last position of a box2d body every frame. Then set the camera to the interpolated position.
import java.util.Random;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Input.Orientation;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.JointEdge;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;
import com.badlogic.gdx.utils.viewport.FitViewport;
public class GameScreen implements Screen {
private static learnGame game;
private static OrthographicCamera camera;
private static FitViewport viewport;
private static Random rand;
private static BitmapFont font;
private static Vector3 touch;
private static double frameTime;
private static double accumulator;
private static float animTime = 0f;
private static float step = 1f / 60f;
private static boolean killBody;
private static Buttons buttons;
public static Player tom;
public static InputMultiplexer multiplexer;
public static Bodies world;
public static boolean paused;
private static Sprite tomSprite = new Sprite(new Texture(Gdx.files.internal("chars/bob.png")));
public GameScreen(learnGame learngame) {
GameScreen.game = learngame;
multiplexer = new InputMultiplexer();
Gdx.input.setInputProcessor(multiplexer);
camera = new OrthographicCamera();
viewport = new FitViewport(40, 22.5f, camera);
buttons = new Buttons(game, multiplexer, viewport); //HUD stuff
world = new Bodies(viewport, multiplexer); //creates a box2d world
tom = new Player(world.box2d, 10, 15, 1f, multiplexer); //creates a body with a CircleShape with a radius of 1. Catches user input to apply forces to the body
Assets.loadSprites();
world.box2d.getBodies(world.bodies);
font = new BitmapFont();
touch = new Vector3();
rand = new Random();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
learnGame.batch.setProjectionMatrix(camera.combined);
frameTime = 0;
if (!paused) {
frameTime = Math.min(Gdx.graphics.getRawDeltaTime(), 0.25);
accumulator += frameTime;
tom.update(); //here I apply forces to the body that I attach a camera to
generalUpdate(delta, touch, camera);
updatePositions(); //get previous positions for interpolation
while (accumulator >= step) {
world.box2d.step(step, 6, 2);
accumulator -= step;
interpolate((float) (accumulator / step));
}
world.box2d.clearForces();
learnGame.batch.begin();
if (tom.getBody().isActive()) {
tomSprite.setSize(tom.getHeight(), tom.getHeight());
tomSprite.setOriginCenter();
tomSprite.setRotation(Assets.tom.angle);//this doesn't work right but not the point.
tomSprite.setPosition(Assets.tom.pos.x, Assets.tom.pos.y);
tomSprite.draw(learnGame.batch);
}
learnGame.batch.end();
cameraUpdate(); //update the camera to the same position as the sprite
} else { //else pause the game
learnGame.batch.begin();
learnGame.batch.draw(Assets.pauset, viewport.getCamera().position.x
- (viewport.getCamera().viewportWidth / 2), viewport.getCamera().position.y
- (viewport.getCamera().viewportHeight / 2), viewport.getWorldWidth(), viewport.getWorldHeight());
learnGame.batch.end();
if (Gdx.input.isKeyJustPressed(Keys.SPACE) || Gdx.input.justTouched()) {
paused = false;
}
}
//destroy fixtures and bodies outside of the world step
for (Fixture fixture : Bodies.fixturesToDestroy) {
if (Bodies.destroyJoint == true) {
world.box2d.destroyJoint(Bodies.joint);
Bodies.joint = null;
Bodies.destroyJoint = false;
}
fixture.getBody().destroyFixture(fixture);
Bodies.fixturesToDestroy.removeValue(fixture, true);
}
for (Body body : Bodies.bodiesToDestroy) {
world.box2d.destroyBody(body);
body.setActive(false);
Bodies.bodiesToDestroy.removeValue(body, true);
}
}
#Override
public void show() {
Assets.firstSound.play();
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true);
Assets.reloadFont();
}
#Override
public void pause() {
paused = true;
}
#Override
public void resume() {
}
#Override
public void hide() {
paused = true;
}
#Override
public void dispose() {
Bodies.box2d.dispose();
Bodies.debugRenderer.dispose();
Buttons.stage.dispose();
Assets.cFrame.getTexture().dispose();
Assets.firstSound.dispose();
System.out.println("disposed");
}
public void generalUpdate(float delta, Vector3 touch, OrthographicCamera camera) {
if (Gdx.input.isKeyPressed(Keys.PAGE_UP)) {
camera.zoom -= 2f * Gdx.graphics.getDeltaTime();
;
} else if (Gdx.input.isKeyPressed(Keys.PAGE_DOWN)) {
camera.zoom += 2f * Gdx.graphics.getDeltaTime();
}
}
public static void cameraUpdate() {
camera.position.set(Assets.tom.pos, 0);
System.out.println("cam:" + Assets.tom.pos.x);
camera.update();
}
public static void kill() {
world.box2d.dispose();
world.debugRenderer.dispose();
Assets.cFrame.getTexture().dispose();
}
public void updatePositions() {
for (MySprite name : Assets.spriteList) {
name.prevPos = name.body.getTransform().getPosition();
name.prevAngle = name.body.getTransform().getRotation();
}
}
public void interpolate(float alpha) {
for (MySprite name : Assets.spriteList) {
name.pos.x = (name.body.getTransform().getPosition().x) * alpha + name.prevPos.x * (1.0f - alpha);
name.pos.y = (name.body.getTransform().getPosition().y) * alpha + name.prevPos.y * (1.0f - alpha);
name.angle = (name.body.getTransform().getRotation() * alpha + name.prevAngle * (1.0f - alpha));
}
}
}
I have tested my interpolation implementation without the camera moving and it appears to work fine. I've been able to test this on my desktop and on my android. The sprite "jumps" a lot more on my android but it happens on both devices. Not sure where I went wrong here, would really appreciate some input!
It's an old question, I know, but I just solved my Problem today.
I have implemented a fixed Timestep and interpolation, but my body lagged and jittered and jumped like there would be no tomorrow.
Please do following things to your camera Movement:
Use the interpolated Position from the body, NOT the original body position.
Use camera.position.slerp(x, y, z, alpha) to do a smooth movement. (You need to play a bit with the variables.
A really simple Example:
static Vector3 desiredPosition;
static {
desiredPosition = new Vector3(0, 0, 0);
}
public static moveCam(Player player, Camera camera){
desiredPosition.x = player.interpolatedPos.x;
desiredPosition.y = player.interpolatedPos.y;
camera.slerp(desiredPosition, Gdx.graphics.getDeltaTime * 5);
camera.update();
}
and in your render do the following
render(float delta){
moveCam(player, camera);
}
(Instant position setting caused jitter for my moving Object)
I wanted to give up my project, because of this bug. But I managed to do it. I hope it'll help people out there.
€dit: A guess why the jumping body is happening:
The interpolated position is not synchron with the camera (set Position then update world(now interpolated pos is offset with camera), then draw)
But I'm not sure with this.
You must have used the facebook android app. I want to implement the same navigation that the facebook is using like:
on swipping left the menu is opened and swipping right the chat list is shown. And in the middle the activities and layouts keep on changing. But I am confused how to make such a navigation. (Most noticable thing is that the middle page is half shown when the swipping is done on left or right.) help?
You have two good options.
Navigation Drawer: http://developer.android.com/design/patterns/navigation-drawer.html
Sliding Drawer: https://github.com/jfeinstein10/SlidingMenu
You can make multiple navigation drawers for your use case.
I installed the FB app an hour ago. When you tap the icon in the top-right corner of the main content, the main content moves left to reveal content on the right. The revealed content remains fixed and behind the main content during the move.
I wrote this lightweight abstraction based on the above analysis:
package org.yourdomain.app;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
abstract public class NavigatorActivity extends Activity {
static public final String TAG = "NavigatorActivity";
static public final Boolean DEBUG_LIFECYCLE = false;
/**
* True when menu is on and vice versa
*/
private boolean mToggled;
/**
* Percentage of the main content to keep shown
*/
private float mDistance;
/**
* Navigator listener
*/
private NavigatorListener mListener;
/**
* Content view
*/
private ViewGroup mContentLayout;
/**
* Menu frame
*/
private FrameLayout mMenuLayout;
/**
* Main frame
*/
private FrameLayout mMainLayout;
/**
* Speed of toggle animation
*/
private long mSpeed;
/**
* Width of the content view
*/
private int mContentWidth;
/**
* Height of the content view
*/
private int mContentHeight;
/**
* The current distance to slide the main frame
*/
private int mToggleDistance;
/**
*
*/
public NavigatorActivity() {
mToggled = false;
mDistance = 80;
mSpeed = 300l;
}
/**
* Speed setter.
*
* Controls how fast the menu frame is revealed.
*
* #param speed
*/
public void setSpeed(long speed) {
mSpeed = speed;
}
/**
* Distance setter.
*
* The distance is the % of the oriented screen to pull the main frame in order to reveal the
* menu frame.
*
* #param distance
*/
public void setDistance(float distance) {
mDistance = distance;
}
/**
* Navigator listener setter.
*
* #param listener
*/
public void setNavigatorListener(NavigatorListener listener) {
mListener = listener;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (DEBUG_LIFECYCLE) Log.v(TAG, "onCreate " + this + ": " + savedInstanceState);
initContentLayout();
setContentView(mContentLayout);
initMenuLayout();
mContentLayout.addView(mMenuLayout);
initMainLayout();
mContentLayout.addView(mMainLayout);
}
/**
* Initializes the main frame.
*/
private void initMainLayout() {
int hw = RelativeLayout.LayoutParams.MATCH_PARENT;
mMainLayout = new FrameLayout(this) {
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(mContentWidth, mContentHeight);
}
};
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(hw, hw);
p.addRule(RelativeLayout.ALIGN_PARENT_TOP);
p.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
mMainLayout.setLayoutParams(p);
}
/**
* Initializes the menu frame.
*/
private void initMenuLayout() {
int hw = RelativeLayout.LayoutParams.MATCH_PARENT;
mMenuLayout = new FrameLayout(this);
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(hw, hw);
p.addRule(RelativeLayout.ALIGN_PARENT_TOP);
p.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
mMenuLayout.setLayoutParams(p);
}
/**
* Initialize the activity's content layout.
*/
private void initContentLayout() {
final int hw = RelativeLayout.LayoutParams.MATCH_PARENT;
mContentLayout = new RelativeLayout(this) {
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mContentWidth = MeasureSpec.getSize(widthMeasureSpec);
mContentHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(mContentWidth, mContentHeight);
mToggleDistance = Math.round((mDistance / 100f) * (float) mContentWidth);
}
};
mContentLayout.setLayoutParams(new RelativeLayout.LayoutParams(hw, hw));
}
/**
* Inflates two project XML layout and adds them to the menu and main layout frames.
*
* #param menuLayoutResId
* #param mainLayoutResId
*/
protected void setContentViews(int menuLayoutResId, int mainLayoutResId) {
final LayoutInflater inflater = getLayoutInflater();
inflater.inflate(menuLayoutResId, mMenuLayout);
inflater.inflate(mainLayoutResId, mMainLayout);
}
/**
* Toggle the menu frame.
*/
final public void toggleNavigator() {
final boolean isToggled = mToggled = !mToggled;
if (DEBUG_LIFECYCLE) Log.v(TAG, "toggleNavigatorMenu " + this);
RelativeLayout.LayoutParams menuParams = (RelativeLayout.LayoutParams) mMenuLayout.getLayoutParams();
menuParams.setMargins(mContentWidth - mToggleDistance, 0, 0, 0);
final RelativeLayout.LayoutParams mainParams = (RelativeLayout.LayoutParams) mMainLayout.getLayoutParams();
ValueAnimator animator = ValueAnimator.ofInt(mainParams.leftMargin, mToggled ? -mToggleDistance : 0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mainParams.leftMargin = (Integer) valueAnimator.getAnimatedValue();
mMainLayout.requestLayout();
}
});
animator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {}
#Override
public void onAnimationEnd(Animator animation) {
if (mListener != null) {
mListener.onNavigatorToggled(isToggled);
}
}
#Override
public void onAnimationCancel(Animator animation) {}
#Override
public void onAnimationRepeat(Animator animation) {}
});
animator.setDuration(mSpeed);
animator.start();
}
public static interface NavigatorListener {
public void onNavigatorToggled(boolean isToggled);
}
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
toggleNavigator();
}
return super.onKeyUp(keyCode, event);
}
}
Example usage:
package org.yourdomain.project;
import android.os.Bundle;
import org.yourdomain.app.NavigatorActivity;
public class YourActivity extends NavigatorActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setDistance(80); // will leave 20% of the main content in view
setNavigatorListener(new NavigatorListener() {
#Override
public void onNavigatorToggled(boolean isToggled) {
// Load content dynamically, like FB does?
}
});
setContentViews(R.layout.layout_menu, R.layout.activity_your);
}
}
compileSdkVersion 20
minSdkVersion 16
targetSdkVersion 20
Clicking on the physical menu button on your Android device will toggle the navigator.
I plan on using this in a couple of startup projects of my own.
Let me know if you have any questions. I hope this helps you.
I'm learning Android programming.
So I managed to implement a simple app that rolls a ball over the screen if you tilt yout phone. But right now it is as simple as:
if roll > 0 then xpos++ else xpos-- end and the same for ypos.
So I want to calculate a more exact direction and also I would like the ball to roll faster the more the phone is tilting.
So if I know the tilt in the roll direction and the pitch direction, how do I calculate the direction and speed of the ball?
Here is how:
package benyamephrem.tiltball;
import android.graphics.Point;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Handler;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.SensorEventListener;
import android.widget.Toast;
public class TiltBall extends ActionBarActivity {
BallView mBallView = null;
Handler RedrawHandler = new Handler(); //so redraw occurs in main thread
Timer mTmr = null;
TimerTask mTsk = null;
int mScrWidth, mScrHeight;
android.graphics.PointF mBallPos, mBallSpd;
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE); //hide title bar
//set app to full screen and keep screen on
getWindow().setFlags(0xFFFFFFFF, LayoutParams.FLAG_FULLSCREEN | LayoutParams.FLAG_KEEP_SCREEN_ON);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tilt_ball);
//create pointer to main screen
final FrameLayout mainView = (android.widget.FrameLayout) findViewById(R.id.main_view);
//get screen dimensions
Display display = getWindowManager().getDefaultDisplay();
mScrWidth = display.getWidth();
mScrHeight = display.getHeight();
mBallPos = new android.graphics.PointF();
mBallSpd = new android.graphics.PointF();
//create variables for ball position and speed
mBallPos.x = mScrWidth / 2;
mBallPos.y = mScrHeight / 5;
mBallSpd.x = 0;
mBallSpd.y = 0;
//create initial ball
mBallView = new BallView(this, mBallPos.x, mBallPos.y, 75);
mainView.addView(mBallView); //add ball to main screen
mBallView.invalidate(); //call onDraw in BallView
//listener for accelerometer, use anonymous class for simplicity
((SensorManager) getSystemService(Context.SENSOR_SERVICE)).registerListener(
new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent event) {
//set ball speed based on phone tilt (ignore Z axis)
//***Change speed here by multiplying event values by bigger numbers***
mBallSpd.x = -event.values[0] * (30/10);
mBallSpd.y = event.values[1] * (30/10);
//timer event will redraw ball
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
} //ignore
},
((SensorManager) getSystemService(Context.SENSOR_SERVICE))
.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0),
SensorManager.SENSOR_DELAY_NORMAL);
//listener for touch event
mainView.setOnTouchListener(new android.view.View.OnTouchListener() {
public boolean onTouch(android.view.View v, android.view.MotionEvent e) {
//set ball position based on screen touch
mBallPos.x = e.getX();
mBallPos.y = e.getY();
//timer event will redraw ball
return true;
}
});
} //OnCreate
//listener for menu button on phone
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("Exit"); //only one menu item
return super.onCreateOptionsMenu(menu);
}
//listener for menu item clicked
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
if (item.getTitle() == "Exit") //user clicked Exit
finish(); //will call onPause
return super.onOptionsItemSelected(item);
}
//For state flow see http://developer.android.com/reference/android/app/Activity.html
#Override
public void onPause() //app moved to background, stop background threads
{
mTmr.cancel(); //kill\release timer (our only background thread)
mTmr = null;
mTsk = null;
super.onPause();
}
#Override
public void onResume() //app moved to foreground (also occurs at app startup)
{
//create timer to move ball to new position
mTmr = new Timer();
mTsk = new TimerTask() {
public void run() {
//if debugging with external device,
// a log cat viewer will be needed on the device
Log.d("TiltBall", "Timer Hit - " + mBallPos.x + ":" + mBallPos.y);
//move ball based on current speed
mBallPos.x += mBallSpd.x;
mBallPos.y += mBallSpd.y;
//if ball goes off screen, reposition to opposite side of screen
if (mBallPos.x > mScrWidth) mBallPos.x = 0;
if (mBallPos.y > mScrHeight) mBallPos.y = 0;
if (mBallPos.x < 0) mBallPos.x = mScrWidth;
if (mBallPos.y < 0) mBallPos.y = mScrHeight;
//update ball class instance
mBallView.x = mBallPos.x;
mBallView.y = mBallPos.y;
//redraw ball. Must run in background thread to prevent thread lock.
RedrawHandler.post(new Runnable() {
public void run() {
mBallView.invalidate();
}
});
}
}; // TimerTask
mTmr.schedule(mTsk, 10, 10); //start timer
super.onResume();
} // onResume
#Override
public void onDestroy() //main thread stopped
{
super.onDestroy();
//wait for threads to exit before clearing app
System.runFinalizersOnExit(true);
//remove app from memory
android.os.Process.killProcess(android.os.Process.myPid());
}
//listener for config change.
//This is called when user tilts phone enough to trigger landscape view
// we want our app to stay in portrait view, so bypass event
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}
} //TiltBallActivity
Here is the BallView class;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
public class BallView extends View {
public float x;
public float y;
private final int r;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//construct new ball object
public BallView(Context context, float x, float y, int r) {
super(context);
//color hex is [transparncy][red][green][blue]
mPaint.setColor(0xFF1325E0); //not transparent. color is blue
this.x = x;
this.y = y;
this.r = r; //radius
}
//qcalled by invalidate()
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, r, mPaint);
}
public int getRadius(){
return r;
}
}
This is not my code but I forgot its source so credits to whoever made it. I am using it for a current project I'm working on.
I am working on a game and have run into some issues. My architecture is something like this:
class GameView is used to draw bitmaps on my surfaces
class GameLoopThread is used to implement my game loop (if it wasn't obvious...)
class MovementUtils is used to hold all of my utilities related to moving objects
I want to house methods like gravity and movement controls in MovementUtils, but I'm having trouble actually updating the values in GameView. I tried using an intent, to no avail. I'll post my code, and maybe someone can show me what I should do. Also, ignore the Accelerometer lines, that's another problem entirely...
GameView.java
package com.example.connorgame;
import java.util.EventObject;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class GameView extends SurfaceView {
private Bitmap platform;
private Bitmap character;
private Bitmap background;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private MainActivity mainactivity;
private MovementUtils moveutil;
public float charX = 0;
public float charY = 0;
private boolean isFalling = true;
private boolean isJumping = false;
private float initialJumpY = 0;
private int initialFrame;
private int currentFrame;
private int frameDifference;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
mainactivity = (MainActivity) context;
moveutil = new MovementUtils();
holder = getHolder();
holder.addCallback(new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
platform = BitmapFactory.decodeResource(getResources(), R.drawable.platform);
character = BitmapFactory.decodeResource(getResources(), R.drawable.character);
background = BitmapFactory.decodeResource(getResources(), R.drawable.background);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(background, 0, 0, null);
canvas.drawBitmap(platform, 30, 700, null);
canvas.drawBitmap(character, charX, charY, null);
moveutil.gravity(charY, isFalling);
if (charY > getHeight() - character.getHeight()) {
initialFrame = gameLoopThread.numFrames;
initialJumpY = charY;
isFalling = false;
isJumping = true;
}
if (isJumping == true && isFalling == false) {
currentFrame = gameLoopThread.numFrames;
frameDifference = (currentFrame - initialFrame);
charY = charY - 5;
if (charY == initialJumpY - 100) {
isJumping = false;
isFalling = true;
}
}
}
}
MovementUtils.java
package com.example.connorgame;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
public class MovementUtils {
public void gravity (float charY, boolean isFalling) {
if(isFalling == true){
charY = charY + 5;
}
}
}
If I understand what you're doing correctly, you're trying to have charY in the GameView class modified by the gravity function. The issue is that float is a primitive, so it is based by value and only the local copy will be modified. If you passed an object instead, the object would be modified.
One solution is probably to just return the new Y position instead of trying to make gravity() change the Y position.
Another solution is to pass the GameView to MovementUtils and let MovementUtils modify it.
In other words, you would pass the GameView like this new MovementUtils(this);
And the gravity function would call a void setCharY(int y); in GameView.
public class MovementUtils {
private GameView gameView;
public MovementUtils(GameView view) {
this.gameView = view;
}
public void gravity (float charY, boolean isFalling) {
if(isFalling == true){
charY = charY + 5;
gameView.setCharY(charY);
}
}
}
Java only supports passing by value
So when you call moveutil.gravity(charY, isFalling);
and change it within the function, e.g.
public void gravity (float charY, boolean isFalling) {
if(isFalling == true){
charY = charY + 5;
}
Such change only occur in the local copy of the variable charY and does not affect the instance variable charY defined in your GameView class
To solve this, you can define your variables as objects as well as the argument to your functions, e.g.
public void gravity (Float charY, boolean isFalling) {}
And in your GameView class:
private Float charX = 0; // why you define them public !?
private Float charY = 0;
Alternatively, you can pass an instance of your GameView class or create a ValueObject class (similar to Context object in android) and use it to pass arguments