I'm using a TeeChart library to display Candlesticks chart on Android.
Just for now I'm developing a simple SWT application to test some capabilities of the library.
I'm adding about 400 candles to my chart using
series.add(c.getOpen(), c.getHigh(), c.getLow(), c.getClose())
By the time when there're a lot of candles displayed they begin ovelap with each other.
What is the best way to avoid this?
My idea was to calculate the number of candles which can be displayed without overlapping using chart width, candle width and some spacing between candles:
private int getNumberOfCandlesVisible() {
final Candle candles = (Candle) series;
final int panelWidth = chart.getWidth();
final int candleWidth = candles.getCandleWidth();
return panelWidth / (candleWidth + CANDLE_SPACING);
}
Then I tried to display last N values if series.getCount > getNumberOfCandlesVisible() using zoom or setMinMax() an axis. Is it the right way?
I want to scroll the chart to display to most actual values as they apper, but it doesn't seem to be smooth... Maybe there's an option somewhere?
Very similar to this one, isn't it?
Then, I guess the major issue here is how to smooth the transition.
To do this you could use a TimerTask in Android. Ie:
public class MainActivity extends Activity {
Timer timer;
MyTimerTask myTimerTask;
int cycleCount;
class MyTimerTask extends TimerTask {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
cycleCount++;
if (cycleCount <= 60) {
double tmpIncr = (tChart1.getSeries(0).getXValues().getLast() - tChart1.getSeries(0).getXValues().getValue(tChart1.getSeries(0).getCount()-2)) / 60;
tChart1.getAxes().getBottom().setAutomaticMaximum(false);
tChart1.getAxes().getBottom().setMaximum(tChart1.getAxes().getBottom().getMaximum()+tmpIncr);
tChart1.refreshControl();
} else {
startStopTimer(false);
}
}
});
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout group = (LinearLayout) findViewById(R.id.chart_layout1);
createChart(group);
initializeChart();
}
private TChart tChart1;
private int timerInterval, testIndex;
private int chartPaintCtr;
private int pointsRefreshed, pointsPlotted;
private float myDensity;
private void createChart(LinearLayout group) {
tChart1 = new TChart(this);
group.addView(tChart1);
}
private void initializeChart() {
// apply theme
ThemesList.applyTheme(tChart1.getChart(), 1);
// multitouch drag zoom
tChart1.getZoom().setZoomStyle(ZoomStyle.INCHART_MULTI);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
myDensity = metrics.density;
tChart1.getAspect().setFontZoom(
tChart1.getAspect().getFontZoom() * myDensity);
// hide things for better speed
tChart1.getAspect().setView3D(false);
tChart1.getLegend().setVisible(false);
tChart1.getFooter().setVisible(false);
tChart1.getHeader().setVisible(false);
tChart1.getWalls().setVisible(false);
tChart1.getZoom().setAnimated(true);
tChart1.getZoom().setAnimatedSteps(15);
FastLine lineSeries1 = new FastLine(tChart1.getChart());
lineSeries1.fillSampleValues();
tChart1.getAxes().getTop().getGrid().setVisible(false);
tChart1.getAxes().getRight().getGrid().setVisible(false);
int sep = 150;
tChart1.getAxes().getTop().getLabels().setSeparation(sep);
tChart1.getAxes().getRight().getLabels().setSeparation(sep);
tChart1.getAxes().getBottom().getLabels().setSeparation(sep);
tChart1.getAxes().getLeft().getLabels().setSeparation(sep);
tChart1.getPanel().setMarginLeft(7);
final Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
tChart1.getZoom().setAnimated(false);
msgSet = false;
chartPaintCtr = 0;
cycleCount = 0;
Random generator = new Random();
int tmpRandom = generator.nextInt((int)tChart1.getSeries(0).getYValues().getRange()/10);
tChart1.setAutoRepaint(false);
tChart1.getSeries(0).add(50, tChart1.getSeries(0).getYValues().getLast()-(int)tChart1.getSeries(0).getYValues().getRange()/20+tmpRandom);
tChart1.setAutoRepaint(true);
startStopTimer(true);
}
});
}
public void startStopTimer(Boolean run) {
if (run) {
if (timer != null) {
timer.cancel();
}
// re-schedule timer here
// otherwise, IllegalStateException of
// "TimerTask is scheduled already"
// will be thrown
timer = new Timer();
myTimerTask = new MyTimerTask();
// delay to first xxms, repeat in xxms
timer.schedule(myTimerTask, 0, timerInterval);
} else {
if (timer != null) {
timer.cancel();
timer = null;
}
}
}
}
In the example above, we have a FastLine series populated with 25 values. Then, when we press the button, we add another point at a far position (50).
Related
I have some problem with my method.
I have 5 input fields on screen, and when this screen is created (onCreate method), i want that this element will appear with animation one after one and not all together.
Here is the method that I created.
private void setInputsAnimation() {
for (int i = 0; i < inputs.size(); i++) {
final int finalI = i;
handler.postDelayed(new Runnable() {
#Override
public void run() {
inputs.get(finalI).setVisibility(View.VISIBLE);
YoYo.with(Techniques.DropOut)
.duration(1000)
.repeat(0)
.playOn(inputs.get(finalI));
}
}, 500);
}
}
For some reason, when I run this method , the animation is working but for element together, and not one after one.
Here is the inputs array method:
private List<EditText> inputs = new ArrayList<>();
private List<EditText> inputs() {
inputs.add(et1);
inputs.add(et2);
inputs.add(et3);
inputs.add(et4);
inputs.add(et5);
return inputs;
}
I want to implement an image slider with viewpager. The slider should automatically scroll horizontally. So I use a timer as below:
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == NUM_PAGES-1) {
currentPage = 0;
timer.cancel();
timer = new Timer();
}
mPager.setCurrentItem(currentPage++, true);
}
};
timer = new Timer(); // This will create a new Thread
timer .schedule(new TimerTask() { // task to be scheduled
#Override
public void run() {
handler.post(Update);
}
}, 500, 3000);
mPager is the viewpager which already has been set.
The problem is that when it reaches the last item (image), it stops there and doesn't scroll anymore. Another thing, when I scroll it manually to the first page, it suddenly scrolls to the last page ignorant of timer.
Any help would be appreciated.
You can try this,
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
int currentPage = mPager.getCurrentItem();
if (currentPage == NUM_PAGES-1) {
currentPage = 0;
} else {
currentPage++;
}
mPager.setCurrentItem(currentPage, true);
handler.postDelayed(this, 5000);
}
};
handler.postDelayed(Update, 500);
I have an activity with 8 buttons in it. I want the backgrounds for all the buttons to randomly change between each other every second that the activity is running. To do this I use the following code:
public class CoinFrenzy extends Activity {
private int draw1 = R.drawable.buttonshape1;
private int draw2 = R.drawable.buttonshape2;
private int draw3 = R.drawable.buttonshape3;
private int draw4 = R.drawable.buttonshape4;
private int draw5 = R.drawable.buttonshape5;
private int draw6 = R.drawable.buttonshape6;
private int draw7 = R.drawable.buttonshape7;
private int draw8 = R.drawable.buttonshape8;
private ArrayList<Integer> selector = new ArrayList<>();
private ArrayList<Button> buttonlist = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_coin_frenzy);
Button btn1 = (Button) findViewById(R.id.angry_btn);
Button btn2 = (Button) findViewById(R.id.angry_btn2);
Button btn3 = (Button) findViewById(R.id.angry_btn3);
Button btn4 = (Button) findViewById(R.id.angry_btn4);
Button btn5 = (Button) findViewById(R.id.angry_btn5);
Button btn6 = (Button) findViewById(R.id.angry_btn6);
Button btn7 = (Button) findViewById(R.id.angry_btn7);
Button btn8 = (Button) findViewById(R.id.angry_btn8);
buttonlist.add(btn1);
buttonlist.add(btn2);
buttonlist.add(btn3);
buttonlist.add(btn4);
buttonlist.add(btn5);
buttonlist.add(btn6);
buttonlist.add(btn7);
buttonlist.add(btn8);
selector.add(draw1);
selector.add(draw2);
selector.add(draw3);
selector.add(draw4);
selector.add(draw5);
selector.add(draw6);
selector.add(draw7);
selector.add(draw8);
h.postDelayed(myRunnable, 1000);
}
public static int randInt(int min, int max) {
// NOTE: Usually this should be a field rather than a method
// variable so that it is not re-seeded every call.
Random rand = new Random();
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = rand.nextInt((max - min) + 1) + min;
return randomNum;
}
private Handler h = new Handler(Looper.getMainLooper());
private Runnable myRunnable = new Runnable() {
public void run() {
for (Integer x : selector) {
int randomInt = randInt(0, 7);
int back = selector.get(randomInt);
Button currbtn = buttonlist.get(randomInt);
currbtn.setBackgroundResource(back);
}
//run again in one second
h.postDelayed(myRunnable, 1000);
}};
However, after one second passes my app crashes without any logcat errors.
First do these 2 things:
You have to remove change the background of the button code from thread. This is the main cause of crash.
Below you can find two methods on how can one run a code on the main/UI thread on android:
runOnUIThread Activity’s method
runOnUiThread(new Runnable() {
public void run() {
// change background color here
}
});
Handler
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
// UI code goes here
}
});
you might want to use one of the two methods whenever you want to update the UI like updating colors on views or other UI related stuff. Otherwise, the application might crash because the UI classes on Android are not thread safe.
This will help you!!
Replace your
private Handler h = new Handler();
to
private Handler h = new Handler(Looper.getMainLooper());
otherwise your code will be run outside the UI thread
You have to pass dynamic index into
int randomInt = randInt(0, 7);
you are always passing same values for min is 0 and max is 7, so it doesn't change any background.
Use selector random ids replace 7 with dynamic loop value x.
private Runnable myRunnable = new Runnable() {
public void run() {
for (Integer x : selector) {
int randomInt = randInt(0, x);
int back = selector.get(randomInt);
Button currbtn = buttonlist.get(randomInt);
currbtn.setBackgroundResource(back);
}
//run again in one second
h.postDelayed(myRunnable, 1000);
}};
It would be helpful to you.
my app currently crashes when you press the home or back button saying "Unfortunately, Rocks has crashed". I have narrowed it down to the line of code that reads:
// draws the canvas on the panel
this.gamePanel.render(canvas);
in this context:
package com.background.rocks;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
private static final String TAG = MainThread.class.getSimpleName();
// desired fps
private final static int MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private Graphics gamePanel;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, Graphics gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this.gamePanel.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gamePanel.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
}
But I'm new to using canvas so I don't know how to fix it. It creates a NullPointerException so I'm assuming it's to do with when resuming the app or pausing the app the canvas isn't stored properly.
Edit: Graphics class (without imports):
package com.background.rocks;
public class Graphics extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = Graphics.class.getSimpleName();
private MainThread thread;
private Player player;
private ArrayList<Rock> rocks = new ArrayList<Rock>();
private Random random = new Random();
private CountDownTimer countdown;
public Graphics(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create shape and load bitmap
player = new Player(BitmapFactory.decodeResource(getResources(), R.drawable.player_orange), 540, 1500);
// create the game loop thread
thread = new MainThread(getHolder(), this);
// make the GamePanel focusable so it can handle events
setFocusable(true);
timer();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and
// we can safely start the game loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// delegating event handling to the shape
player.handleActionDown((int) event.getX(), (int) event.getY());
// check if in the lower part of the screen we exit
if (event.getY() > getHeight() - 50) {
thread.setRunning(false);
((Activity) getContext()).finish();
} else {
Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
}
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
// the gestures
if (player.isTouched()) {
// the shape was picked up and is being dragged
player.setX((int) event.getX());
player.setY((int) event.getY());
}
}
if (event.getAction() == MotionEvent.ACTION_UP) {
// touch was released
if (player.isTouched()) {
player.setTouched(false);
}
}
return true;
}
**public void render(Canvas canvas) {
if (canvas != null) {
canvas.drawColor(Color.WHITE);
player.draw(canvas);
Rock[] rockArray = rocks.toArray(new Rock[0]);
for (Rock rock : rockArray) {
rock.draw(canvas);
}
}
}**
/**
* This is the game update method. It iterates through all the objects and
* calls their update method if they have one or calls specific engine's
* update method.
*/
public void update() {
Rock[] rockArray = rocks.toArray(new Rock[0]);
for (Rock rock : rocks) {
rock.update();
}
}
public void timer() {
if (countdown != null) {
countdown.cancel();
}
countdown = new CountDownTimer(30000, 800) {
public void onTick(long millisUntilFinished) {
rocks.add(new Rock(BitmapFactory.decodeResource(getResources(), R.drawable.rock), random.nextInt(1080 - 1) + 1, 0));
}
public void onFinish() {
countdown.start();
}
}.start();
}
}
Edit 2: So I managed to change my Render method in Graphics and so the app doesn't crash now when you press home or back, however when I try and resume the app I get a black screen. I'm assuming because it hasn't loaded the canvas back in, but I'm not sure how to do this, any help?
Start running the thread once the surface gets created. i.e. when you get a callback surfaceCreated(), start the thread.
Code Snippet
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
This might be too late but what I did to fix it was I put a n if statement to test whether the canvas is still their before rendering and updating. Should look like this:
If (canvas != null){
this.gamePanel.render (canvas);
this.gamePanel.update ();
}
I think I followed the same tutorial as you, you will probobly encounter another issue after this on is solved. Im still looking for an answer to that issue. Hope this helps!
I want an app which changes background color when pressing a button. After 500 ms, I want to change background color to black for 2000ms. And then repeat whole process again, until user terminates that.
I have following code but its not working as I think it should.
private void set() {
rl.setBackgroundColor(Color.WHITE);
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
rl.setBackgroundColor(Color.BLACK);
set(); // can I do that?
}
});
}
}, 500);
}
Can someone point me to right direction how can I do that? So I want:
Execute some code
After X time passed by , I want to execute another code and it should stay that way for X amount of time
Repeat process until user cancels that.
Something like this should work I think
Handler handler = new Handler();
Runnable turnBlack = new Runnable(){
#Override
public void run() {
myView.setBackgroundColor(Color.BLACK);
goWhite();
}};
Runnable turnWhite = new Runnable(){
#Override
public void run() {
myView.setBackgroundColor(Color.White);
goBlack();
}};
public void goBlack() {
handler.postDelayed(turnBlack, 500);
}
public void goWhite() {
handler.postDelayed(turnWhite, 2000);
}
There is much easier way to do this using AnimationDrawable:
AnimationDrawable drawable = new AnimationDrawable();
ColorDrawable color1 = new ColorDrawable(Color.YELLOW);
ColorDrawable color2 = new ColorDrawable(Color.BLACK);
// First color yellow for 500 ms
drawable.addFrame(color1, 500);
// Second color black for 2000 ms
drawable.addFrame(color2, 2000);
// Set if animation should loop. In this case yes it will
drawable.setOneShot(false);
Button btn = (Button)findViewById(R.id.button1);
btn.setBackground(drawable);
findViewById(R.id.buttonLan).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Start animation
((AnimationDrawable)v.getBackground()).start();
}
});