无法在行布局(基于 Xml )中填充自定义视图(完全基于 java )
Not able to populate a custom view ( totally java based ) in a line layout ( Xml based )
我有一个完全基于 Java 的自定义视图(没有 XML )代码在下面,我想将它添加到一个新的基于 XML 的布局中..请指导如何做到这一点,但我也希望当它显示时,屏幕将显示 50% xml 布局和 50% 自定义视图是否可能?请看下图更清楚
下面是动画的Java代码:
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) {
}
}
}
您只是将 setContentView(mSimulationView) 设置为仅 mSimulationView。
您已经在 xml 文件中包含了自定义布局和线性布局的所有内容。
setContentView(R.layout.your_xml)
如果我理解正确,那么您不需要覆盖 linearLayout,您的模拟视图将如下所示。
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);
}
}
创建一个接口,以便在我们的粒子系统想要绘制新帧或简单地传递视图并对其调用 invalidate 时得到通知;
interface ParticleSystemListener {
public void needNewFrame();
}
您的 particleSystem 应该执行所有计算并处理添加和删除粒子以及将 dway 命令向下传递给粒子。
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);
}
}
}
所有状态都将驻留在粒子 class 中。粒子 class 应该知道它的位置以及如何绘制它自己
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
}
}
如您所见,这不是完整的实现,我没有检查您是否正确计算了加速度和位置,这只是对您的 classes 外观的概述,并且永远不会在您的内部创建对象onDraw() 循环。 onDraw() 内部应该只包含绘图所需的代码。在 onDraw 之外处理所有计算,并在准备好调用 invalidate() 新帧时处理。或者,如果您需要更多性能,您可以覆盖 SurfaceView 或 TextureView。
好的,最后我发现这是一个非常非常简单的解决方案,但是我花了 4 天的时间连续谷歌搜索和示例代码阅读试图理解类似类型的应用程序
我从 Adam Porter 的传感器代码中得到灵感,最终得到了这个
Objective 是为了显示上面附加的 Java (动态视图代码),其中没有 xml 涉及但是我想 运行 通过 XML 通过设置,以便我可以将其限制在一个小框架或布局中,而不是整个屏幕
以下是我执行的步骤
在 XML 文件中
1、在layouts下创建一个名为:dyn的XML文件
2、在那个XML文件中添加了一个框架布局,ID为:fl(framelayout)
在我的 java 文件中:
1、设置内容为R.layout.dyn而不是之前设置为msimulationview
2,创建一个名为 jfl 的 java 框架布局,然后将其与名为 fl 的基于 xml 的框架布局链接起来
3,将视图(msimulation view)添加到基于java的框架布局,即Jfl
以下是只用了 3 行就实现了的代码...
感谢每一位花时间帮助解决这个问题的人
代码如下:
setContentView( R.layout.dyn);
FrameLayout jfl = findViewById( R.id.fl );
mSimulationView.setId( R.id.fl );
jfl.getContext();
jfl.addView( mSimulationView );
非常感谢大家。
这是完整的 Java 文件:
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) {
}
}
}
我有一个完全基于 Java 的自定义视图(没有 XML )代码在下面,我想将它添加到一个新的基于 XML 的布局中..请指导如何做到这一点,但我也希望当它显示时,屏幕将显示 50% xml 布局和 50% 自定义视图是否可能?请看下图更清楚
下面是动画的Java代码:
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) {
}
}
}
您只是将 setContentView(mSimulationView) 设置为仅 mSimulationView。 您已经在 xml 文件中包含了自定义布局和线性布局的所有内容。
setContentView(R.layout.your_xml)
如果我理解正确,那么您不需要覆盖 linearLayout,您的模拟视图将如下所示。
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);
}
}
创建一个接口,以便在我们的粒子系统想要绘制新帧或简单地传递视图并对其调用 invalidate 时得到通知;
interface ParticleSystemListener {
public void needNewFrame();
}
您的 particleSystem 应该执行所有计算并处理添加和删除粒子以及将 dway 命令向下传递给粒子。
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);
}
}
}
所有状态都将驻留在粒子 class 中。粒子 class 应该知道它的位置以及如何绘制它自己
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
}
}
如您所见,这不是完整的实现,我没有检查您是否正确计算了加速度和位置,这只是对您的 classes 外观的概述,并且永远不会在您的内部创建对象onDraw() 循环。 onDraw() 内部应该只包含绘图所需的代码。在 onDraw 之外处理所有计算,并在准备好调用 invalidate() 新帧时处理。或者,如果您需要更多性能,您可以覆盖 SurfaceView 或 TextureView。
好的,最后我发现这是一个非常非常简单的解决方案,但是我花了 4 天的时间连续谷歌搜索和示例代码阅读试图理解类似类型的应用程序 我从 Adam Porter 的传感器代码中得到灵感,最终得到了这个
Objective 是为了显示上面附加的 Java (动态视图代码),其中没有 xml 涉及但是我想 运行 通过 XML 通过设置,以便我可以将其限制在一个小框架或布局中,而不是整个屏幕
以下是我执行的步骤
在 XML 文件中 1、在layouts下创建一个名为:dyn的XML文件 2、在那个XML文件中添加了一个框架布局,ID为:fl(framelayout) 在我的 java 文件中: 1、设置内容为R.layout.dyn而不是之前设置为msimulationview 2,创建一个名为 jfl 的 java 框架布局,然后将其与名为 fl 的基于 xml 的框架布局链接起来 3,将视图(msimulation view)添加到基于java的框架布局,即Jfl
以下是只用了 3 行就实现了的代码... 感谢每一位花时间帮助解决这个问题的人
代码如下:
setContentView( R.layout.dyn);
FrameLayout jfl = findViewById( R.id.fl );
mSimulationView.setId( R.id.fl );
jfl.getContext();
jfl.addView( mSimulationView );
非常感谢大家。
这是完整的 Java 文件:
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) {
}
}
}