Related
I have a custom view which is totally Java based ( no XML ) code is below and i want to add this inside a new XML based layout ..Please guide how to do this however i also want that when it get displayed the screen will show 50 % xml layout and 50 % customview is it even possible ? please see below picture for more clarity
follows is the Java code of the animation :
public class AccelerometerPlayActivity extends Activity {
private SimulationView mSimulationView;
private SensorManager mSensorManager;
private PowerManager mPowerManager;
private WindowManager mWindowManager;
private Display mDisplay;
private WakeLock mWakeLock;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get an instance of the SensorManager
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
// Get an instance of the PowerManager
mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
// Get an instance of the WindowManager
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
// Create a bright wake lock
mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass()
.getName());
// instantiate our simulation view and set it as the activity's content
mSimulationView = new SimulationView(this);
mSimulationView.setBackgroundResource(R.drawable.wood);
setContentView(mSimulationView);
}
#Override
protected void onResume() {
super.onResume();
/*
* when the activity is resumed, we acquire a wake-lock so that the
* screen stays on, since the user will likely not be fiddling with the
* screen or buttons.
*/
mWakeLock.acquire();
// Start the simulation
mSimulationView.startSimulation();
mSimulationView.setLayoutParams(new FrameLayout.LayoutParams(700, 300));
}
#Override
protected void onPause() {
super.onPause();
/*
* When the activity is paused, we make sure to stop the simulation,
* release our sensor resources and wake locks
*/
// Stop the simulation
mSimulationView.stopSimulation();
// and release our wake-lock
mWakeLock.release();
}
class SimulationView extends LinearLayout implements SensorEventListener {
// diameter of the balls in meters
private static final float sBallDiameter = 0.004f;
private static final float sBallDiameter2 = sBallDiameter * sBallDiameter;
private final int mDstWidth;
private final int mDstHeight;
private Sensor mAccelerometer;
private long mLastT;
private float mXDpi;
private float mYDpi;
private float mMetersToPixelsX;
private float mMetersToPixelsY;
private float mXOrigin;
private float mYOrigin;
private float mSensorX;
private float mSensorY;
private float mHorizontalBound;
private float mVerticalBound;
private final ParticleSystem mParticleSystem;
/*
* Each of our particle holds its previous and current position, its
* acceleration. for added realism each particle has its own friction
* coefficient.
*/
class Particle extends View {
private float mPosX = (float) Math.random();
private float mPosY = (float) Math.random();
private float mVelX;
private float mVelY;
public Particle(Context context) {
super(context);
}
public Particle(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Particle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public Particle(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void computePhysics(float sx, float sy, float dT) {
final float ax = -sx/5;
final float ay = -sy/5;
mPosX += mVelX * dT + ax * dT * dT / 2;
mPosY += mVelY * dT + ay * dT * dT / 2;
mVelX += ax * dT;
mVelY += ay * dT;
}
/*
* Resolving constraints and collisions with the Verlet integrator
* can be very simple, we simply need to move a colliding or
* constrained particle in such way that the constraint is
* satisfied.
*/
public void resolveCollisionWithBounds() {
final float xmax = mHorizontalBound;
final float ymax = mVerticalBound;
final float x = mPosX;
final float y = mPosY;
if (x > xmax) {
mPosX = xmax;
mVelX = 0;
} else if (x < -xmax) {
mPosX = -xmax;
mVelX = 0;
}
if (y > ymax) {
mPosY = ymax;
mVelY = 0;
} else if (y < -ymax) {
mPosY = -ymax;
mVelY = 0;
}
}
}
/*
* A particle system is just a collection of particles
*/
class ParticleSystem {
static final int NUM_PARTICLES = 5;
private Particle mBalls[] = new Particle[NUM_PARTICLES];
ParticleSystem() {
/*
* Initially our particles have no speed or acceleration
*/
for (int i = 0; i < mBalls.length; i++) {
mBalls[i] = new Particle(getContext());
mBalls[i].setBackgroundResource(R.drawable.ball);
mBalls[i].setLayerType(LAYER_TYPE_HARDWARE, null);
addView(mBalls[i], new ViewGroup.LayoutParams(mDstWidth, mDstHeight));
}
}
/*
* Update the position of each particle in the system using the
* Verlet integrator.
*/
private void updatePositions(float sx, float sy, long timestamp) {
final long t = timestamp;
if (mLastT != 0) {
final float dT = (float) (t - mLastT) / 1000.f /** (1.0f / 1000000000.0f)*/;
final int count = mBalls.length;
for (int i = 0; i < count; i++) {
Particle ball = mBalls[i];
ball.computePhysics(sx, sy, dT);
}
}
mLastT = t;
}
/*
* Performs one iteration of the simulation. First updating the
* position of all the particles and resolving the constraints and
* collisions.
*/
public void update(float sx, float sy, long now) {
// update the system's positions
updatePositions(sx, sy, now);
// We do no more than a limited number of iterations
final int NUM_MAX_ITERATIONS = 1;
/*
* Resolve collisions, each particle is tested against every
* other particle for collision. If a collision is detected the
* particle is moved away using a virtual spring of infinite
* stiffness.
*/
boolean more = true;
final int count = mBalls.length;
for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) {
more = false;
for (int i = 0; i < count; i++) {
Particle curr = mBalls[i];
for (int j = i + 1; j < count; j++) {
Particle ball = mBalls[j];
float dx = ball.mPosX - curr.mPosX;
float dy = ball.mPosY - curr.mPosY;
float dd = dx * dx + dy * dy;
// Check for collisions
if (dd <= sBallDiameter2) {
/*
* add a little bit of entropy, after nothing is
* perfect in the universe.
*/
dx += ((float) Math.random() - 0.5f) * 0.0001f;
dy += ((float) Math.random() - 0.5f) * 0.0001f;
dd = dx * dx + dy * dy;
// simulate the spring
final float d = (float) Math.sqrt(dd);
final float c = (0.5f * (sBallDiameter - d)) / d;
final float effectX = dx * c;
final float effectY = dy * c;
curr.mPosX -= effectX;
curr.mPosY -= effectY;
ball.mPosX += effectX;
ball.mPosY += effectY;
more = true;
}
}
curr.resolveCollisionWithBounds();
}
}
}
public int getParticleCount() {
return mBalls.length;
}
public float getPosX(int i) {
return mBalls[i].mPosX;
}
public float getPosY(int i) {
return mBalls[i].mPosY;
}
}
public void startSimulation() {
/*
* It is not necessary to get accelerometer events at a very high
* rate, by using a slower rate (SENSOR_DELAY_UI), we get an
* automatic low-pass filter, which "extracts" the gravity component
* of the acceleration. As an added benefit, we use less power and
* CPU resources.
*/
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
}
public void stopSimulation() {
mSensorManager.unregisterListener(this);
}
public SimulationView(Context context) {
super(context);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mXDpi = metrics.xdpi;
mYDpi = metrics.ydpi;
mMetersToPixelsX = mXDpi / 0.0254f;
mMetersToPixelsY = mYDpi / 0.0254f;
// rescale the ball so it's about 0.5 cm on screen
mDstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f);
mDstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f);
mParticleSystem = new ParticleSystem();
Options opts = new Options();
opts.inDither = true;
opts.inPreferredConfig = Bitmap.Config.RGB_565;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// compute the origin of the screen relative to the origin of
// the bitmap
mXOrigin = (w - mDstWidth) * 0.5f; //#nyy this is changing the rect of balls to simulate
mYOrigin = (h - mDstHeight) * 0.5f;
mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f);//#nyy this is changing the rect of balls to simulate
mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
/*
* record the accelerometer data, the event's timestamp as well as
* the current time. The latter is needed so we can calculate the
* "present" time during rendering. In this application, we need to
* take into account how the screen is rotated with respect to the
* sensors (which always return data in a coordinate space aligned
* to with the screen in its native orientation).
*/
switch (mDisplay.getRotation()) {
case Surface.ROTATION_0:
mSensorX = event.values[0];
mSensorY = event.values[1];
break;
case Surface.ROTATION_90:
mSensorX = -event.values[1];
mSensorY = event.values[0];
break;
case Surface.ROTATION_180:
mSensorX = -event.values[0];
mSensorY = -event.values[1];
break;
case Surface.ROTATION_270:
mSensorX = event.values[1];
mSensorY = -event.values[0];
break;
}
}
#Override
protected void onDraw(Canvas canvas) {
/*
* Compute the new position of our object, based on accelerometer
* data and present time.
*/
final ParticleSystem particleSystem = mParticleSystem;
final long now = System.currentTimeMillis();
final float sx = mSensorX;
final float sy = mSensorY;
particleSystem.update(sx, sy, now);
final float xc = mXOrigin;
final float yc = mYOrigin;
final float xs = mMetersToPixelsX;
final float ys = mMetersToPixelsY;
final int count = particleSystem.getParticleCount();
for (int i = 0; i < count; i++) {
/*
* We transform the canvas so that the coordinate system matches
* the sensors coordinate system with the origin in the center
* of the screen and the unit is the meter.
*/
final float x = xc + particleSystem.getPosX(i) * xs;
final float y = yc - particleSystem.getPosY(i) * ys;
particleSystem.mBalls[i].setTranslationX(x);
particleSystem.mBalls[i].setTranslationY(y);
}
// and make sure to redraw asap
invalidate();
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
}
you are just setting setContentView(mSimulationView) to mSimulationView only.
you have include all content that is custom layout and linearlayout in xml file.
setContentView(R.layout.your_xml)
If i understood you correctly then you don't need to override the linearLayout your simulation view would look like this.
class SimulationView extends View implements SensorEventListener, ParticleSystemListener {
private final ParticleSystem mParticleSystem;
/* all your normal initialization */
public SimulationView(Context context) {
super(context);
/* all your normal initialization */
mParticleSystem = new mParticleSystem(this); // initialize the system here whatever resources you need for your particles if you use bitmaps you can pass a bitmap here
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// we notify the particleSystem that the size has changed so i can adjust it's bounds
mParticleSystem.setBounds(left, top, right, bottom);
}
#Override
public void onSensorChanged(SensorEvent event) {
/* all your normal code */
mParticleSystem.updatePositions(x, y, time)
}
#Override
public void needNewFrame(){
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
/* draw your background or just call super witch will draw it for you */
mParticleSystem.drawAllParticles(canvas);
}
}
Create an interface to to get notified when we particle system wants to draw a new frame or simply pass the view and call invalidate on it;
interface ParticleSystemListener {
public void needNewFrame();
}
Your particleSystem should do all the computation and handle adding and removing particles as well as passing the dway command down to the particles.
class ParticleSystem {
private float mParticleSystemListener;
private Particle mBalls[] = new Particle[NUM_PARTICLES];
private Rect mBounds = new Rect();
public ParticleSystem (ParticleSystemListener listener) {
mParticleSystemListener = listener;
/* all your normal initialization */
}
// our particle system keeps track of the bounds in which it draws the particles and tells the particles when it has changed
public void setBounds(left, top, right, bottom){
mBounds.left = left;
mBounds.top = top;
mBounds.right = right;
mBounds.bottom = bottom;
for(ball : mBalls){
ball.boundsChanged();
}
// after we computed new positions is time to refresh the view
mParticleSystemListener.needNewFrame();
}
private void updatePositions(float sx, float sy, long timestamp){
/* all your normal computation*/
for(ball : mBalls){
ball.computePhysics(sx, sy, dT);
}
// after we computed new positions is time to refresh the view
mParticleSystemListener.needNewFrame();
}
// the view wants us to draw on this canvas so we just pass the canvas to the particles
public void drawAllParticles(canvas){
for(ball : mBalls){
ball.draw(canvas);
}
}
}
All the state is gonna reside in the particle class. the particle class should know its position and how to draw itself
class Particle {
private Paint mPaint;
private Bitmap mSprite;
private Rect mBounds;
public Particle(Rect bounds) {
mBounds = bounds;
/* initialize your paint here or get a bitmap passed in */
}
public void computePhysics(float sx, float sy, float dT);
public void boundsChanged(){
// move your particle since the bounds have changed
}
public void onDraw(Canvas canvas) {
canvas.drawCircle(x, y ,r, paint); // draw a circle at position
or
canvas.drawBitmap(mSprite, float left, float top, some paint or null) // draw a sprite
}
}
As you can see this is not the full implementation i didn't check if you calculate the accelerations and positions correctly this is just un overview of how your classes should look and never create objects inside your onDraw() loop. Inside onDraw() should live only code that is necessary for drawing. Handle All your calculations outside onDraw and when ready for a new frame call invalidate(). Or if you need even more performance you can override SurfaceView or TextureView.
Ok FInally i figured it out it was a very very simple solution however it took me 4 days of continuous googling and sample code reading trying to make sense of similar kind of apps
i got the idea from Adam Porter code for sensors and finally got this
Objective was to show the above attached Java ( dynamic view code ) where NO xml was ever involved however i want to run that code via XML by setting so that i can limit it in a small frame or layout instead of whole screen
follows are the steps i did
in XML file
1, create an XML file named :dyn under layouts
2, added a frame layout in that XML file with ID : fl ( framelayout)
and in my java file :
1, set the content to R.layout.dyn instead of previously set as msimulationview
2, created a java framelayout called jfl and then linked it with xml based framelayout called fl
3, added the view( msimulation view ) to the java based framelayout i.e. Jfl
follows is the code it just took 3 lines to achieve that ...
thanks for every one who took their time helping out this
follows is the code :
setContentView( R.layout.dyn);
FrameLayout jfl = findViewById( R.id.fl );
mSimulationView.setId( R.id.fl );
jfl.getContext();
jfl.addView( mSimulationView );
THanks a lot every one.
here is the complete Java file :
package com.example.sensormanager;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.BitmapFactory.Options;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import java.util.zip.Inflater;
public class acceleromparticles extends Activity {
public SimulationView mSimulationView;
private SensorManager mSensorManager;
private PowerManager mPowerManager;
private WindowManager mWindowManager;
private Display mDisplay;
private WakeLock mWakeLock;
public int ui = 1 ;
public FrameLayout fl;
/** Called when the activity is first created. */
#SuppressLint("ResourceType")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get an instance of the SensorManager
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
// Get an instance of the PowerManager
mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
// Get an instance of the WindowManager
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mSimulationView = new SimulationView(getApplicationContext());
// fl.addView( mSimulationView );
setContentView( R.layout.dyn);
FrameLayout jfl = findViewById( R.id.fl );
jfl.addView( mSimulationView );
mSimulationView.setBackgroundResource(R.drawable.wood);
// setContentView(mSimulationView);
}
#Override
protected void onResume() {
super.onResume();
/*
* when the activity is resumed, we acquire a wake-lock so that the
* screen stays on, since the user will likely not be fiddling with the
* screen or buttons.
*/
//mWakeLock.acquire();
// Start the simulation
mSimulationView.startSimulation();
// mSimulationView.setLayoutParams(new FrameLayout.LayoutParams(700, 300));
}
#Override
protected void onPause() {
super.onPause();
/*
* When the activity is paused, we make sure to stop the simulation,
* release our sensor resources and wake locks
*/
// Stop the simulation
mSimulationView.stopSimulation();
// and release our wake-lock
// mWakeLock.release();
}
class SimulationView extends FrameLayout implements SensorEventListener {
// diameter of the balls in meters
private static final float sBallDiameter = 0.0005f;
private static final float sBallDiameter2 = sBallDiameter * sBallDiameter;
private final int mDstWidth;
private final int mDstHeight;
private Sensor mAccelerometer;
private long mLastT;
private float mXDpi;
private float mYDpi;
private float mMetersToPixelsX;
private float mMetersToPixelsY;
private float mXOrigin;
private float mYOrigin;
private float mSensorX;
private float mSensorY;
private float mHorizontalBound;
private float mVerticalBound;
public final ParticleSystem mParticleSystem;
/*
* Each of our particle holds its previous and current position, its
* acceleration. for added realism each particle has its own friction
* coefficient.
*/
class Particle extends View {
private float mPosX = (float) Math.random();
private float mPosY = (float) Math.random();
private float mVelX;
private float mVelY;
public Particle(Context context) {
super( context );
}
public Particle(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Particle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public Particle(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void computePhysics(float sx, float sy, float dT) {
final float ax = -sx/35; //viscosity changes
final float ay = -sy/35;
mPosX += mVelX * dT + ax * dT * dT ; //original /2
mPosY += mVelY * dT + ay * dT * dT ;
mVelX += ax * dT;
mVelY += ay * dT;
}
public void resolveCollisionWithBounds() {
final float xmax = mHorizontalBound;
final float ymax = mVerticalBound;
final float x = mPosX;
final float y = mPosY;
if (x > xmax) {
mPosX = xmax;
mVelX = 0;
} else if (x < -xmax) {
mPosX = -xmax;
mVelX = 0;
}
if (y > ymax) {
mPosY = ymax;
mVelY = 0;
} else if (y < -ymax) {
mPosY = -ymax;
mVelY = 0;
}
}
}
/*
* A particle system is just a collection of particles
*/
class ParticleSystem {
static final int NUM_PARTICLES = 1;
private Particle mBalls[] = new Particle[NUM_PARTICLES];
ParticleSystem() {
/*
* Initially our particles have no speed or acceleration
*/
for (int i = 0; i < mBalls.length; i++) {
mBalls[i] = new Particle(getContext());
mBalls[i].setBackgroundResource(R.drawable.ball);
mBalls[i].setLayerType(LAYER_TYPE_HARDWARE, null);
addView(mBalls[i], new ViewGroup.LayoutParams(mDstWidth, mDstHeight));
}
}
/*
* Update the position of each particle in the system using the
* Verlet integrator.
*/
private void updatePositions(float sx, float sy, long timestamp) {
final long t = timestamp;
if (mLastT != 0) {
final float dT = (float) (t - mLastT) / 1000.f /** (1.0f / 1000000000.0f)*/;
final int count = mBalls.length;
for (int i = 0; i < count; i++) {
Particle ball = mBalls[i];
ball.computePhysics(sx, sy, dT);
}
}
mLastT = t;
}
/*
* Performs one iteration of the simulation. First updating the
* position of all the particles and resolving the constraints and
* collisions.
*/
public void update(float sx, float sy, long now) {
// update the system's positions
updatePositions(sx, sy, now);
// We do no more than a limited number of iterations
final int NUM_MAX_ITERATIONS = 2; //nyy changed from 10 to 1
/*
* Resolve collisions, each particle is tested against every
* other particle for collision. If a collision is detected the
* particle is moved away using a virtual spring of infinite
* stiffness.
*/
boolean more = true;
final int count = mBalls.length;
for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) {
more = false;
for (int i = 0; i < count; i++) {
Particle curr = mBalls[i];
for (int j = i + 1; j < count; j++) {
Particle ball = mBalls[j];
float dx = ball.mPosX - curr.mPosX;
float dy = ball.mPosY - curr.mPosY;
float dd = dx * dx + dy * dy;
// Check for collisions
if (dd <= sBallDiameter2) {
/*
* add a little bit of entropy, after nothing is
* perfect in the universe.
*/
dx += ((float) Math.random() - 0.01f) * 0.0001f; // 0.05 changed to 0.01 will change the animation pattern and will
dy += ((float) Math.random() - 0.01f) * 0.0001f; // 0.0001 chnaged to .001
dd = dx * dx + dy * dy;
// simulate the spring
final float d = (float) Math.sqrt(dd);
final float c = (0.5f * (sBallDiameter - d)) / d;
final float effectX = dx * c;
final float effectY = dy * c;
curr.mPosX -= effectX;
curr.mPosY -= effectY;
ball.mPosX += effectX;
ball.mPosY += effectY;
more = true;
}
}
curr.resolveCollisionWithBounds();
}
}
}
public int getParticleCount() {
return mBalls.length;
}
public float getPosX(int i) {
return mBalls[i].mPosX;
}
public float getPosY(int i) {
return mBalls[i].mPosY;
}
}
public void startSimulation() {
/*
* It is not necessary to get accelerometer events at a very high
* rate, by using a slower rate (SENSOR_DELAY_UI), we get an
* automatic low-pass filter, which "extracts" the gravity component
* of the acceleration. As an added benefit, we use less power and
* CPU resources.
*/
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
}
public void stopSimulation() {
mSensorManager.unregisterListener(this);
Toast.makeText( getApplicationContext(), "Accelerometer Disengaged", Toast.LENGTH_SHORT ).show();
}
public SimulationView(Context context) {
super(context);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
DisplayMetrics metrics = new DisplayMetrics();
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mXDpi = metrics.xdpi;
mYDpi = metrics.ydpi;
//this will reduce the size of the ball oroginal was 0.0254 changed t 0.0854
mMetersToPixelsX = mXDpi / 0.00154f;
mMetersToPixelsY = mYDpi / 0.00154f;
// rescale the ball so it's about 0.5 cm on screen
mDstWidth = (int) (sBallDiameter * mMetersToPixelsX * 0.5f);
mDstHeight = (int) (sBallDiameter * mMetersToPixelsY * 0.5f);
mParticleSystem = new ParticleSystem();
Options opts = new Options();
opts.inDither = true;
opts.inPreferredConfig = Bitmap.Config.RGB_565;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// compute the origin of the screen relative to the origin of
// the bitmap
mXOrigin = (w - mDstWidth) * 0.5f;
mYOrigin = (h - mDstHeight) * 0.5f;
mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f);
mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f);
}
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
/*
* record the accelerometer data, the event's timestamp as well as
* the current time. The latter is needed so we can calculate the
* "present" time during rendering. In this application, we need to
* take into account how the screen is rotated with respect to the
* sensors (which always return data in a coordinate space aligned
* to with the screen in its native orientation).
*/
switch (mDisplay.getRotation()) {
case Surface.ROTATION_0:
mSensorX = event.values[0];
mSensorY = event.values[1];
break;
case Surface.ROTATION_90:
mSensorX = -event.values[1];
mSensorY = event.values[0];
break;
case Surface.ROTATION_180:
mSensorX = -event.values[0];
mSensorY = -event.values[1];
break;
case Surface.ROTATION_270:
mSensorX = event.values[1];
mSensorY = -event.values[0];
break;
}
}
#Override
protected void onDraw(Canvas canvas) {
/*
* Compute the new position of our object, based on accelerometer
* data and present time.
*/
final ParticleSystem particleSystem = mParticleSystem;
final long now = System.currentTimeMillis();
final float sx = mSensorX;
final float sy = mSensorY;
particleSystem.update(sx, sy, now);
final float xc = mXOrigin;
final float yc = mYOrigin;
final float xs = mMetersToPixelsX;
final float ys = mMetersToPixelsY;
final int count = particleSystem.getParticleCount();
for (int i = 0; i < count; i++) {
/*
* We transform the canvas so that the coordinate system matches
* the sensors coordinate system with the origin in the center
* of the screen and the unit is the meter.
*/
final float x = xc + particleSystem.getPosX(i) * xs;
final float y = yc - particleSystem.getPosY(i) * ys;
particleSystem.mBalls[i].setTranslationX(x);
particleSystem.mBalls[i].setTranslationY(y);
}
// and make sure to redraw asap
invalidate();
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
}
i'm trying to move a circle to the point i touch in my running app. I want to see the circle moving along a path towards this point i touch.
I have three classes:
public class Drawing extends View{
Context ctx;
static Circle c1;
private float circleCenterX = 100;
private float circleCenterY = 100;
private float lerpX;
private float lerpY;
private float time = 25;
private float frames = 100;
public Drawing(Context context) {
super(context);
this.ctx = context;
c1 = new Circle (165, 350, 33);
}
public void update(float x, float y) {
this.circleCenterX = x;
this.circleCenterY = y;
}
protected void onDraw (android.graphics.Canvas canvas){
Paint p = new Paint();
p.setColor(Color.GREEN);
lerpX = (circleCenterX - c1.getX()) * (time / frames) + c1.getX();
lerpY = (circleCenterY - c1.getY()) * (time / frames) + c1.getY();
canvas.drawCircle(lerpX, lerpY, c1.getR(), p);
c1.setX(lerpX);
c1.setY(lerpY);
}
public class Circle {
private float x;
private float y;
private float r;
public Circle(float x, float y, float r) {
super();
this.x = x;
this.y = y;
this.r = r;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getR() {
return r;
}
public void setR(float r) {
this.r = r;
}`
public class Game extends Activity implements OnTouchListener {
Drawing d;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
d=new Drawing(this);
setContentView(d);
d.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent me) {
d.update(me.getX(), me.getY());
d.invalidate();
return true;
}
I think i will need something like a while or for loop to increment the x and y coords and or maybe need a speed value?!
Maybe im totally wrong and its a lot of more math to get it.
Thanks for your help
Cheers
Every time onDraw is called you need to move the circle a bit. The easiest way is just to move a certain number of pixels each time it's called.
To be able to do this you need to keep track of:
Where the animation started
Where you want the animation to end
Use linear interpolation to calculate the position of the circle in each onDraw call
I am a beginner with LibGDX. I am trying to create an android game based fixed size tiled map (33x21 tiles). Tiles I am using are 32x32px. So far I managed to load the map created with Tiled and add touch gestures like zoom in/out and panning. The game character movement will be turn based tile by tile, so I need to have a possibility to select specific tile in order to perform some action. I tried solution from here: LibGDX: How to make tiled map tiles clickable? and it works perfectly fine, until the screen is not zoomed in.
Without zoom, the left-bottom corner coordinates shows that it is (0,0), which is correct. When I zoom in, the left-bottom corner of the visible part of the map instead of keeping it's correct coordinates (i.e. (480, 320)) it becomes (0,0) again. I tried to use camera.unproject(Vector3), but I was unsuccessful. Probably used it wrong. I am also not really convinced, that the way in which I try to get tiles is the most appropriate one. Can you help me get tiles coordinates properly? Below is the code that I am using:
public class TiledTest extends ApplicationAdapter {
public TiledMap tiledMap;
OrthographicCamera camera;
TiledMapRenderer tiledMapRenderer;
GestureDetector gesture;
InputMultiplexer myInputMultiplexer;
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
public static final float PAN_RATE = (float) 0.01;
private static final float ZOOM_SPEED = (float) 0.009;
int columns, rows;
TiledMapTileLayer grid_layer;
#Override
public void create () {
gesture =new GestureDetector(new MyGestureListener());
myInputMultiplexer = new InputMultiplexer();
float unitScale = 1 / 32f;
camera = new OrthographicCamera();
camera.setToOrtho(true, 33,21);
tiledMap = new TmxMapLoader().load("tiled_map.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap, unitScale);
grid_layer = (TiledMapTileLayer)tiledMap.getLayers().get(0);
Stage stage = new TiledMapStage(tiledMap);
myInputMultiplexer.addProcessor(gesture);
myInputMultiplexer.addProcessor(stage);
Gdx.input.setInputProcessor(myInputMultiplexer);
}
#Override
public void render () {
tiledMapRenderer.setView(camera);
tiledMapRenderer.render();
camera.update();
}
public class TiledMapActor extends Actor {
private TiledMapTileLayer.Cell cell;
public TiledMapActor(TiledMap tileMap, TiledMapTileLayer tiledLayer, TiledMapTileLayer.Cell cell) {
tiledMap = tileMap;
grid_layer = tiledLayer;
this.cell = cell;
}
}
public class TiledMapClickListener extends ClickListener {
private TiledMapActor actor;
public TiledMapClickListener(TiledMapActor actor) {
this.actor = actor;
}
#Override
public void clicked(InputEvent event, float x, float y) {
int a = (int) actor.getX();
int b = (int) actor.getY();
System.out.println(actor.cell + " has been clicked.");
System.out.println("x = " + a + "y = " + b );
}
}
public class TiledMapStage extends Stage {
private TiledMap tiledMap;
public TiledMapStage(TiledMap tiledMap) {
this.tiledMap = tiledMap;
for (MapLayer layer : tiledMap.getLayers()) {
TiledMapTileLayer tiledLayer = (TiledMapTileLayer)layer;
createActorsForLayer(tiledLayer);
}
}
private void createActorsForLayer(TiledMapTileLayer tiledLayer) {
for (int x = 0; x <= tiledLayer.getWidth(); x++) {
for (int y = 0; y < tiledLayer.getHeight(); y++) {
TiledMapTileLayer.Cell cell = tiledLayer.getCell(x, y);
TiledMapActor actor = new TiledMapActor(tiledMap, tiledLayer, cell);
actor.setBounds(x * tiledLayer.getTileWidth(), y * tiledLayer.getTileHeight(), tiledLayer.getTileWidth(),
tiledLayer.getTileHeight());
addActor(actor);
EventListener eventListener = new TiledMapClickListener(actor);
actor.addListener(eventListener);
}
}
}
}
public void resize(float width, float height) {
}
public class MyGestureListener implements GestureListener{
#Override
public boolean touchDown(float x, float y, int pointer, int button) {
return false;
}
#Override
public boolean tap(float x, float y, int count, int button) {
return false;
}
#Override
public boolean longPress(float x, float y) {
return false;
}
#Override
public boolean fling(float velocityX, float velocityY, int button) {
return false;
}
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
float effectiveViewportWidth = camera.viewportWidth * camera.zoom;
float effectiveViewportHeight = camera.viewportHeight * camera.zoom;
camera.position.x = MathUtils.clamp(camera.position.x, effectiveViewportWidth / 2f, 33 - effectiveViewportWidth / 2f);
camera.position.y = MathUtils.clamp(camera.position.y, effectiveViewportHeight / 2f, 21 - effectiveViewportHeight / 2f);
if (camera.zoom < 1) {
camera.translate(-deltaX * PAN_RATE, -deltaY * PAN_RATE, 0);
camera.update();
}
return false;
}
#Override
public boolean panStop(float x, float y, int pointer, int button) {
//Gdx.app.log("Text", "panstop");
return false;
}
#Override
public boolean zoom (float originalDistance, float currentDistance){
float ratio = originalDistance/currentDistance;
camera.zoom += ZOOM_SPEED * ratio;
if (camera.zoom < 0.3)
{
camera.zoom = (float) 0.3;
}
else if (camera.zoom > 1)
{
camera.zoom = 1;
}
System.out.println(camera.zoom);
return false;
}
#Override
public boolean pinch (Vector2 initialFirstPointer, Vector2 initialSecondPointer, Vector2 firstPointer, Vector2 secondPointer){
camera.zoom -= .01;
camera.update();
return false;
}
}
}
So you want to transfer the screen coordinates to world coordinates.
For example:
#Override
public void clicked(InputEvent event, float x, float y) {
Vector3 touch = new Vector3(x, y, 0);
camera.unproject(touch);
System.out.println("Screen coordinates translated to world coordinates: "
+ "X: " + touch.x + " Y: " + touch.y);
}
}
Above clicked function does not worked for me, but you led me to the answer. I dumped whole assigning actors for tiles and used camera.unproject in MyGestureListener in touchDown.
If someone is interested I just leave it here :)
#Override
public boolean touchDown(float x, float y, int pointer, int button) {
Vector3 temp_coord = new Vector3(x,y,0);
Vector3 coords = camera.unproject(temp_coord);
x =(int) coords.x;
y =(int) coords.y;
writerX = x;
writerY = y;
System.out.println("Screen coordinates translated to world coordinates: "
+ "X: " + x + " Y: " + y);
return false;
}
I have take Example of Accelerator with SensorManager, in which canvas(ball) are get update its position as per device Accelerator are rotated. Here is image :
As shown in the image there is a ball and one line. The ball's position is frequently updated, while the line's position is static.
I would like to have the ball bounce back when it touches the line. I have tried since from 3 day, but don't understand how I can do this.
here is my code:
public class ballsensor extends Activity implements SensorEventListener {
// sensor-related
private SensorManager mSensorManager;
private Sensor mAccelerometer;
// animated view
private ShapeView mShapeView;
// screen size
private int mWidthScreen;
private int mHeightScreen;
// motion parameters
private final float FACTOR_FRICTION = 0.5f; // imaginary friction on the
// screen
private final float GRAVITY = 9.8f; // acceleration of gravity
private float mAx; // acceleration along x axis
private float mAy; // acceleration along y axis
private final float mDeltaT = 0.5f; // imaginary time interval between each
// acceleration updates
// timer
private Timer mTimer;
private Handler mHandler;
private boolean isTimerStarted = false;
private long mStart;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the screen always portait
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// initializing sensors
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// obtain screen width and height
Display display = ((WindowManager) this
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mWidthScreen = display.getWidth();
mHeightScreen = display.getHeight() - 35;
// initializing the view that renders the ball
mShapeView = new ShapeView(this);
mShapeView.setOvalCenter((int) (mWidthScreen * 0.6),
(int) (mHeightScreen * 0.6));
setContentView(mShapeView);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
#Override
public void onSensorChanged(SensorEvent event) {
// obtain the three accelerations from sensors
mAx = event.values[0];
mAy = event.values[1];
float mAz = event.values[2];
// taking into account the frictions
mAx = Math.signum(mAx) * Math.abs(mAx)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
mAy = Math.signum(mAy) * Math.abs(mAy)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
}
#Override
protected void onResume() {
super.onResume();
// start sensor sensing
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onPause() {
super.onPause();
// stop senser sensing
mSensorManager.unregisterListener(this);
}
// the view that renders the ball
private class ShapeView extends SurfaceView implements
SurfaceHolder.Callback {
private final int RADIUS = 30;
private final float FACTOR_BOUNCEBACK = 0.50f;
private int mXCenter;
private int mYCenter;
private RectF mRectF;
private final Paint mPaint;
private ShapeThread mThread;
private float mVx;
private float mVy;
public ShapeView(Context context) {
super(context);
getHolder().addCallback(this);
mThread = new ShapeThread(getHolder(), this);
setFocusable(true);
mPaint = new Paint();
mPaint.setColor(0xFFFFFFFF);
mPaint.setAlpha(192);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setAntiAlias(true);
mRectF = new RectF();
}
// set the position of the ball
public boolean setOvalCenter(int x, int y) {
mXCenter = x;
mYCenter = y;
return true;
}
// calculate and update the ball's position
public boolean updateOvalCenter() {
mVx -= mAx * mDeltaT;
mVy += mAy * mDeltaT;
System.out.println("mVx is ::" + mVx);
System.out.println("mVy is ::" + mVy);
mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT));
mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT));
if (mXCenter < RADIUS) {
mXCenter = RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter < RADIUS) {
mYCenter = RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
if (mXCenter > mWidthScreen - RADIUS) {
mXCenter = mWidthScreen - RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter > mHeightScreen - 2 * RADIUS) {
mYCenter = mHeightScreen - 2 * RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
return true;
}
// update the canvas.
#Override
protected void onDraw(Canvas canvas) {
if (mRectF != null) {
mRectF.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter
+ RADIUS, mYCenter + RADIUS);
canvas.drawColor(0XFF000000);
// canvas.drawOval(mRectF, mPaint);
Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
R.drawable.stripe1);
Bitmap ball = BitmapFactory.decodeResource(getResources(),
R.drawable.blackwhiteball);
canvas.drawBitmap(ball, mXCenter - RADIUS, mYCenter - RADIUS,
mPaint);
canvas.drawBitmap(kangoo, 130, 10, null);
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mThread.setRunning(true);
mThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
class ShapeThread extends Thread {
private SurfaceHolder mSurfaceHolder;
private ShapeView mShapeView;
private boolean mRun = false;
public ShapeThread(SurfaceHolder surfaceHolder, ShapeView shapeView) {
mSurfaceHolder = surfaceHolder;
mShapeView = shapeView;
}
public void setRunning(boolean run) {
mRun = run;
}
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
#Override
public void run() {
Canvas c;
while (mRun) {
mShapeView.updateOvalCenter();
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
mShapeView.onDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
Rather than try to fix your code, work at the design level by developing a software architecture that has two components: physics model and display. The key is to separate the physics of the problem from the display. Modelling the physics becomes much easier when done separately from the display. Likewise the display also becomes easier. Have two separate packages - one for the physics and one for the display.
Start with a simpler version of the problem where the physics world just has a point and a line. Model the point reflecting off the line. You have some code that does this. Just rip it out of the current code. Make sure the physics does what you expect it to without worrying about the display.
Design a class for the ball. The ball has velocity and position properties. It has a move method that updates the position based on the velocity for one time click. The move method checks to see if it has interacted (collided) with the wall and changes the velocity according the physics you want your world to have. The collision detection is done by asking the wall were it is. The physics could be angle of incidence equals angle of reflection, or you could have a spin property on the ball that changes how the ball bounces. The key is that all of the physics modelling is done separately from the display. Similarly, you create a class for the wall. Initially the wall is fixed, but you could add movement to it. The nice thing is that if you've designed the ball class correctly changing the wall to make it move doesn't effect the design of the ball class. Also, none of these changes to the physics effect how the display is done.
Make a display that simply translates the physics into a presentation on the screen.
From there you can add complexity to your model. Make the point a circle. Redo the physics to make it work with this new complexity. The display won't change much, but keep them separate.
I have my CS1 class do versions of this same problem. Two years ago, I had them make a pong game. Last year a version of Centipede. This coming semester they'll have Breakout as a project. When they model the physics separately from the display, they get it working. When they don't, it is usually a muddled mess.
Physics modyle shall run in separate thread, and use best available time resolution for position updates. ( milliseconds should be enough ) This is how I design gameloop:
lastFrameTime = System.currentTimeMillis();
// as long as we run we move
while (state == GameState.RUNNING) {
currentFrame++;
timeNow = System.currentTimeMillis();
// sleep until this frame is scheduled
long l = lastFrameTime + FRAME_DELAY - timeNow;
updatePositions();
redraw();
if (l > 0L) {
try {
Thread.sleep(l);
}
catch (Exception exception) {
}
} else {
// something long kept us from updating, reset delays
lastFrameTime = timeNow;
l = FRAME_DELAY;
}
lastFrameTime = timeNow + l;
// be polite, let others play
Thread.yield();
}
It is important to give up control of the thread, for UI tasks which will be processing events and hive commands to your phyiscs engine.
As for collision detection - it is pretty simple math. Your line is vertical, and zou must just check whether difference in x-coord of line and center is got less than radius - then reverse x-componen of velocity
You can use Rect.intersects(Rect, Rect) to detect collisions. Use your bitmap width and height to set up the new Rects.
Here is a dirty example:
import java.util.Timer;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.Display;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
public class ballsensor extends Activity implements SensorEventListener {
// sensor-related
private SensorManager mSensorManager;
private Sensor mAccelerometer;
// animated view
private ShapeView mShapeView;
// screen size
private int mWidthScreen;
private int mHeightScreen;
// motion parameters
private final float FACTOR_FRICTION = 0.5f; // imaginary friction on the
// screen
private final float GRAVITY = 9.8f; // acceleration of gravity
private float mAx; // acceleration along x axis
private float mAy; // acceleration along y axis
private final float mDeltaT = 0.5f; // imaginary time interval between each
// acceleration updates
// timer
private Timer mTimer;
private Handler mHandler;
private final boolean isTimerStarted = false;
private long mStart;
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the screen always portait
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// initializing sensors
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// obtain screen width and height
final Display display = ((WindowManager) this
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mWidthScreen = display.getWidth();
mHeightScreen = display.getHeight() - 35;
// initializing the view that renders the ball
mShapeView = new ShapeView(this);
mShapeView.setOvalCenter((int) (mWidthScreen * 0.6),
(int) (mHeightScreen * 0.6));
setContentView(mShapeView);
}
#Override
public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
}
#Override
public void onSensorChanged(final SensorEvent event) {
// obtain the three accelerations from sensors
mAx = event.values[0];
mAy = event.values[1];
final float mAz = event.values[2];
// taking into account the frictions
mAx = Math.signum(mAx) * Math.abs(mAx)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
mAy = Math.signum(mAy) * Math.abs(mAy)
* (1 - FACTOR_FRICTION * Math.abs(mAz) / GRAVITY);
}
#Override
protected void onResume() {
super.onResume();
// start sensor sensing
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onPause() {
super.onPause();
// stop senser sensing
mSensorManager.unregisterListener(this);
}
// the view that renders the ball
private class ShapeView extends SurfaceView implements
SurfaceHolder.Callback {
private final int RADIUS = 30;
private final float FACTOR_BOUNCEBACK = 0.50f;
private int mXCenter;
private int mYCenter;
private final RectF mRectF;
private final Paint mPaint;
private final ShapeThread mThread;
private float mVx;
private float mVy;
private final Rect lineRect = new Rect();
private final Rect ballRect = new Rect();
public ShapeView(final Context context) {
super(context);
getHolder().addCallback(this);
mThread = new ShapeThread(getHolder(), this);
setFocusable(true);
mPaint = new Paint();
mPaint.setColor(0xFFFFFFFF);
mPaint.setAlpha(192);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setAntiAlias(true);
mRectF = new RectF();
}
// set the position of the ball
public boolean setOvalCenter(final int x, final int y) {
mXCenter = x;
mYCenter = y;
return true;
}
// calculate and update the ball's position
public boolean updateOvalCenter() {
mVx -= mAx * mDeltaT;
mVy += mAy * mDeltaT;
System.out.println("mVx is ::" + mVx);
System.out.println("mVy is ::" + mVy);
mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT));
mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT));
if (mXCenter < RADIUS) {
mXCenter = RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter < RADIUS) {
mYCenter = RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
if (mXCenter > mWidthScreen - RADIUS) {
mXCenter = mWidthScreen - RADIUS;
mVx = -mVx * FACTOR_BOUNCEBACK;
}
if (mYCenter > mHeightScreen - 2 * RADIUS) {
mYCenter = mHeightScreen - 2 * RADIUS;
mVy = -mVy * FACTOR_BOUNCEBACK;
}
if(Rect.intersects(lineRect, ballRect)){
mVx = -mVx * FACTOR_BOUNCEBACK;
mVy = -mVy * FACTOR_BOUNCEBACK;
mXCenter += (int) (mDeltaT * (mVx + 0.6 * mAx * mDeltaT)) * 5;
mYCenter += (int) (mDeltaT * (mVy + 0.6 * mAy * mDeltaT)) * 5;
}
return true;
}
// update the canvas.
#Override
protected void onDraw(final Canvas canvas) {
if (mRectF != null) {
mRectF.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter
+ RADIUS, mYCenter + RADIUS);
canvas.drawColor(0XFF000000);
// canvas.drawOval(mRectF, mPaint);
final Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
R.drawable.blankcard);
lineRect.set(130, 10, 130 + kangoo.getWidth(), 10 + kangoo.getHeight());
final Bitmap ball = BitmapFactory.decodeResource(getResources(),
R.drawable.blankcard);
ballRect.set(mXCenter - RADIUS, mYCenter - RADIUS, mXCenter - RADIUS + ball.getWidth(), mYCenter - RADIUS + ball.getHeight());
canvas.drawBitmap(ball, mXCenter - RADIUS, mYCenter - RADIUS,
mPaint);
canvas.drawBitmap(kangoo, 130, 10, null);
}
}
#Override
public void surfaceChanged(final SurfaceHolder holder, final int format, final int width,
final int height) {
}
#Override
public void surfaceCreated(final SurfaceHolder holder) {
mThread.setRunning(true);
mThread.start();
}
#Override
public void surfaceDestroyed(final SurfaceHolder holder) {
boolean retry = true;
mThread.setRunning(false);
while (retry) {
try {
mThread.join();
retry = false;
} catch (final InterruptedException e) {
}
}
}
}
class ShapeThread extends Thread {
private final SurfaceHolder mSurfaceHolder;
private final ShapeView mShapeView;
private boolean mRun = false;
public ShapeThread(final SurfaceHolder surfaceHolder, final ShapeView shapeView) {
mSurfaceHolder = surfaceHolder;
mShapeView = shapeView;
}
public void setRunning(final boolean run) {
mRun = run;
}
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
#Override
public void run() {
Canvas c;
while (mRun) {
mShapeView.updateOvalCenter();
c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
mShapeView.onDraw(c);
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
Needs improvement but might get you on the right track.
I'm trying to implement a touch responsive image view - like the one in the Gallery application. I've managed to do that, and it works well, except that it is VERY laggy compared to the stock Gallery.
I'm looking for ways to make it smoother.
Here's what I have now:
#SuppressWarnings("unused")
public class ImageDisplayView extends ImageView {
private static final String TAG = "ImageDisplayView";
private static final int MODE_NONE = 0;
private static final int MODE_DRAG = 1;
private static final int MODE_ZOOM = 2;
// Zoom Bounds
private static final float SCALE_MIN = 0.8f;
private static final float SCALE_BOTTOM = 1.0f;
private static final float SCALE_TOP = 10.0f;
private static final float SCALE_MAX = 12.0f;
// Transformation
private Matrix mMatrix = new Matrix();
private Matrix mPreMatrix = new Matrix();
private PointF mPivot = new PointF();
private PointF mNewPivot = new PointF();
private float mDist;
// State
private boolean mInitialScaleDone = false;
private boolean mEnabled = true;
private RectF mBounds = new RectF();
private Float mScale = null;
private float mMode = MODE_NONE;
public ImageDisplayView(Context context) {
super(context);
initialScale();
}
public ImageDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
initialScale();
}
public ImageDisplayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialScale();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initialScale();
}
private void initialScale() {
if (!mInitialScaleDone && getDrawable() != null) {
mInitialScaleDone = true;
final Runnable r = new Runnable() {
#Override
public void run() {
// Fit to screen
float scale = calculateFitScreenScale();
mMatrix.reset();
mMatrix.postScale(scale, scale);
setImageMatrix(mMatrix);
// Center
float dx = (getWidth() - mBounds.width()) / 2, dy = (getHeight() - mBounds
.height()) / 2;
mMatrix.postTranslate(dx, dy);
setImageMatrix(mMatrix);
mScale = 1.0f;
}
};
if (getWidth() == 0) {
getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
r.run();
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
} else {
r.run();
}
}
}
#Override
public void setImageMatrix(Matrix matrix) {
super.setImageMatrix(matrix);
mBounds.set(getDrawable().getBounds());
matrix.mapRect(mBounds);
}
#Override
public void setEnabled(boolean enabled) {
mEnabled = enabled;
if (!enabled && mMode != MODE_NONE) {
if (mMode == MODE_DRAG) {
checkLimits(null);
} else {
mPivot.set(getWidth() / 2, getHeight() / 2);
updateScale();
mPreMatrix.set(mMatrix);
checkLimits(null);
}
mMode = MODE_NONE;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float scale, dx, dy;
if (mEnabled) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getPointerCount() == 1) { // Start mode: drag only.
mMode = MODE_DRAG;
mPivot.set(event.getX(), event.getY());
} else { // Start mode: zoom and drag.
mMode = MODE_ZOOM;
mDist = distance(event);
midPoint(mPivot, event);
}
mPreMatrix.set(mMatrix);
return true;
case MotionEvent.ACTION_UP:
mMode = MODE_NONE;
checkLimits(event);
break;
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerCount() == 2) {
mMode = MODE_DRAG;
mPivot.set(event.getX(), event.getY());
updateScale();
mPreMatrix.set(mMatrix);
}
return true;
case MotionEvent.ACTION_MOVE:
mMatrix.set(mPreMatrix);
if (mMode == MODE_DRAG) {
dx = event.getX() - mPivot.x;
dy = event.getY() - mPivot.y;
mMatrix.postTranslate(dx, dy);
} else if (mMode == MODE_ZOOM) {
scale = distance(event) / mDist;
midPoint(mNewPivot, event);
dx = mNewPivot.x - mPivot.x;
dy = mNewPivot.y - mPivot.y;
mMatrix.postTranslate(dx, dy);
float postScale = mScale * scale;
if (postScale < SCALE_MIN) {
scale = SCALE_MIN / mScale;
} else if (postScale > SCALE_MAX) {
scale = SCALE_MAX / mScale;
}
mMatrix.postScale(scale, scale, mNewPivot.x, mNewPivot.y);
}
setImageMatrix(mMatrix);
return true;
}
}
return super.onTouchEvent(event);
}
private void updateScale() {
mScale = (mBounds.width() / getDrawable().getIntrinsicWidth())
/ calculateFitScreenScale();
}
#IntendedCaller("onTouchEvent(MotionEvent)")
private void checkLimits(MotionEvent event) {
float scale, dx, dy;
if (mScale < SCALE_BOTTOM) {
scale = SCALE_BOTTOM;
} else if (mScale > SCALE_TOP) {
scale = SCALE_TOP;
} else {
scale = mScale;
}
RectF scaleBounds = new RectF(mBounds); // Use the scale of the image
// to determine drag
// threshold.
scaleBounds.offset(getWidth() / 2 - scaleBounds.centerX(), getHeight()
/ 2 - scaleBounds.centerY());
dx = Math.min(0.0f, Math.max(0.0f, scaleBounds.left) - mBounds.left)
+ Math.max(0.0f, Math.min(getWidth(), scaleBounds.right)
- mBounds.right);
dy = Math.min(0.0f, Math.max(0.0f, scaleBounds.top) - mBounds.top)
+ Math.max(0.0f, Math.min(getHeight(), scaleBounds.bottom)
- mBounds.bottom);
animateTransformation(event, 500, null, scale, dx, dy);
}
public void animateTransformation(MotionEvent event, long duration,
final Runnable callback, final float targetScale, final float dx,
final float dy) {
mPreMatrix.set(mMatrix);
final float scale = targetScale / mScale;
final float px, py;
if (event != null) {
PointF pivot = new PointF();
midPoint(pivot, event);
px = scale > 1.0f ? mBounds.centerX() : dx > 0 ? mBounds.right
: dx < 0 ? mBounds.left : pivot.x;
py = scale > 1.0f ? mBounds.centerY() : dy > 0 ? mBounds.bottom
: dy < 0 ? mBounds.top : pivot.y;
} else {
px = mBounds.centerX();
py = mBounds.centerY();
}
Utils.animate(this, new Animator() {
#Override
public void onAnimationEnd() {
updateScale();
if (callback != null) {
callback.run();
}
}
#Override
public void makeStep(float percent) {
mMatrix.set(mPreMatrix);
float s = 1.0f + percent * (scale - 1.0f);
float tempx = dx * percent, tempy = dy * percent;
mMatrix.postTranslate(tempx, tempy);
mMatrix.postScale(s, s, px + tempx, py + tempy);
setImageMatrix(mMatrix);
}
}, duration);
}
public RectF getBounds() {
return mBounds;
}
public float getScale() {
return mScale;
}
public void scale(float scale, float px, float py) {
mMatrix.set(getImageMatrix());
mMatrix.postScale(scale, scale, px, py);
setImageMatrix(mMatrix);
mScale = scale;
}
public void translate(float dx, float dy) {
mMatrix.set(getImageMatrix());
mMatrix.postTranslate(dx, dy);
setImageMatrix(mMatrix);
}
public void resetAndCenter() {
mMatrix.set(getImageMatrix());
mMatrix.postScale(1 / mScale, 1 / mScale, mBounds.centerX(),
mBounds.centerY());
setImageMatrix(mMatrix);
// Center
float dx = (getWidth() - mBounds.width()) / 2, dy = (getHeight() - mBounds
.height()) / 2;
mMatrix.postTranslate(dx, dy);
setImageMatrix(mMatrix);
updateScale();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mInitialScaleDone = false;
initialScale();
}
private float distance(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF p, MotionEvent event) {
if (event.getPointerCount() == 1) {
p.set(event.getX(), event.getY());
} else {
p.set((event.getX(1) + event.getX(0)) / 2,
(event.getY(1) + event.getY(0)) / 2);
}
}
private float calculateFitScreenScale() {
RectF r = new RectF(getDrawable().getBounds());
float w = getWidth(), h = getHeight();
if (r.width() > r.height()) {
return w / r.width();
} else if (r.width() == r.height()) {
if (w < h) {
return w / r.width();
} else {
return h / r.height();
}
} else {
return h / r.height();
}
}
}
What can I do in order to make this less laggy? Any help much appreciated.
Have you tried it on an actual device? The emulator in notoriously slow.
Here's something that might help:
Check out the Batching section at the Android Dev Resource page:
http://developer.android.com/reference/android/view/MotionEvent.html
I've noticed that how a device batches together historical points in a continuous motion can differ considerably. This may also differ considerably between devices.
In your code, you only use getX and getY, which takes the most recent event, ignoring all the historical points.
If your device happens to be batching large amounts of historical events together, using only the most recent ones (via getX and getY) might cause lag.
The link suggests how one might consume all the historical points.
You could implement a solution that checks whether too many historical points have been batched together (event.getHistorySize() > x). If the number of historical points are above a threshold of your choosing, make intermediate frame updates based on, say, historySize / 2. Something like that.
Please review this question here
How can I get zoom functionality for images?
theres answer by Mike Ortiz down, implementing Mutli-Touch ImageView