2016-01-03 5 views
8

まず、私のラインの一部を自由に削除する必要があるため、これは他の「スムーズライン」の質問と重複しません。私の行を保存する特別な方法。特別なラインをスムーズにフォローする必要があります。他の動作もあります。

私はユーザーの指に沿って線を引く必要があります。しかし、私はまた、この行の末尾を自由に削除することができる必要があります。

基本的に私はこのゲームでは、ユーザーのマウスを以下の青色の線のように見えるように、このラインの動作を必要とする:私は追加私のonTouch方法でいくつかのコードを持ってこれを行うには

http://hakim.se/experiments/html5/coil/

ユーザーの指が動くたびに配列をポイントします。

@Override 
public boolean onTouch(View v, MotionEvent event) { 



    //This for loop is supposed to add all points that were in between this 
    //motion event and the previous motion event to the "linePoints" array. 

    for(int i = 0; i < event.getHistorySize(); i++) { 
     linePoints[arrayIndex] = new Point((int) event.getHistoricalX(i), (int) event.getHistoricalY(i)); 
     arrayIndex++; 
    } 

    //This adds the current location of the user's finger to "linePoints" 
    // array 
    linePoints[arrayIndex] = new Point((int) event.getX(), (int) event.getY()); 
    arrayIndex++; 

    //This switch statement makes it so that if the user lifts their finger 
    // off the screen the line will get deleted. 
    switch (event.getAction()) { 
     case MotionEvent.ACTION_DOWN: 
      screenPressed = true; 
      setEventTime(); //Ignore setEventTime(); 
      break; 
     case MotionEvent.ACTION_UP: 
      screenPressed = false; 
      linePoints = new Point[10000]; 
      arrayIndex = 0; 
      break; 
    } 
return true; 


} 

はその後onDraw()メソッドではゲームはライン上のすべての点を描画します。

@Override 
public void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 


    //This code loops through all of linePoints and then draws every point 
    // in linePoints to create a line on screen. 
    for(Point p : linePoints) { 
     if(p == null) { 
      break; 
     } 

     canvas.drawRect(p.x, p.y, p.x+ 2, p.y + 2, black); 
     invalidate(); 

    // I have not added in the deletion behavior yet, because my current 
    // method cannot create a smooth line. 
    } 

私が代わりにAndroidの パス()クラスを使用してのラインを作るためのポイントを描画することを選んだ理由なぜなら私は自由に行の一部を削除したいからです(配列 "linePoints"からポイントを削除することによって)。

問題は、指をあまりに速く動かしてポイントが広がり、ラインのように見えなくなることです。

どのようにしてラインがスムーズになりますが、その一部を削除できるように保存することもできますか?

編集:私が提供するように、どのように線が詳細にされるかについての詳細が誰かに尋ねられました。

「X」秒以上行を描画している場合は、その行を削除したいと考えています。私が行を削除する方法は次のとおりです。

行の終わりは、行が完全に削除されるまで、またはユーザーが画面から指を離すまで、(ユーザーがそれを描いているまで)消えていきます。

EDIT 2:ラインが交差しているかどうかを知る必要があります。そのため、ポイントストレージシステムを選択した理由は、アレイの2ポイントが同じ座標を持っていたら私はラインが交差したかどうかを知るだろう)。私は現在、これをどのように実装するのか考えていません(ポイントが連続していないためです)が、何かを見つけたらさらに編集を行います。

EDIT 3:ポイントが散発的に離れていても、ラインが交差するかどうかを判断する解決策を見つけました。しかし、私はまだギャップのない滑らかな線を作るという問題を解決していません。

ソリューション:

たびにゲームは、それが配列とモデル線分「A」に追加し、前のポイントにそれを比較します配列に新しいポイントを追加します。次に、線分 "A"と、配列内の2点から作成されたすべての前の線分を比較し、比較された線分が交差するかどうかを判断します。彼らがそうするなら、私はラインに交差点があることを知っています。

EDIT 4:これは、現在使用している最新の完全なコードです。詳細なコメントを提供するために、このコードI(TRY)と私の目標と私がこれまで行っているの説明上部の概要内部

この大きなコードの前に、私の現在の問題は、ユーザーが特定の時間以上線を描いていれば、一定のペース(例えば10ミリメートル/秒)で線を削除できることです。

package com.vroy.trapper; 

import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.graphics.Point; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.View; 


public class GameView extends View implements View.OnTouchListener { 

    // I am basically trying to replicate the game: http://hakim.se/experiments/html5/coil/ 
    // without any of the lighting effects and my version will have slightly 
    // different behavior. 

    // Right now all I am concerned with is allowing the line to be deleted at a constant pace 
    // if the user has been drawing a line for more than "X" seconds. 


    /* 
    OVERVIEW: 

    array of points "linePoints" stores all locations of user touching screen 
    that are captured by system. 

    Each time a new point is added to "linePoints" I draw a path from the previous point 
    to the new point. (Sidenote: While this does make the line look very smooth it can still look odd sometimes) 

    The game also checks for intersections in the line to see if the line has made a 
    polygon. I do this because this is important for a feature that will be implemented. 

    The system then draws the path on screen. 

    The system also checks if the user has lifted their finger off the screen, 
    if the user has then the system deletes the current line on screen and resets all variables. 

    TO BE IMPLEMENTED: 

    If the line has formed a polygon then the game will check if that polygon contains certain 
    objects that will randomly spawn onscreen. 


    PROBLEMS: 

    1. Currently I want the line to start deleting itself from the back if the user 
    has been drawing the line for more then "X" seconds. However I am not sure how to do this. 

    */ 


    // General variables. 
    private int screenWidth; 
    private int screenHeight; 
    public static boolean screenPressed; //Might not need. 
    // public static float contactLocX; 
    // public static float contactLocY; 

     //Time variables. 
     private static long startTime; //This variable is used in conjunction with the 
             //elapsedTime() method to determine if the user 
             // has been drawing a line for more then "X" seconds. 


    //Game variables. 
    private static int orbWidth; //Not used currently. This will be the width of the randomly spawned tokens. 
    private Point[] linePoints; //The array that holds all captured points. 
    private int arrayIndex; 
    private Path linePath; //The path that the canvas draws. 
    private boolean firstPoint; //If firstPoint is true then that means is 1st point in current line. 
           //I need this for the path.MoveTo() method. 

    //Debug values. (Not used currently) 
    private int debug; 
    private String strdebug; 

    //Paints 
    Paint black = new Paint(); 


    public GameView(Context context, AttributeSet attrs) { 
     super(context, attrs); 


     black.setARGB(255, 0, 0, 0); //Paint used to draw line. 
     black.setStyle(Paint.Style.STROKE); 
     black.setStrokeWidth(3); 


     linePoints = new Point[10000]; 

     GameView gameView = (GameView) findViewById(R.id.GameScreen); //Setting up onTouch listener. 
     gameView.setOnTouchListener(this); 

     arrayIndex = 0; 
     linePath = new Path(); //Setting up initial path. 
     firstPoint = true; 
    } 


    //Currently OnSizeChanged is not needed, I only keep it for the future when I implement 
    // the random object spawning system. 
    @Override 
    public void onSizeChanged(int w, int h, int oldw, int oldh) { 
     super.onSizeChanged(w, h, oldw, oldh); 
     screenHeight = getHeight(); 
     screenWidth = getWidth(); 
     orbWidth = screenHeight/20; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 

     canvas.drawPath(linePath, black); 

     //Currently "1000000000" is a placeholder value (in nano-seconds) 
     if(elapsedTime() > 1000000000) { 

      //Code that evenly deletes the line starting from the back 
      //(this is where I most need your assistance). 


     } 

     invalidate(); //I don't know if this is the best way to refresh the screen 

    } 



    @Override 
    public boolean onTouch(View v, MotionEvent event) { 

     //Sets up starting point of path 
     if(firstPoint) { 
      firstPoint = false; 
      linePath.moveTo(event.getX(),event.getY()); 
      linePoints.add(new TimeStampedPoint((int)event.getX(),    (int)event.getY(),event.getEventTime())); 



     } 

     //Adds points to path & linePoints that were missed. 
     for(int i = 0; i < event.getHistorySize(); i++) { 
      linePoints[arrayIndex] = new Point((int) event.getHistoricalX(i), (int) event.getHistoricalY(i)); 
      linePath.lineTo(linePoints[arrayIndex].x,linePoints[arrayIndex].y); 
      if(arrayIndex >= 1) { 
       checkForIntersections(linePoints[arrayIndex - 1], linePoints[arrayIndex]); 
      } 
      arrayIndex++; 
     } 

     //Adds current point to path & linePath(); 
     linePoints[arrayIndex] = new Point((int) event.getX(), (int) event.getY()); 
     if (arrayIndex >= 1) { 
      checkForIntersections(linePoints[arrayIndex - 1] ,linePoints[arrayIndex]); 
     } 
     linePath.lineTo(linePoints[arrayIndex].x,linePoints[arrayIndex].y); 
     arrayIndex++; 

     //This switch statements creates initial actions for when the finger is pressed/lifted. 
     switch (event.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
       screenPressed = true; 
       setEventTime(); //This starts the timer that will eventually reach "X" seconds. 
       break; 
      case MotionEvent.ACTION_UP: //The primary purpose of this "switch" is to delete the old line 
             // & reset variables in preparation for new line 
       screenPressed = false; 
       linePoints = new Point[10000]; //Possibly filling heap with empty arrays. 
       linePath = new Path(); 
       arrayIndex = 0; 
       firstPoint = true; 
       break; 
     } 

     return true; 
    } 


    private void checkForIntersections(Point p, Point p2) { 

     for(int i = arrayIndex - 3; i > 0; i--) { 
      if(intersect(p,p2,linePoints[i],linePoints[i-1])) { 
       //RETURN POINTS IN THE POLYGON THAT WILL BE USED TO DETERMINE IF "TOKENS" 
       // ARE IN POLYGON. 
      } 
     } 
    } 

    private void setEventTime() { 
    startTime = System.nanoTime(); 
    } 

    //Checks current time since setEventTime 
    private long elapsedTime() { 
    return System.nanoTime() - startTime; 
    } 

    // Things used to determine intersections. 

    //Used to determine orientation of <something> 
    private static int orientation(Point p, Point q, Point r) { 
     double val = (q.y - p.y) * (r.x - q.x) 
       - (q.x - p.x) * (r.y - q.y); 

     if (val == 0.0) 
      return 0; // colinear 
     return (val > 0) ? 1 : 2; // clock or counterclock wise 
    } 

    //Determines intersection of 2 lines (P1,Q1) & (P2,Q2). 
    private static boolean intersect(Point p1, Point q1, Point p2, Point q2) { 

     int o1 = orientation(p1, q1, p2); 
     int o2 = orientation(p1, q1, q2); 
     int o3 = orientation(p2, q2, p1); 
     int o4 = orientation(p2, q2, q1); 

     if (o1 != o2 && o3 != o4) 
      return true; 

     return false; 
    } 

    //Will shorten checking process by determining if 2 lines do/don't have the same bounding box. 
    //Not yet implemented. 
    private static boolean boundBoxCheck(Point p1, Point q1, Point p2, Point q2) { 
     return true; //Placeholder code 
    } 


    } 

EDIT 5:

私が原因境界エラーのうちインデックスにstKentさん/タイタンのコードと私のコードのクラッシュの私の実装を行いました。

私は、問題を見つけて、それを修正しようとしますが、私は他の誰かがそれを固定で手を取りたい包み、私はここに私のコードを掲載しますやるまで。

package com.vroy.trapper; 

import android.annotation.SuppressLint; 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Paint; 
import android.graphics.Path; 
import android.graphics.Point; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.View; 

import java.sql.Time; 
import java.util.ArrayList; 
import java.util.List; 


public class GameView extends View implements View.OnTouchListener { 

    // I am basically trying to replicate the game: http://hakim.se/experiments/html5/coil/ 
    // without any of the lighting effects and my version will have slightly 
    // different behavior. 

    // Right now all I am concerned with is allowing the line to be deleted at a constant pace 
    // if the user has been drawing a line for more than "X" seconds. 


    /* 
    OVERVIEW: 

    array of points "linePoints" stores all locations of user touching screen 
    that are captured by system. 

    Each time a new point is added to "linePoints" I draw a path from the previous point 
    to the new point. (Sidenote: While this does make the line look very smooth it can still look odd sometimes) 

    The game also checks for intersections in the line to see if the line has made a 
    polygon. I do this because this is important for a feature that will be implemented. 

    The system then draws the path on screen. 

    The system also checks if the user has lifted their finger off the screen, 
    if the user has then the system deletes the current line on screen and resets all variables. 

    TO BE IMPLEMENTED: 

    If the line has formed a polygon then the game will check if that polygon contains certain 
    objects that will randomly spawn onscreen. 


    PROBLEMS: 

    1. Currently I want the line to start deleting itself from the back if the user 
    has been drawing the line for more then "X" seconds. However I am not sure how to do this. 

    */ 


    // General variables. 
    private int screenWidth; 
    private int screenHeight; 
    public static boolean screenPressed; //Might not need. 
// public static float contactLocX; 
// public static float contactLocY; 

     //Time variables. 
     private static long startTime; //This variable is used in conjunction with the 
             //elapsedTime() method to determine if the user 
             // has been drawing a line for more then "X" seconds. 


    //Game variables. 
    private static int orbWidth; //Not used currently. This will be the width of the randomly spawned tokens. 
    private List<TimeStampedPoint> linePoints; //The array that holds all captured points. 
    private int arrayIndex; 
    private Path linePath; //The path that the canvas draws. 
    private List<TimeStampedPoint> validPoints; 
    private boolean firstPoint; //If firstPoint is true then that means is 1st point in current line. 
           //I need this for the path.MoveTo() method. 

    //Debug values. (Not used currently) 
    private int debugint; 
    private String strdebug; 

    //Paints 
    Paint black = new Paint(); 


    public GameView(Context context, AttributeSet attrs) { 
     super(context, attrs); 


     black.setARGB(255, 0, 0, 0); //Paint used to draw line. 
     black.setStyle(Paint.Style.STROKE); 
     black.setStrokeWidth(3); 


     linePoints = new ArrayList<>(); 
     validPoints = new ArrayList<>(); 

     GameView gameView = (GameView) findViewById(R.id.GameScreen); //Setting up onTouch listener. 
     gameView.setOnTouchListener(this); 

     arrayIndex = 0; 
     linePath = new Path(); //Setting up initial path. 
     validPoints = new ArrayList<>(); 
     firstPoint = true; 
    } 


    //Currently OnSizeChanged is not needed, I only keep it for the future when I implement 
    // the random object spawning system. 
    @Override 
    public void onSizeChanged(int w, int h, int oldw, int oldh) { 
     super.onSizeChanged(w, h, oldw, oldh); 
     screenHeight = getHeight(); 
     screenWidth = getWidth(); 
     orbWidth = screenHeight/20; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 

     linePath.rewind(); 

     validPoints = removeExpiredPoints(); 
     updatePathUsingPoints(validPoints); 

     canvas.drawPath(linePath, black); 


     linePoints = validPoints; 


     invalidate(); //I don't know if this is the best way to refresh the screen 

    } 



    @Override 
    public boolean onTouch(View v, MotionEvent event) { 
     debugint = arrayIndex; 
     strdebug = Integer.toString(debugint); 

     Log.i("ARRAY INDEX: ",strdebug); 

     debugint = linePoints.size(); 
     strdebug = Integer.toString(debugint); 

     Log.i("LIST SIZE: ",strdebug); 



     //Sets up starting point of path 
     if(firstPoint) { 
      firstPoint = false; 
      linePath.moveTo(event.getX(),event.getY()); 
      linePoints.add(new TimeStampedPoint((int)event.getX(),(int)event.getY(),event.getEventTime())); 
     } 

     //Adds points to path & linePoints that were missed. 
     for(int i = 0; i < event.getHistorySize(); i++) { 
      linePoints.add(new TimeStampedPoint((int) event.getHistoricalX(i), (int) event.getHistoricalY(i), event.getHistoricalEventTime(i))); 
      linePath.lineTo(linePoints.get(arrayIndex).x,linePoints.get(arrayIndex).y); 
      if(arrayIndex >= 1) { 
       checkForIntersections(linePoints.get(arrayIndex), linePoints.get(arrayIndex)); 
      } 
      arrayIndex++; 
     } 

     //Adds current point to path & linePath(); 

     debugint = linePoints.size(); 
     strdebug = Integer.toString(debugint); 
     Log.i("Before" , strdebug); 



     linePoints.add(new TimeStampedPoint((int) event.getX(), (int) event.getY(),event.getEventTime())); 

     debugint = linePoints.size(); 
     strdebug = Integer.toString(debugint); 
     Log.i("After:", strdebug); 


     if (arrayIndex >= 1) { 
      checkForIntersections(linePoints.get(arrayIndex - 1) ,linePoints.get(arrayIndex)); 
     } 
     linePath.lineTo(linePoints.get(arrayIndex).x,linePoints.get(arrayIndex).y); 
     arrayIndex++; 

     //This switch statements creates initial actions for when the finger is pressed/lifted. 
     switch (event.getAction()) { 
      case MotionEvent.ACTION_DOWN: 
       screenPressed = true; 
       setEventTime(); //This starts the timer that will eventually reach "X" seconds. 
       break; 
      case MotionEvent.ACTION_UP: //The primary purpose of this "switch" is to delete the old line 
             // & reset variables in preparation for new line 
       screenPressed = false; 
       linePoints.clear(); 
       linePath = new Path(); 
       arrayIndex = 0; 
       firstPoint = true; 
       break; 
     } 

     return true; 
    } 


    private void checkForIntersections(TimeStampedPoint p, TimeStampedPoint p2) { 

     for(int i = arrayIndex - 3; i > 0; i--) { 
      if(intersect(p,p2,linePoints.get(i),linePoints.get(i-1))) { 
       //RETURN POINTS IN THE POLYGON THAT WILL BE USED TO DETERMINE IF "TOKENS" 
       // ARE IN POLYGON. 
      } 
     } 
    } 

    private void setEventTime() { 
    startTime = System.nanoTime(); 
    } 

    //Checks current time since setEventTime 
    private long elapsedTime() { 
    return System.nanoTime() - startTime; 
    } 

    // Things used to determine intersections. 

    //Used to determine orientation of <something> 
    private static int orientation(Point p, Point q, Point r) { 
     double val = (q.y - p.y) * (r.x - q.x) 
       - (q.x - p.x) * (r.y - q.y); 

     if (val == 0.0) 
      return 0; // colinear 
     return (val > 0) ? 1 : 2; // clock or counterclock wise 
    } 

    //Determines intersection of 2 lines (P1,Q1) & (P2,Q2). 
    private static boolean intersect(TimeStampedPoint p1, TimeStampedPoint q1, TimeStampedPoint p2, TimeStampedPoint q2) { 

     int o1 = orientation(p1, q1, p2); 
     int o2 = orientation(p1, q1, q2); 
     int o3 = orientation(p2, q2, p1); 
     int o4 = orientation(p2, q2, q1); 

     if (o1 != o2 && o3 != o4) 
      return true; 

     return false; 
    } 

    //Will shorten checking process by determining if 2 lines do/don't have the same bounding box. 
    //Not yet implemented. 
    private static boolean boundBoxCheck(Point p1, Point q1, Point p2, Point q2) { 
     return true; //Placeholder code 
    } 


    //Point class that also stores time of creation 
    @SuppressLint("ParcelCreator") 
    private static class TimeStampedPoint extends Point { 

     private final long timeStamp; 

     private TimeStampedPoint(final int x, final int y, final long timeStamp) { 
      super(x, y); 
      this.timeStamp = timeStamp; 
     } 
    } 


    private List<TimeStampedPoint> removeExpiredPoints() { 
     final List<TimeStampedPoint> result = new ArrayList<>(); 

     for (final TimeStampedPoint point: linePoints) { 
      if (System.currentTimeMillis() - point.timeStamp <= 10000) { 
       // We only include points in the result if they are not expired. 
       result.add(point); 
      } 
     } 

     return result; 
    } 

    private void updatePathUsingPoints(final List<TimeStampedPoint> validPoints) { 
     if (validPoints.size() < 2) { 
      return; // Return the empty path here; nothing to draw. 
     } 

     linePath.moveTo(validPoints.get(0).x,validPoints.get(0).y); 

     for (int i = 1; i < validPoints.size(); i++) { 
      final Point targetPoint = validPoints.get(i); 
      linePath.lineTo(targetPoint.x, targetPoint.y); 
     } 


    } 

} 

私は注意する必要がある非常に非常に重要なこともあります。 私はそれが編集4までこれを注目していない私のせいであると考えているが、私はラインで私もそれが均等に削除することを希望エンドから削除したい一方で、私はstkentが提供する現在のコードを考え、タイタンがでポイントを削除します(ポイントが不均一に広がっているので)、一貫したペースでラインしかし、実際にライン自体を意味するものではありません一貫したペースで削除されます。多数の編集を通して私にこだわっためのみんなに

多くのおかげで私は解決策は、ラインが一貫したペースで削除することを可能にすることを見つけることができます願っています今まで。

+0

パスを描くことで、あらゆる点の間。しかし、私はまだ一定のペースでラインを削除するための解決策はありません。違うパスは長さが違っていて削除のペースが不均一になるからです。 – Roymunson

+0

描画されたパスに沿って補間して、新しいエンドポイントを見つけることはできますか?それは、少なくとも部分的な削除の錯覚を許すでしょう。アーク長でパスを簡単にパラメータ化できるかどうかは覚えていない。 – stkent

+0

@stkent私は実際に描画されたパスに沿って補間するために熱い知らない。おそらくあなたは答えとしてコードを投稿することができますか? – Roymunson

答えて

2

最新のコードを元にして、まず最初に試してみましょう。ない場合にのみ、(任意の時点で1つのライン/パスの描画されます

  • 、あなたが反復することにより、各パスについて以下に概説された手順を実行する必要があります:私はこの答えに、以下の仮定を作ってるんです

)パスのいくつかのコレクションのタイムスタンプを追加しPointクラスのラッパーを作成します。

private static class TimeStampedPoint extends Point { 

    private final long timeStamp; 

    private TimeStampedPoint(final int x, final int y, final long timeStamp) { 
     super(x, y); 
     this.timeStamp = timeStamp; 
    } 
} 

その後、ヨーヨーを更新あなたのポイントストレージを次のように変更してください:

List<TimeStampedPoint> linePoints = new ArrayList<>(); 

(この結果、コードを変更する必要があります。具体的には、あなたではなく、明示的にarrayIndexを追跡するよりも、このリストの最後に新しいポイントを追加するList方法addを使用することができます)

をごonTouchEvent方法では、コードブロックを置き換えます。

for(int i = 0; i < event.getHistorySize(); i++) { 
    linePoints[arrayIndex] = new Point((int) event.getHistoricalX(i), (int) event.getHistoricalY(i)); 
    linePath.lineTo(linePoints[arrayIndex].x,linePoints[arrayIndex].y); 
    if(arrayIndex >= 1) { 
     checkForIntersections(linePoints[arrayIndex - 1], linePoints[arrayIndex]); 
    } 
    arrayIndex++; 
} 
をこのようなもので

for(int i = 0; i < event.getHistorySize(); i++) { 
    TimeStampedPoint point = new TimeStampedPoint((int) event.getHistoricalX(i), (int) event.getHistoricalY(i), event.getHistoricalEventTime(i)); 

    linePoints.add(point); 
    linePath.lineTo(point.x, point.y); 

    int numberOfPoints = linePoints.size(); 

    if(numberOfPoints >= 2) { 
     checkForIntersections(linePoints.get(numberOfPoints - 2), linePoints.get(numberOfPoints - 1)); 
    } 
} 

あなたはlinePoints配列に値を追加する他のどこでも同様の調整を行います。このループでは、もはやPathが徐々に作成されていないことに注意してください。これは、Pathを構築する前に、いくつかのサニタイズ(期限切れのポイントの削除)を実行するためです。これを行うには、描画を準備するたびにlinePathをクリアします(パフォーマンスが悪い場合はこのメソッドを別の場所に移動することができますが、推奨ライフサイクルをクリアするにはonDrawにあることをお勧めします)。 validPointsはタイプList<TimeStampedPoint> Sの別のフィールドである

@Override 
public void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    // Reset the Path. 
    linePath.rewind(); 

    validPoints = removeExpiredPoints(); 
    updatePathUsingPoints(validPoints); 
    canvas.drawPath(linePath, black); 

    linePoints = validPoints; 

    invalidate(); //I don't know if this is the best way to refresh the screen 
} 

:あなたのonDraw方法は、次のようになります。 [一般にonDrawの中からinvalidateを呼び出すのはおそらく最善の方法ではありませんが、それはこの質問の範囲外です。]

2つの新しいメソッドは、ここで紹介されています:

private List<TimeStampedPoint> removeExpiredPoints() { 
    final List<TimeStampedPoint> result = new ArrayList<>(); 

    for (final TimeStampedPoint point: linePoints) { 
     if (System.uptimeMillis() - point.getTimeStamp <= 10000) { 
      // We only include points in the result if they are not expired. 
      result.add(point); 
     } 
    } 

    return result; 
} 

private void updatePathUsingPoints(final List<TimeStampedPoint> validPoints) { 
    if (validPoints.size() < 2) { 
     return linePath; // Return the empty path here; nothing to draw. 
    } 

    linePath.moveTo(validPoints.get(0)); 

    for (int i = 1; i < validPoints.size(); i++) { 
     final Point targetPoint = validPoints.get(i); 
     linePath.lineTo(targetPoint.x, targetPoint.y); 
    } 
} 

うまくいけば、これはあなたに始めるためのフレームワークを十分に与えます。線が消えているのが目立たない場合は、私は助けることができるアイデアを持っていますが、それはより多くの入力ですので、早めに最適化してみましょう:)

+0

"Valid Points"が別のarrayListであることを確認したいだけです。私は正しいでしょうか? – Roymunson

+0

はい、 'TimeStampedPoint'sの別の' ArrayList' :)この情報を含むように答えを編集しました。 – stkent

+0

また、updatePathUsingPoints()は何も返さないはずです。 'linePath = updatePathUsingPoints(validPoints);'行のために、メソッドは何かを返さなければならず、それは正しく見えませんでした。 – Roymunson

3

あなたは常に10000ポイントを保存する必要はないかもしれないように私は、ArrayListの代わりに静的な配列を使用することをお勧め。また、Pointのサブクラスを作成し、インスタンス化時にタイムスタンプを格納することをお勧めします。検討:

public class TimedPoint extends Point { 
    private static final int KEEP_ALIVE_TIME_MS = 200; //tweak this value to your needs 
    private final long time; 

    public TimedPoint(int x, int y) { 
     super(x, y); 
     time = System.currentTimeMillis(); 
    } 

    public TimedPoint(int x, int y, long time) { 
     super(x, y); 
     this.time = time; 
    } 

    public boolean hasExpired(long time) { 
     return (time-this.time>KEEP_ALIVE_TIME_MS); 
    } 
} 

public class GameView extends View ... { 
    ArrayList<TimedPoint> linePoints = new ArrayList<>(); //Lists can grow and shrink to demand 
    //this implementation is backed by an array. 
    ... 

    public void addPoint(int x, int y) { 
     linePoints.add(new TimedPoint(x, y); 
    } 

    public void removeOldPoints() { 
     long time = System.currentTimeMillis(); 
     Iterator<TimedPoint> i = linePoints.iterator(); 
     while(i.hasNext()) { 
      TimedPoint point = i.next(); 
      if(point.hasExpired(time)) 
       i.remove(); 
     } 
    } 
} 

removeOldPoints()は、その時間差TimedPointで定義された閾値よりも大きいlinePointsから任意の点を削除します。これは、removeOldPoints()を定期的に呼び出すことができることを前提としています。ヒントは、onDraw()で電話するのがいいでしょう。

onDrawremoveOldPoints()が呼び出されてから線が描画される場合は、linePointsに保持されている点を描画する必要があります。その時点では、リストを反復して点を線で描くのと同じくらい簡単です。描画すると「尾」が消え始めます。


またTimedPointからlinePointsを渡し、将来の特定の時間に自分自身を削除するには建設時にTimer、およびschedule()TimedPointを設定することができます。これはremoveOldPoints()を定期的に呼び出すことはできません。次の点を考慮してください。

このアプローチでも調整できることがいくつかあります。例えば、ポイントは "生まれ"とすぐに "死ぬ"ように始まります。より適切であれば、リストに追加されたときにのみ変更することができます。

また、最適化の余地があるかもしれません。これは、ポイントごとに新しいスレッドを生成する可能性があるためです。これにより実際にはパフォーマンスが向上するはずです(removeOldPoints()がボトルネックの場合).CPUがコンテキストスイッチによって無効にされるまで、あなたがペダンシーを感じている、またはパフォーマンスが問題になった場合、スレッドプールとキューを使用することができます。

Hereは、新しいクラスに順応するためのArrayListのドキュメントです。

あなたがまだ問題を抱えているようだ:)


EDITコーディングハッピー。これを試して、それがあなたのために何をしているか教えてください。

public class GameView ... { 
    ArrayList<TimedPoint> linePoints = new ArrayList<>(); //Lists can grow and shrink to demand 
    //this implementation is backed by an array. 
    ... 

    @Override 
    public void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 
     removeOldPoints(); 
     Path path = linePointsToPath(); //I'm not sure if you need to store path, let's generate it. 
     if(path != null) 
      canvas.drawPath(path, black); 
    } 

    public void addPoint(int x, int y) { 
     linePoints.add(new TimedPoint(x, y); 
     invalidate(); 
    } 

    public void removeOldPoints() { 
     int oldLen = linePoints.size(); 
     long time = System.currentTimeMillis(); 
     Iterator<TimedPoint> i = linePoints.iterator(); 
     while(i.hasNext()) { 
      TimedPoint point = i.next(); 
      if(point.hasExpired(time)) 
       i.remove(); 
     } 
     int newLen = linePoints.size(); 
     if(newLen != oldLen) //if we removed items from list 
      invalidate(); 
    } 

    //small tweaks to stKents method 
    private Path linePointsToPath() { 
     if(linePoints.size() < 2) 
      return null; 
     Path path = new Path(); 
     Point p = points.get(0); 
     Path.moveTo(p.x, p.y); 
     for(Point point : linePoints) { 
      if(p != point) 
       path.lineTo(point.x, point.y); //skip first point, because of moveTo 
     } 
     return path; 
    } 

    @Override 
    public boolean onTouch(View v, MotionEvent event) { 
     ... 
     addPoint(...); 
    } 
} 
+0

これは私の問題の本当に素晴らしい解決策です。あなたのコードで見られる1つの問題は、 'System.currenttimemillis()'がTimeStampedPointクラスで間違った時刻を与えることです。その時のonTouchメソッドによって)。 – Roymunson

+0

私はそれを考えなかった。私は 'TimedPoint'に別のコンストラクタを追加しました。あなたは歴史的時代のために自分の時間を設定することができます。 'Point'の必要な部分を' TimedPoint'で置き換え、コンストラクタの最後の引数として時間を渡すか、自動的な "now"タイムスタンプのためにそれを省略します。私は 'addPoint()'のようにします。 – MeetTitan

+0

'onTouch'で無効化を呼び出すことも私の最初の考えでしたが、ユーザーが指を離したときに行が削除されるのを止めると思います:/ – stkent

関連する問題