我正在尝试用图表制作程序并在屏幕上打印图表。我使用GraphPainter(一个SurfaceView类型,实现Runnable)类将图形绘制到屏幕上。在无限循环的绘图函数中,我正在更新图形的元素:节点列表和Graph类中的边缘列表。节点列表完全从图形更新(当我删除,添加或选择节点时),但如果我从实际图形中删除边缘,则画笔中的边缘列表不会更新。
我调试了它,当删除边时,Graph类中的graphEdgeList会正确更新。它的尺寸越来越小。任何人都可以告诉我为什么GraphPainter的列表没有相应更新?
GraphDrawer.java:
package com.rares.graphit;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
public class GraphDrawer extends Activity implements OnTouchListener{
/**Suface Manipulator*/
GraphPainter gp;
/**Graph object*/
public static Graph graph = null;
/**Touch coordinates*/
public static float touchX = 0;
public static float touchY = 0;
/**Drag state*/
public boolean isDragging = false;
/**Drag coordinates*/
public static float dragX = 0;
public static float dragY = 0;
/**Id of the node that is currently selected
* Is -1 if no node is selected during a touch event*/
public static int currentId = -1;
/**Id of a node that was selected before and an action between 2 nodes or on a node
* that needs to be deleted or moved*/
public static int firstId = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*Set window to fullscreen**/
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
gp = new GraphPainter(this);
gp.setOnTouchListener(this);
graph = new Graph();
setContentView(gp);
}
@Override
public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
touchX = event.getX();
touchY = event.getY();
currentId = graph.pointIntersectsNode(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
dragX = event.getX();
dragY = event.getY();
/**Function will return -1 if there is no intersection or node ID if drag start intersects the node*/
/**Selected node will be red on the screen*/
if ((dragX - touchX)*(dragX - touchX) + (dragY - touchY)*(dragY - touchY) > Graph.Circle.RADIUS_DEFAULT){
if(currentId != -1){
graph.setNodePosition(currentId, dragX, dragY);
}
isDragging = true;
}
break;
case MotionEvent.ACTION_UP:
if(!isDragging){
if(currentId == -1)
graph.addNode(touchX, touchY);
else{
if(!graph.getGraphNodeList().get(currentId).isSelected){
graph.markNodeAsSelected(currentId);
if(firstId == -1)
firstId = currentId;
else{
if(graph.isEdge(Math.min(firstId, currentId), Math.max(firstId, currentId))){
graph.deleteEdge(Math.min(firstId,currentId),Math.max(firstId, currentId));
}
graph.addEdge(Math.min(firstId,currentId),Math.max(firstId, currentId));
graph.markNodeAsDeselected(currentId);
graph.markNodeAsDeselected(firstId);
firstId = -1;
}
}
else{
Log.d("IDS", "ID1: " + Integer.toString(currentId) + "\nSelected: " + Integer.toString(firstId));
/*graph.markNodeAsDeselected(id1);*/
if(firstId == currentId){
graph.deleteNode(currentId);
}
currentId = -1;
firstId = -1;
}
}
}
/*else
if(id != -1){
graph.markNodeAsDeselected(id);
id = -1;
}*/
//Reset values
isDragging = false;
touchX = touchY = dragX = dragY = 0;
break;
}
return true;
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
gp.resume();
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
gp.pause();
}
}
GraphPainter.java:
package com.rares.graphit;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GraphPainter extends SurfaceView implements Runnable {
/**ArrayLists for drawing the elements of the graph*/
ArrayList<Graph.Node> listNode;
ArrayList<Graph.Edge> listEdge;
/**Holder of surface*/
SurfaceHolder holder;
/**Drawing thread*/
Thread drawThread;
/**Canvas for drawing*/
Canvas canvas = null;
/**Bool for running state*/
boolean isRunning = false;
public GraphPainter(Context context){
super(context);
holder = getHolder();
drawThread = new Thread(this);
isRunning = true;
drawThread.start();
}
@Override
public void run(){
while (isRunning) {
if (holder.getSurface().isValid()) {
canvas = holder.lockCanvas();
/**Draw background*/
canvas.drawRGB(255, 255, 255);
/**Draw Graph*/
drawGraph();
/**Print to screen*/
holder.unlockCanvasAndPost(canvas);
}
}
}
public void drawGraph(){
/**Draw Egdes*/
listEdge = GraphDrawer.graph.getGraphEdgeList();
Log.d("PAINT_EDGES", "Size of listEdge: " + listEdge.size());
for(int i = 0; i < listEdge.size(); ++i){
float startX = listNode.get(listEdge.get(i).id1).drawingCircle.x;
float startY = listNode.get(listEdge.get(i).id1).drawingCircle.y;
float stopX = listNode.get(listEdge.get(i).id2).drawingCircle.x;
float stopY = listNode.get(listEdge.get(i).id2).drawingCircle.y;
Paint linePaint = new Paint();
linePaint.setColor(Graph.NODE_COLOR_UNSELECTED);
canvas.drawLine(startX, startY, stopX, stopY, linePaint);
}
/**Draw Nodes*/
listNode = GraphDrawer.graph.getGraphNodeList();
for(int i = 0; i < listNode.size(); ++i){
canvas.drawCircle(listNode.get(i).drawingCircle.x, listNode.get(i).drawingCircle.y, listNode.get(i).drawingCircle.R, listNode.get(i).drawingCircle.circlePaint);
}
/*clear the arraylists**/
/*listNode.clear();
listEdge.clear();*/
}
/**Will be called from GraphDrawer when onPause() will occur*/
public void pause(){
isRunning = false;
try {
drawThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
drawThread = null;
}
public void resume(){
drawThread = new Thread(this);
isRunning = true;
drawThread.start();
}
}
Graph.java:
package com.rares.graphit;
import java.util.ArrayList;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
/**Graph structure and manipulator. Momentarily, it will be an unoriented graph*/
public class Graph {
/**Circle for drawing purposes*/
public static class Circle{
/**Circle Radius setting*/
public static final float RADIUS_DEFAULT = 30;
/**Circle coordinates*/
float x, y;
float R;
/**Circle style*/
Paint circlePaint = null;
/*Circle default color*/
public int DEFAULT_COLOR = Color.BLUE;
public int drawColor;
/**Creates a circle with the Point as center, r as radius, and the default color
* of a node*/
public Circle(float x, float y, float r){
this.x = x;
this.y = y;
this.R = r;
this.circlePaint = new Paint();
this.circlePaint.setColor(DEFAULT_COLOR);
drawColor = DEFAULT_COLOR;
}
}
/**Graph node structure*/
public static class Node{
/**For drawing purposes*/
Circle drawingCircle;
/**Node ID in the graph*/
int ID;
/**Flag that shows wether the node is selected*/
public boolean isSelected = false;
public Node(float x, float y){
this.ID = numberOfNodes;
drawingCircle = new Circle(x, y, Circle.RADIUS_DEFAULT);
}
public void setColor(int color){
this.drawingCircle.circlePaint.setColor(color);
}
}
/**Graph edge structure*/
public static class Edge{
/**Nodes between whom the edge will be drawn
* id1 - the lower number
* id2 - the higher number
* */
public int id1, id2;
/**Constructor that will set the nodes of the edge*/
public Edge(int a, int b){
if(a > b){
id1 = b; id2 = a;
}
else{
id1 = a; id2 = b;
}
}
}
/**List of vicinity of the graph*/
public static ArrayList<Node> graphNodeList;
/**Current number of nodes*/
public static int numberOfNodes = 0;
/**List of the edges of the graph*/
public static ArrayList<Edge> graphEdgeList;
/**Node default colors*/
public static int NODE_COLOR_UNSELECTED = Color.BLUE;
public static int NODE_COLOR_SELECTED = Color.RED;
/**All elements will be added through addNode() or addEdge() methods*/
public Graph(){
graphNodeList = new ArrayList<Graph.Node>(0);
graphEdgeList = new ArrayList<Graph.Edge>(0);
}
/**List of vicinity getter*/
public ArrayList<Node> getGraphNodeList(){
return graphNodeList;
}
/**List of Edges getter*/
public ArrayList<Edge> getGraphEdgeList(){
return graphEdgeList;
}
/**Adds a node into the graph, with the canvas coordinates: x and y*/
public void addNode(float x, float y){
Node newNode = new Node(x, y);
newNode.ID = graphNodeList.size();
graphNodeList.add(newNode);
newNode = null;
}
/**Deletes a node from the graph */
void deleteNode(int id){
graphNodeList.remove(id);
deleteEdges(id);
for(int i = id; i < graphNodeList.size(); ++i){
int aux = graphNodeList.get(i).ID;
if(aux != i)
resetEdgeNodes(aux, i);
graphNodeList.get(i).ID = i;
}
}
/**Verifies if a point described by its coordinates intersects one if the nodes on
* the screen.
* Returns the ID of the node if true
* -1 otherwise*/
public int pointIntersectsNode(float X, float Y){
for(int i = 0; i < graphNodeList.size(); i++){
float centerX = graphNodeList.get(i).drawingCircle.x;
float centerY = graphNodeList.get(i).drawingCircle.y;
float circleRadius = graphNodeList.get(i).drawingCircle.R;
if((centerX-X) * (centerX-X) + (centerY-Y)*(centerY-Y) < circleRadius*circleRadius)
return i;
}
return -1;
}
/**Marks node as selected and will paint it the COLOR_SELECTED color*/
public void markNodeAsSelected(int id){
graphNodeList.get(id).setColor(NODE_COLOR_SELECTED);
graphNodeList.get(id).isSelected = true;
}
/**Marks the node back as deselected, with its default color*/
public void markNodeAsDeselected(int id) {
graphNodeList.get(id).drawingCircle.circlePaint.setColor(NODE_COLOR_UNSELECTED);
graphNodeList.get(id).isSelected = false;
}
/**Sets the position of the node with the id ID at the (X,Y) point*/
public void setNodePosition(int id, float X, float Y) {
graphNodeList.get(id).drawingCircle.x = X;
graphNodeList.get(id).drawingCircle.y = Y;
}
/**Adds an edge between two nodes*/
public void addEdge(int id1, int id2){
Edge edge = new Edge(id1, id2);
graphEdgeList.add(edge);
}
/**Verifies if an edge between nodes id1 and id2 exists*/
public boolean isEdge(int id1, int id2){
for(int i = 0; i < graphEdgeList.size(); ++i){
if(id1 == graphEdgeList.get(i).id1 && id2 == graphEdgeList.get(i).id2)
return true;
}
return false;
}
/**Deletes an edge from the list, by the nodes IDs
* id1 is the node with the minimum id
* id2 is the node with the maximum id
* */
public void deleteEdge(int id1, int id2){
Log.d("EDGE_DELETE", "Size before a single edge deletion: " + Integer.toString(graphEdgeList.size()));
for(int i = 0; i < graphEdgeList.size(); ++i)
if(id1 == graphEdgeList.get(i).id1 && id2 == graphEdgeList.get(i).id2){
Log.d("EDGE", "EDGE DELETED" + Integer.toString(id1) + " " + Integer.toString(id2));
graphEdgeList.remove(i);
Log.d("EDGE_DELETE", "Size after a single edge deletion: " + Integer.toString(graphEdgeList.size()));
return;
}
}
/**Deletes the edges which has one of the nodes with its ID set to id*/
public void deleteEdges(int id){
for(int i = 0; i < graphEdgeList.size(); ++i){
if(id == graphEdgeList.get(i).id1 || id == graphEdgeList.get(i).id2){
graphEdgeList.remove(i);
i--;
}
}
}
/**Resets the List of Edges when a node is deleted*/
public void resetEdgeNodes(int oldId, int newId){
for(int i = 0; i < graphEdgeList.size(); ++i){
int nodeId1 = graphEdgeList.get(i).id1;
int nodeId2 = graphEdgeList.get(i).id2;
if(nodeId1 == oldId){
graphEdgeList.get(i).id1 = newId;
}
else
if(nodeId2 == oldId){
graphEdgeList.get(i).id2 = newId;
}
/*If the new nodes are nod in order, swap them**/
if(graphEdgeList.get(i).id1 > graphEdgeList.get(i).id2){
int aux = graphEdgeList.get(i).id1;
graphEdgeList.get(i).id1 = graphEdgeList.get(i).id2;
graphEdgeList.get(i).id2 = aux;
}
}
}
}
答案 0 :(得分:0)
您遇到的主要问题是多线程编程中的常见问题。在java中,您需要解决的两个问题是从一个线程访问图形对象同时从另一个线程修改图形对象的安全性。
另一个问题是两个线程可能有不同的图形对象视图,必须采取措施让运行时/编译器知道在某些代码边界之后,必须同步图形对象的视图。 / p>
这可能有助于研究同步关键字和内存同步。当然,可能存在其他解决方案/优化,最好的方法是了解所提到的多线程问题,并首先采用最直接的解决方案......如果可能,稍后处理性能/优化。