我有一个应用程序,绘制一个点网格(让我们说5x5)。要求用户在该网格上绘制线条。如果用户的手指触摸网格中的一个点,则该点被着色以显示该点是所绘制的路径的一部分。此外,将在每两个触摸点之间绘制一条线。
问题 - 我的表现非常差,导致很少的事情:
event.getAction()
中的动作事件的粒度不佳。我的意思是enter code here
例如,它不是每10个像素注册一个运动,而是每100个像素记录一次运动。反过来,这将导致应用程序不重绘用户触摸的一些点。看一下应用程序"如何错过"在用户触摸的点上并且没有绘制绿点:
我尝试了一些事情:
else if(event.getAction() == MotionEvent.ACTION_MOVE)
内onTouchEvent(MotionEvent event)
,我读到这可能会让CPU有时间赶上所有触摸事件 - 没有改变一件事this.destroyDrawingCache()
添加到doDraw()
的最后(我使用它代替onDraw,正如我使用的一个教程所建议的那样)。我认为这将清除所有事件/绘图缓存,这似乎正在减慢系统速度 - 没有改变一件事。我对Android动画相当新,所以我不知道如何继续:
doDraw()
(我的onDraw())和onTouchEvent()
中尽可能少地做。invalidate()
的内容,但不知道如何以及何时使用它。如果我理解正确,每次调用doDraw()
时我的视图都会被重新绘制。例如,我的网格是静态的 - 我怎么能避免重绘它呢?++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
我尝试使用canvas.drawCircle(xPos, yPos, 8, mNodePaint);
代替canvas.drawBitmap(mBitmap, xPos, yPos, null);
。我认为,如果我使用实际的位图,这可能会提高性能。事实上 - 它没有!我有点困惑,这样一个简单的应用程序如何在设备上造成如此沉重的负担。我一定是做错了事。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
我考虑了@LadyWoodi建议的内容 - 我已经从循环中删除了所有变量声明 - 无论如何这是一个不好的做法,我也摆脱了所有的" System.Out"我使用的行,所以我可以记录应用程序行为,以更好地理解为什么我得到这样一个蹩脚的性能我很伤心地说,如果有性能上的变化(我不'吨实际测量,帧速率变更),它是可以忽略不计。
还有其他想法吗?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
- 绘制一次网格。
使用Bitmap.createBitmap()
将图形作为位图。
- 使用canvas.drawBitmap()
绘制静态点网格的位图。
- 当我的线程运行时,我检查它是否绘制了网格点。如果它正在运行,我将不会重新创建静态点网格。我只会从之前渲染的位图渲染它。
令人惊讶的是,这对我的表现没有任何改变!每次重绘点网格都不会对应用程序性能产生真正的视觉效果。
我决定在绘图线程中使用canvas = mHolder.lockCanvas(new Rect(50, 50, 150, 150));
。只是为了测试目的,看看我是否限制每次渲染的区域,我可以更好地获得性能。这也没有帮助。
然后我转向Eclipse中的DDMS工具尝试分析应用程序。它的结果是,canvas.drawPath(path, mPathPaint);
(Canvas.native_drawPath)消耗了大约88.5%的CPU时间!!!
但为什么??!我的路径绘图相当简单,mGraphics包含一系列路径,我所做的就是弄清楚每条路径是否在游戏画面的边界内,然后我画一条路径:
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
在我的示例中,任何人都可以看到任何编程错误的代码行吗?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
我已对doDraw()
方法进行了更改。基本上我所做的只是在某些事情发生变化的情况下画画。在所有其他情况下,我只是存储屏幕的缓存位图并进行渲染。请看一下:
public void doDraw(Canvas canvas)
{
synchronized (mViewThread.getSurefaceHolder())
{
if(mGraphics.size() > mPathsCount)
{
mPathsCount = mGraphics.size();
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
//nodes that the path goes through, are repainted green
//these nodes are building the drawn pattern
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (PathPoint nodeHit : nodePattern)
{
canvas.drawBitmap(mDotOK, nodeHit.x - ((mDotOK.getWidth()/2) - (mNodeBitmap.getWidth()/2)), nodeHit.y - ((mDotOK.getHeight()/2) - (mNodeBitmap.getHeight()/2)), null);
}
}
mGameField = Bitmap.createBitmap(mGridNodesCount * mNodeGap, mGridNodesCount * mNodeGap, Bitmap.Config.ARGB_8888);
}
else
{
canvas.drawBitmap(mGameField, 0f, 0f, null);
}
现在结果 - 只要设备不必渲染任何路径并且只是从位图绘制,东西就会非常快。但是,当我不得不使用canvas.drawPath()
性能重新渲染屏幕的那一刻变得像在吗啡上的乌龟一样缓慢......我拥有的路径越多(最多6个或更多,这没有!)渲染速度越慢。这有多奇怪? - 我的路径甚至不是很弯曲 - 都是偶然转弯的直线。我的意思是这条线不是很复杂&#34;。
我在下面添加了更多代码 - 如果您有任何改进建议。
非常感谢, D.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~&#34; Panel&#34; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
Bitmap mNodeBitmap;
int mNodeBitmapWidthCenter;
int mNodeBitmapHeightCenter;
Bitmap mDotOK;
ViewThread mViewThread;
ArrayList<PathPoint> mPathPoints;
private ArrayList<Path> mGraphics = new ArrayList<Path>(3);
private ArrayList<ArrayList<PathPoint>> mNodesHitPatterns = new ArrayList<ArrayList<PathPoint>>();
private Paint mPathPaint;
Path mPath = new Path();
//private ArrayList<Point> mNodeCoordinates = new ArrayList<Point>();
private int mGridNodesCount = 5;
private int mNodeGap = 100;
PathPoint mNodeCoordinates[][] = new PathPoint[mGridNodesCount][mGridNodesCount];
PathMeasure mPm;
float mStartCoordinates[] = {0f, 0f};
float mEndCoordinates[] = {0f, 0f};
PathPoint mPathPoint;
Boolean mNodesGridDrawn = false;
Bitmap mGameField = null;
public Boolean getNodesGridDrawn() {
return mNodesGridDrawn;
}
public Panel(Context context) {
super(context);
mNodeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot);
mNodeBitmapWidthCenter = mNodeBitmap.getWidth()/2;
mNodeBitmapHeightCenter = mNodeBitmap.getHeight()/2;
mDotOK = BitmapFactory.decodeResource(getResources(), R.drawable.dot_ok);
getHolder().addCallback(this);
mViewThread = new ViewThread(this);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true); //for better color
mPathPaint.setColor(0xFFFFFF00);
mPathPaint.setStyle(Paint.Style.STROKE);
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeCap(Paint.Cap.ROUND);
mPathPaint.setStrokeWidth(5);
}
public ArrayList<ArrayList<PathPoint>> getNodesHitPatterns()
{
return this.mNodesHitPatterns;
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
//setPadding(100, 100, 0, 0);
if (!mViewThread.isAlive()) {
mViewThread = new ViewThread(this);
mViewThread.setRunning(true);
mViewThread.start();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mViewThread.isAlive()) {
mViewThread.setRunning(false);
}
}
//draw the basic nodes grid that the user will use to draw the lines on
//store as bitmap
public void drawNodesGrid(Canvas canvas)
{
canvas.drawColor(Color.WHITE);
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
int xPos = j * mNodeGap;
int yPos = i * mNodeGap;
try
{
//TODO - changed
mNodeCoordinates[i][j] = new PathPoint(xPos, yPos, null);
}
catch (Exception e)
{
e.printStackTrace();
}
canvas.drawBitmap(mNodeBitmap, xPos, yPos, null);
}
}
mNodesGridDrawn = true;
mGameField = Bitmap.createBitmap(mGridNodesCount * mNodeGap, mGridNodesCount * mNodeGap, Bitmap.Config.ARGB_8888);
}
public void doDraw(Canvas canvas)
{
canvas.drawBitmap(mGameField, 0f, 0f, null);
synchronized (mViewThread.getSurefaceHolder())
{
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
//nodes that the path goes through, are repainted green
//these nodes are building the drawn pattern
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (PathPoint nodeHit : nodePattern)
{
canvas.drawBitmap(mDotOK, nodeHit.x - ((mDotOK.getWidth()/2) - (mNodeBitmap.getWidth()/2)), nodeHit.y - ((mDotOK.getHeight()/2) - (mNodeBitmap.getHeight()/2)), null);
}
}
this.destroyDrawingCache();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (mViewThread.getSurefaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
//System.out.println("Action downE x: " + event.getX() + " y: " + event.getY());
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
//TODO - changed
//PathPoint pathPoint = mNodeCoordinates[i][j];
mPathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - mPathPoint.x) <= 35) && (Math.abs((int)event.getY() - mPathPoint.y) <= 35))
{
//mPath.moveTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//System.out.println("Action down x: " + pathPoint.x + " y: " + pathPoint.y);
ArrayList<PathPoint> newNodesPattern = new ArrayList<PathPoint>();
mNodesHitPatterns.add(newNodesPattern);
//mNodesHitPatterns.add(nh);
//pathPoint.setAction("down");
break;
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_MOVE)
{
final int historySize = event.getHistorySize();
//System.out.println("historySize: " + historySize);
//System.out.println("Action moveE x: " + event.getX() + " y: " + event.getY());
coordinateFound:
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
//TODO - changed
//PathPoint pathPoint = mNodeCoordinates[i][j];
mPathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - mPathPoint.x) <= 35) && (Math.abs((int)event.getY() - mPathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!mPathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
else //no current match => try historical
{
if(historySize > 0)
{
for (int k = 0; k < historySize; k++)
{
//System.out.println("Action moveH x: " + event.getHistoricalX(k) + " y: " + event.getHistoricalY(k));
if((Math.abs((int)event.getHistoricalX(k) - mPathPoint.x) <= 35) && (Math.abs((int)event.getHistoricalY(k) - mPathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!mPathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
}
}
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_UP)
{
// for (int i = 0; i < mGridSize; i++) {
//
// for (int j = 0; j < mGridSize; j++) {
//
// PathPoint pathPoint = mNodeCoordinates[i][j];
//
// if((Math.abs((int)event.getX() - pathPoint.x) <= 35) && (Math.abs((int)event.getY() - pathPoint.y) <= 35))
// {
// //the location of the node
// //mPath.lineTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//
// //System.out.println("Action up x: " + pathPoint.x + " y: " + pathPoint.y);
//
// //mGraphics.add(mPath);
// // mNodesHit.add(pathPoint);
// // pathPoint.setAction("up");
// break;
// }
// }
// }
}
//System.out.println(mNodesHitPatterns.toString());
//create mPath
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (int i = 0; i < nodePattern.size(); i++)
{
if(i == 0) //first node in pattern
{
mPath.moveTo(nodePattern.get(i).x + mNodeBitmapWidthCenter, nodePattern.get(i).y + mNodeBitmapHeightCenter);
}
else
{
mPath.lineTo(nodePattern.get(i).x + mNodeBitmapWidthCenter, nodePattern.get(i).y + mNodeBitmapWidthCenter);
}
//mGraphics.add(mPath);
}
}
mGraphics.add(mPath);
return true;
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Class&#34; ViewThread&#34; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class ViewThread extends Thread {
private Panel mPanel;
private SurfaceHolder mHolder;
private boolean mRun = false;
public ViewThread(Panel panel) {
mPanel = panel;
mHolder = mPanel.getHolder();
}
public void setRunning(boolean run) {
mRun = run;
}
public SurfaceHolder getSurefaceHolder()
{
return mHolder;
}
@Override
public void run()
{
Canvas canvas = null;
while (mRun)
{
canvas = mHolder.lockCanvas();
//canvas = mHolder.lockCanvas(new Rect(50, 50, 150, 150));
if (canvas != null)
{
if(!mPanel.getNodesGridDrawn())
{
mPanel.drawNodesGrid(canvas);
}
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
答案 0 :(得分:1)
这只是想法,但我会尝试从循环中取出所有声明。我知道将它们本地化是有用的,但它通常非常耗时,所以它可以帮助一点。我的第二个想法已经在您的更新中经过测试,所以现在我也很好奇它将如何发展;)
答案 1 :(得分:0)
您使用的是SurfaceView吗?首先,我建议你为你的游戏使用一个图形库...例如,AndEngine非常易于使用,你可以实现比使用Java画布开发更漂亮的游戏。表现也更好。
我发现你的代码没有任何问题,但是在draw方法中有很多处理,更重要的是,在onTouch事件中。你应该避免在循环中使用除法或重数学运算,并尝试在之前预先计算所有内容。
但我坚持;对于你正在做的事情,看一下this,你就可以立刻开始运行它!