Scenario is swipe right to left at a box. and you will get some dynamic result. and verify them.
U can Identify locators for 2 elements -
1 - Element till where u want to reach
2 - A Common Xpath for All Horizontal Elements in that box and run following code to Scroll Horizontally.
public static void scrollTillElementHorizontally(By by, By allElements) {
if (driver.findElements(By) > 0 || driver.findElement(By).isDisplayed())
return;
int y = driver.findElement(allElements).getCenter().y;
int width = driver.manage().window().getSize().getWidth();
double startX = width * 0.80;
double endX = width * 0.20;
TouchAction action = new TouchAction(driver);
int i = 0;
while (i < 50) {
if (driver.findElements(By) > 0 || driver.findElement(By).isDisplayed())
break;
action.press(PointOption.point((int) startX, y)).waitAction(WaitOptions.waitOptions(Duration.ofSeconds(2)))
.moveTo(PointOption.point((int) endX, y)).release().perform();
i++;
}
}
And if u want to remove duplicate elements while scrolling add the elements to HashSet which would remove duplicates while scrolling.
I'm using ARCore to build my android app, where I allowing users to place anchors. I need to be able to check if the Anchor is in the current frame.
Any idea how can I do it?
Thanks!
I created a method based on camera.worldToScreenPoint(worldPosition). So I can check if a position is visible:
fun com.google.ar.sceneform.Camera.isWorldPositionVisible(worldPosition: Vector3): Boolean {
val var2 = com.google.ar.sceneform.math.Matrix()
com.google.ar.sceneform.math.Matrix.multiply(projectionMatrix, viewMatrix, var2)
val var5: Float = worldPosition.x
val var6: Float = worldPosition.y
val var7: Float = worldPosition.z
val var8 = var5 * var2.data[3] + var6 * var2.data[7] + var7 * var2.data[11] + 1.0f * var2.data[15]
if (var8 < 0f) {
return false
}
val var9 = Vector3()
var9.x = var5 * var2.data[0] + var6 * var2.data[4] + var7 * var2.data[8] + 1.0f * var2.data[12]
var9.x = var9.x / var8
if (var9.x !in -1f..1f) {
return false
}
var9.y = var5 * var2.data[1] + var6 * var2.data[5] + var7 * var2.data[9] + 1.0f * var2.data[13]
var9.y = var9.y / var8
return var9.y in -1f..1f
}
(And I fixed the problem that Anton Stukov said in the comments)
There is a quite simple way to do this. Let's say you have an AnchorNode attached to your anchor.
First, get the node world position:
val worldPosition = node.worldPosition
Second, use scene camera to transform world position into a screen point:
val screenPoint = arFragment.arSceneView.scene.camera.worldToScreenPoint(worldPosition)
Now just check whether the point is inside screen size bounds.
If you're using ARCore, you're probably doing frustum culling where you don't render objects that aren't within the viewable space, an optimization used to stop you from making gl calls to render "unviewable" elements of your scene.
If you have access to the objects after the renderer calculates this, then you can use that value.
Another way you can do this, is by grabbing the Camera and getting the View and Projection matrices. Then you can project the anchor coordinates onto 2D screen coordinates and if the calculated coordinates are outside the screen (ie. x/y values are > or < the screen width/height). You'll have to account for objects that are behind the camera too (the dot product between the camera forward and vector from camera to anchor should be positive).
https://developers.google.com/ar/reference/java/com/google/ar/core/Camera.html.
I have problem with my algoritm. I delete position on 0, but my dataset ddint move like -1. Then i add a new Entry on position 25. Continuous this maked my project alway delete last entry and non stop adding the Entry in position 25.
Here is code:
private void addEntry() {
data = mChart.getData();
if (set1 == null) {
set1 = createSet();
data.addDataSet(set1);
}
int prog = 1;
for (int i = 0; i < prog; i++) {
float val = (float) (Math.random() * 2);
float high = (float) (Math.random() * 1) + 2f;
float low = (float) (Math.random() * 1) + 2f;
float open = (float) (Math.random() * 2) + 1f;
float close = (float) (Math.random() * 2) + 1f;
boolean even = i % 2 == 0;
data.addEntry(new CandleEntry(25, val + high, val - low, even ? val + open : val - open,
even ? val - close : val + close), 0);
data.notifyDataChanged();
mChart.notifyDataSetChanged();
mChart.setVisibleXRangeMaximum(25);
mChart.moveViewTo(data.getEntryCount() - 24, 2f, YAxis.AxisDependency.RIGHT);
}
}
private void removeLastEntry() {
CandleData data = mChart.getData();
if (data != null) {
ICandleDataSet set = data.getDataSetByIndex(0);
if (set != null) {
Entry e = set.getEntryForIndex(0);
data.removeEntry(e, 0);
data.notifyDataChanged();
mChart.notifyDataSetChanged();
mChart.invalidate();
}
}
}
DataSet#getEntryForIndex(int index) returns the entry that is first in the backing array of Entry which may or may not be the Entry with the minimum x value on the chart. From the javadoc:
Returns the Entry object found at the given index (NOT xIndex) in the values array.
I suspect you actually want to use something like the following:
float xMin = dataSet.getXMin();
dataSet.remove(dataSet.getEntryForXPos(xMin);
See the javadoc here.
However, your use case is a little bit unusual. You want all the other entries to "move down", it would seem, when you remove the Entry with the minimum value. If you are only doing this infrequently, it may be easier to construct a new dataset when you do that. Please note that while adding Entry to the chart is easy, random access to Entry and removing them is not supported so well. See the following note in the wiki
Please be aware that this library does not officially support drawing LineChart data from an Entry list not sorted by the x-position of the entries in ascending manner. Adding entries in an unsorted way may result in correct drawing, but may also lead to unexpected behaviour.
I am trying to implement something like the filters in zomato app where on selecting the filters the first screen moves showing up only a part of the first screen and whole of second screen where we can select the type of filter which upon selecting gives us all the types of filters after moving the first and the second screen both.
What I have done is I have the inner screens(filter type and filter option) as two relative layouts which are invisible and then i slide them and make them visible by changing the margins of it's layout parameters and requesting the layout .
While showing up the third screen(filter options) the second screen is not visible even if i provide the correct margins .
Can anyone help?
EDIT CODE :
private class SlidingAnimation extends Animation {
private float mainViewStartX, mainViewEndX;
private float sideViewFromX, sideViewToX;
private float sideView2FromX,sideView2ToX;
private View slideInView,slideOutView,slideInView2;
public SlidingAnimation(float fromX, float toX, float sideViewFromX,
float sideViewToX,float sideView2FromX,float sideView2ToX, int duration,View slideInView,View slideOutView,View slideInView2) {
setDuration(duration);
this.slideInView = slideInView;
this.slideOutView = slideOutView;
this.slideInView2 = slideInView2;
mainViewEndX = toX;
mainViewStartX = fromX;
slideOutViewLayoutParams = (LayoutParams) slideOutView.getLayoutParams();
this.sideViewFromX = sideViewFromX;
this.sideViewToX = sideViewToX;
slideInViewLayoutParams = (LayoutParams) slideInView.getLayoutParams();
if(slideInView2!=null){
Log.w(null,"Side View two also exists");
this.sideView2FromX = sideView2FromX ;
this.sideView2ToX = sideView2ToX;
slideInView2LayoutParams = (LayoutParams)slideInView2.getLayoutParams();
}
}
#SuppressLint("NewApi")
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Applies a Smooth Transition that starts fast but ends slowly
if(slideInView2==null){
slideOutViewLayoutParams.rightMargin = (int) (mainViewStartX + ((mainViewEndX - mainViewStartX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideInViewLayoutParams.rightMargin = (int) (sideViewFromX - ((sideViewFromX-sideViewToX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideOutViewLayoutParams.leftMargin = (int)- mainViewEndX;
slideInView.requestLayout();
slideOutView.requestLayout();
Log.w(null,"Slide Out Right Margin:"+slideOutViewLayoutParams.rightMargin);
Log.w(null,"Slide Out Left Margin:"+slideOutViewLayoutParams.leftMargin);
Log.w(null,"Slide In Left Margin:"+slideInViewLayoutParams.leftMargin);
}
else{
slideOutViewLayoutParams.rightMargin = (int) (mainViewStartX + ((mainViewEndX - mainViewStartX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideInViewLayoutParams.rightMargin = (int) (sideViewFromX + ((sideViewToX-sideViewFromX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideOutViewLayoutParams.leftMargin = (int)- mainViewEndX;
slideInViewLayoutParams.leftMargin = (int)-sideViewToX;
slideInView2LayoutParams.rightMargin = (int) (sideView2FromX - ((sideView2FromX-sideView2ToX) * (Math
.pow(interpolatedTime - 1, 5) + 1)));
slideOutView.requestLayout();
slideInView.requestLayout();
slideInView2.requestLayout();
Log.w(null,"Slide Out Right Margin:"+slideOutViewLayoutParams.rightMargin);
Log.w(null,"Slide Out Left Margin:"+slideOutViewLayoutParams.leftMargin);
Log.w(null,"Slide In Left Margin:"+slideInViewLayoutParams.leftMargin);
Log.w(null,"Slide In Right Margin:"+slideInViewLayoutParams.rightMargin);
Log.w(null,"Slide In View 2 Right Margin:"+slideInView2LayoutParams.rightMargin);
}
}
}
}
The call :
SlidingAnimation open = new SlidingAnimation(-width+dpToPx(45), width - dpToPx(45/2), 0,width-dpToPx(45),-width+dpToPx(45),0,1000,filterTypeLayout, selectBusLayout,filterOptionLayout);
Try using two HorizontalScrollViews.
Add the HSVs to a FrameLayout.
Implement OnTouchListeners for the HSVs, handle the touch events and accordingly make the HSV smoothScroll to the specific position on the screen. You could specify positions based on screen width.
example: ScrollView.smoothScrollTo(4*width/5, 0);
[UPDATE]
To conclude this question, I implemented my graph using the following two methods (see below). drawCurve() receives a Canvas and an array of float. The array is properly filled (timestamps are assumed by the value index in the array) and varies from 0.0 to 1.0. The array is sent to prepareWindowArray() that takes a chunk of the array from position windowStart for windowSize-values, in a circular manner.
The array used by the GraphView and by the data provider (a Bluetooth device) is the same. A Class in the middle ensures that GraphView is not reading data that are being written by the Bluetooth device. Since the GraphView always loop thru the array and redraw it at every iteration, it will update according to the data written by the Bluetooth device, and by forcing the write frequency of the Bluetooth device to the refresh frequency of the Graph, I obtain a smooth animation of my signal.
The GraphView's invalidate() method is called by the Activity, which run a Timer to refresh the graph at every x milliseconds. The frequency at which the graph is refreshed is dynamically set, so that it adapt to the flow of data from the Bluetooth device (which specify the frequency of its signal in the header of its packet).
Find the complete code of my GraphView in the answer I wrote below (in the answer section). If you guys find errors or way to optimize it, please let me know; it would be greatly appreciated!
/**
* Read a buffer array of size greater than "windowSize" and create a window array out of it.
* A curve is then drawn from this array using "windowSize" points, from left
* to right.
* #param canvas is a Canvas object on which the curve will be drawn. Ensure the canvas is the
* later drawn object at its position or you will not see your curve.
* #param data is a float array of length > windowSize. The floats must range between 0.0 and 1.0.
* A value of 0.0 will be drawn at the bottom of the graph, while a value of 1.0 will be drawn at
* the top of the graph. The range is not tested, so you must ensure to pass proper values, or your
* graph will look terrible.
* 0.0 : draw at the bottom of the graph
* 0.5 : draw in the middle of the graph
* 1.0 : draw at the top of the graph
*/
private void drawCurve(Canvas canvas, float[] data){
// Create a reference value to determine the stepping between each points to be drawn
float incrementX = (mRightSide-mLeftSide)/(float) windowSize;
float incrementY = (mBottomSide - mTopSide);
// Prepare the array for the graph
float[] source = prepareWindowArray(data);
// Prepare the curve Path
curve = new Path();
// Move at the first point.
curve.moveTo(mLeftSide, source[0]*incrementY);
// Draw the remaining points of the curve
for(int i = 1; i < windowSize; i++){
curve.lineTo(mLeftSide + (i*incrementX), source[i] * incrementY);
}
canvas.drawPath(curve, curvePaint);
}
The prepareWindowArray() method that implement the circular behavior of the array:
/**
* Extract a window array from the data array, and reposition the windowStart
* index for next iteration
* #param data the array of data from which we get the window
* #return an array of float that represent the window
*/
private float[] prepareWindowArray(float[] data){
// Prepare the source array for the graph.
float[] source = new float[windowSize];
// Copy the window from the data array into the source array
for(int i = 0; i < windowSize; i++){
if(windowStart+i < data.length) // If the windows holds within the data array
source[i] = data[windowStart + i]; // Simply copy the value in the source array
else{ // If the window goes beyond the data array
source[i] = data[(windowStart + 1)%data.length]; // Loop at the beginning of the data array and copy from there
}
}
// Reposition the buffer index
windowStart = windowStart + windowSize;
// If the index is beyond the end of the array
if(windowStart >= data.length){
windowStart = windowStart % data.length;
}
return source;
}
[/UPDATE]
I'm making an app that read data from a Bluetooth device at a fixed rate. Everytime that I have new data, I want them to be plotted on the graph to the right, and to translate the remainder of the graph to the left in realtime. Basically, like an oscilloscope would do.
So I made a custom View, with xy axis, a title and units. To do this, I simply draw those things on the View canvas. Now I want to draw the curve. I manage to draw a static curve from an already filled array using this method:
public void drawCurve(Canvas canvas){
int left = getPaddingLeft();
int bottom = getHeight()-getPaddingTop();
int middle = (bottom-10)/2 - 10;
curvePaint = new Paint();
curvePaint.setColor(Color.GREEN);
curvePaint.setStrokeWidth(1f);
curvePaint.setDither(true);
curvePaint.setStyle(Paint.Style.STROKE);
curvePaint.setStrokeJoin(Paint.Join.ROUND);
curvePaint.setStrokeCap(Paint.Cap.ROUND);
curvePaint.setPathEffect(new CornerPathEffect(10) );
curvePaint.setAntiAlias(true);
mCurve = new Path();
mCurve.moveTo(left, middle);
for(int i = 0; i < mData[0].length; i++)
mCurve.lineTo(left + ((float)mData[0][i] * 5), middle-((float)mData[1][i] * 20));
canvas.drawPath(mCurve, curvePaint);
}
It gives me something like this.
There are still things to fix on my graph (the sub-axis are not properly scaling), but these are details I can fix later.
Now I want to change this static graph (that receives a non-dynamic matrice of values) with something dynamic that would redraw the curve every 40ms, pushing the old data to the left and plotting the new data to the right, so I could visualise in real time the information provided by the Bluetooth device.
I know there are some graphing package that exists already, but I'm kinda noob with these things and I'd like to pratice by implementing this graph myself. Also, most of my GraphView class is done, except for the curve part.
Second question, I'm wondering how I should send the new values to the graph. Should I use something like a FIFO stack, or can I achieve what I want with a simple matrice of doubles?
On a side note, the 4 fields at the bottom are already dynamically updated. Well, they are kind of faking the "dynamic", they loop thru the same double matrice again and again, they don't actually take fresh values.
Thanks for your time! If something's unclear about my question, let me know and I'll update it with more details.
As mentioned in my question, here's the class that I designed to solve my problems.
/**
* A View implementation that displays a scatter graph with
* automatic unit scaling.
*
* Call the <i>setupGraph()</i> method to modify the graph's
* properties.
* #author Antoine Grondin
*
*/
public class GraphView extends View {
//////////////////////////////////////////////////////////////////
// Configuration
//////////////////////////////////////////////////////////////////
// Set to true to impose the graph properties
private static final boolean TEST = false;
// Scale configuration
private float minX = 0; // When TEST is true, these values are used to
private float maxX = 50; // Draw the graph
private float minY = 0;
private float maxY = 100;
private String titleText = "A Graph...";
private String xUnitText = "s";
private String yUnitText = "Volts";
// Debugging variables
private boolean D = true;
private String TAG = "GraphView";
//////////////////////////////////////////////////////////////////
// Member fields
//////////////////////////////////////////////////////////////////
// Represent the borders of the View
private int mTopSide = 0;
private int mLeftSide = 0;
private int mRightSide = 0;
private int mBottomSide = 0;
private int mMiddleX = 0;
// Size of a DensityIndependentPixel
private float mDips = 0;
// Hold the position of the axis in regard to the range of values
private int positionOfX = 0;
private int positionOfY = 0;
// Index for the graph array window, and size of the window
private int windowStart = 0;
private int windowSize = 128;
private float[] dataSource;
// Painting tools
private Paint xAxisPaint;
private Paint yAxisPaint;
private Paint tickPaint;
private Paint curvePaint;
private Paint backgroundPaint;
private TextPaint unitTextPaint;
private TextPaint titleTextPaint;
// Object to be drawn
private Path curve;
private Bitmap background;
///////////////////////////////////////////////////////////////////////////////
// Constructors
///////////////////////////////////////////////////////////////////////////////
public GraphView(Context context) {
super(context);
init();
}
public GraphView(Context context, AttributeSet attrs){
super(context, attrs);
init();
}
public GraphView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
init();
}
///////////////////////////////////////////////////////////////////////////////
// Configuration methods
///////////////////////////////////////////////////////////////////////////////
public void setupGraph(String title, String nameOfX, float min_X, float max_X, String nameOfY, float min_Y, float max_Y){
if(!TEST){
titleText = title;
xUnitText = nameOfX;
yUnitText = nameOfY;
minX = min_X;
maxX = max_X;
minY = min_Y;
maxY = max_Y;
}
}
/**
* Set the array this GraphView is to work with.
* #param data is a float array of length > windowSize. The floats must range between 0.0 and 1.0.
* A value of 0.0 will be drawn at the bottom of the graph, while a value of 1.0 will be drawn at
* the top of the graph. The range is not tested, so you must ensure to pass proper values, or your
* graph will look terrible.
* 0.0 : draw at the bottom of the graph
* 0.5 : draw in the middle of the graph
* 1.0 : draw at the top of the graph
*/
public void setDataSource(float[] data){
this.dataSource = data;
}
///////////////////////////////////////////////////////////////////////////////
// Initialization methods
///////////////////////////////////////////////////////////////////////////////
private void init(){
initDrawingTools();
}
private void initConstants(){
mDips = getResources().getDisplayMetrics().density;
mTopSide = (int) (getTop() + 10*mDips);
mLeftSide = (int) (getLeft() + 10*mDips);
mRightSide = (int) (getMeasuredWidth() - 10*mDips);
mBottomSide = (int) (getMeasuredHeight() - 10*mDips);
mMiddleX = (mRightSide - mLeftSide)/2 + mLeftSide;
}
private void initWindowSetting() throws IllegalArgumentException {
// Don't do anything if the given values make no sense
if(maxX < minX || maxY < minY ||
maxX == minX || maxY == minY){
throw new IllegalArgumentException("Max and min values make no sense");
}
// Transform the values in scanable items
float[][] maxAndMin = new float[][]{
{minX, maxX},
{minY, maxY}};
int[] positions = new int[]{positionOfY, positionOfX};
// Place the X and Y axis in regard to the given max and min
for(int i = 0; i<2; i++){
if(maxAndMin[i][0] < 0f){
if(maxAndMin[i][1] < 0f){
positions[i] = (int) maxAndMin[i][0];
} else{
positions[i] = 0;
}
} else if (maxAndMin[i][0] > 0f){
positions[i] = (int) maxAndMin[i][0];
} else {
positions[i] = 0;
}
}
// Put the values back in their right place
minX = maxAndMin[0][0];
maxX = maxAndMin[0][1];
minY = maxAndMin[1][0];
maxY = maxAndMin[1][1];
positionOfY = mLeftSide + (int) (((positions[0] - minX)/(maxX-minX))*(mRightSide - mLeftSide));
positionOfX = mBottomSide - (int) (((positions[1] - minY)/(maxY-minY))*(mBottomSide - mTopSide));
}
private void initDrawingTools(){
xAxisPaint = new Paint();
xAxisPaint.setColor(0xff888888);
xAxisPaint.setStrokeWidth(1f*mDips);
xAxisPaint.setAlpha(0xff);
xAxisPaint.setAntiAlias(true);
yAxisPaint = xAxisPaint;
tickPaint = xAxisPaint;
tickPaint.setColor(0xffaaaaaa);
curvePaint = new Paint();
curvePaint.setColor(0xff00ff00);
curvePaint.setStrokeWidth(1f*mDips);
curvePaint.setDither(true);
curvePaint.setStyle(Paint.Style.STROKE);
curvePaint.setStrokeJoin(Paint.Join.ROUND);
curvePaint.setStrokeCap(Paint.Cap.ROUND);
curvePaint.setPathEffect(new CornerPathEffect(10));
curvePaint.setAntiAlias(true);
backgroundPaint = new Paint();
backgroundPaint.setFilterBitmap(true);
titleTextPaint = new TextPaint();
titleTextPaint.setAntiAlias(true);
titleTextPaint.setColor(0xffffffff);
titleTextPaint.setTextAlign(Align.CENTER);
titleTextPaint.setTextSize(20f*mDips);
titleTextPaint.setTypeface(Typeface.MONOSPACE);
unitTextPaint = new TextPaint();
unitTextPaint.setAntiAlias(true);
unitTextPaint.setColor(0xff888888);
unitTextPaint.setTextAlign(Align.CENTER);
unitTextPaint.setTextSize(20f*mDips);
unitTextPaint.setTypeface(Typeface.MONOSPACE);
}
///////////////////////////////////////////////////////////////////////////////
// Overridden methods
///////////////////////////////////////////////////////////////////////////////
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
regenerateBackground();
}
public void onDraw(Canvas canvas){
drawBackground(canvas);
if(dataSource != null)
drawCurve(canvas, dataSource);
}
///////////////////////////////////////////////////////////////////////////////
// Drawing methods
///////////////////////////////////////////////////////////////////////////////
private void drawX(Canvas canvas){
canvas.drawLine(mLeftSide, positionOfX, mRightSide, positionOfX, xAxisPaint);
canvas.drawText(xUnitText, mRightSide - unitTextPaint.measureText(xUnitText)/2, positionOfX - unitTextPaint.getTextSize()/2, unitTextPaint);
}
private void drawY(Canvas canvas){
canvas.drawLine(positionOfY, mTopSide, positionOfY, mBottomSide, yAxisPaint);
canvas.drawText(yUnitText, positionOfY + unitTextPaint.measureText(yUnitText)/2 + 4*mDips, mTopSide + (int) (unitTextPaint.getTextSize()/2), unitTextPaint);
}
private void drawTick(Canvas canvas){
// No tick at this time
// TODO decide how I want to put those ticks, if I want them
}
private void drawTitle(Canvas canvas){
canvas.drawText(titleText, mMiddleX, mTopSide + (int) (titleTextPaint.getTextSize()/2), titleTextPaint);
}
/**
* Read a buffer array of size greater than "windowSize" and create a window array out of it.
* A curve is then drawn from this array using "windowSize" points, from left
* to right.
* #param canvas is a Canvas object on which the curve will be drawn. Ensure the canvas is the
* later drawn object at its position or you will not see your curve.
* #param data is a float array of length > windowSize. The floats must range between 0.0 and 1.0.
* A value of 0.0 will be drawn at the bottom of the graph, while a value of 1.0 will be drawn at
* the top of the graph. The range is not tested, so you must ensure to pass proper values, or your
* graph will look terrible.
* 0.0 : draw at the bottom of the graph
* 0.5 : draw in the middle of the graph
* 1.0 : draw at the top of the graph
*/
private void drawCurve(Canvas canvas, float[] data){
// Create a reference value to determine the stepping between each points to be drawn
float incrementX = (mRightSide-mLeftSide)/(float) windowSize;
float incrementY = mBottomSide - mTopSide;
// Prepare the array for the graph
float[] source = prepareWindowArray(data);
// Prepare the curve Path
curve = new Path();
// Move at the first point.
curve.moveTo(mLeftSide, source[0]*incrementY);
// Draw the remaining points of the curve
for(int i = 1; i < windowSize; i++){
curve.lineTo(mLeftSide + (i*incrementX), source[i] * incrementY);
}
canvas.drawPath(curve, curvePaint);
}
///////////////////////////////////////////////////////////////////////////////
// Intimate methods
///////////////////////////////////////////////////////////////////////////////
/**
* When asked to draw the background, this method will verify if a bitmap of the
* background is available. If not, it will regenerate one. Then, it will draw
* the background using this bitmap. The use of a bitmap to draw the background
* is to avoid unnecessary processing for static parts of the view.
*/
private void drawBackground(Canvas canvas){
if(background == null){
regenerateBackground();
}
canvas.drawBitmap(background, 0, 0, backgroundPaint);
}
/**
* Call this method to force the <i>GraphView</i> to redraw the cache of it's background,
* using new properties if you changed them with <i>setupGraph()</i>.
*/
public void regenerateBackground(){
initConstants();
try{
initWindowSetting();
} catch (IllegalArgumentException e){
Log.e(TAG, "Could not initalize windows.", e);
return;
}
if(background != null){
background.recycle();
}
background = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas backgroundCanvas = new Canvas(background);
drawX(backgroundCanvas);
drawY(backgroundCanvas);
drawTick(backgroundCanvas);
drawTitle(backgroundCanvas);
}
/**
* Extract a window array from the data array, and reposition the windowStart
* index for next iteration
* #param data the array of data from which we get the window
* #return an array of float that represent the window
*/
private float[] prepareWindowArray(float[] data){
// Prepare the source array for the graph.
float[] source = new float[windowSize];
// Copy the window from the data array into the source array
for(int i = 0; i < windowSize; i++){
if(windowStart+i < data.length) // If the windows holds within the data array
source[i] = data[windowStart + i]; // Simply copy the value in the source array
else{ // If the window goes beyond the data array
source[i] = data[(windowStart + 1)%data.length]; // Loop at the beginning of the data array and copy from there
}
}
// Reposition the buffer index
windowStart = windowStart + windowSize;
// If the index is beyond the end of the array
if(windowStart >= data.length){
windowStart = windowStart % data.length;
}
return source;
}
}
Well I would start by just trying to redraw it all with the code you have and real dynalic data. Only if that is not quick enough do you need to try anything fancy like scrolling...
If you need fancy I would try somthing like this.
I would draw the dynamic part of the graph into a secondary Bitmap that you keep between frames rather than directly to the canves. I would have the background none dynamic part of the graph in another bitmap that only gets drawen on rescale etc.
In this secondary dynamic bitmap when ploting new data you first need to clear the old data you are replacing you do this by drawing the apropriate slice of the static background bitmap over the top of the stale data, thus clearing it and geting the background nice and fresh again. You then just need to draw your new bit of dynamic data. The trick is that You draw into this second bitmap left to right then just wrap back to the left at the end and start over.
To get from the soncodary bitmap to your cancas draw the bitmap to the canvas in two parts. The older data to the right of what you just added needs to be drawn onto the left part of your final canvas and the new data needs to be drawn imediatly to the right of it.
For sending the data a circular buffer would be the normal thing for this sort of data where once it's off the graph you don't care about it.