Страница 1 из 1

Проблема с рисованием динамического графика

Добавлено: 12 дек 2013, 14:21
heartbeatMe
Столкнулся с проблемой. Необходимо выводить на экран смартфона динамический график. Использую для этой цели Canvas и SurfaceView. Собственно проблема в том, что через 0.5 - 1 сек SurfaceView перестает рисовать поступившие данные.

WaveformPlotThread.java:

Код: Выделить всё

import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class WaveformPlotThread extends Thread {
	private SurfaceHolder holder;
	private WaveformView plot_area;
	private boolean _run = false;
	
	public WaveformPlotThread(SurfaceHolder surfaceHolder, WaveformView view){
		holder = surfaceHolder;
		plot_area = view;
	}
	public void setRunning(boolean run){
		_run = run;
	}
	@Override
	public void run(){
		Canvas c;
		while(_run){
			c = null;
			try{
				c = holder.lockCanvas(null);
				synchronized (holder) {
					plot_area.PlotPoints(c);
				}
			}finally{
				if(c!=null){
					holder.unlockCanvasAndPost(c);
				}
			}
		}
	}
}
WaveformView.java:

Код: Выделить всё

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class WaveformView extends SurfaceView implements SurfaceHolder.Callback {
	
	// Отладка
        private static final String TAG = "WaveformView";
        private static final boolean D = true;
    
	private WaveformPlotThread plot_thread;
	private final int width = 320;
	private final int heigth = 240;
	private final double Km = height/66000; // получаем Коэффициент масштаба
	
	private static int[] ch1_data = new int[width];
	private static int ch1_pos = 120;
	private Paint ch1_color = new Paint();
	private Paint grid_paint = new Paint();
	private Paint cross_paint = new Paint();
	private Paint outline_paint = new Paint();
	
	public WaveformView(Context context, AttributeSet attrs) {  
		super(context, attrs);  
		getHolder().addCallback(this);
		
		int i;
		for(i=0; i<width; i++){
			ch1_data[i] = ch1_pos;
		}
		
		plot_thread = new WaveformPlotThread(getHolder(), this);
		ch1_color.setColor(Color.rgb(66, 170, 255));
		ch1_color.setStrokeWidth(5);
		grid_paint.setColor(Color.rgb(100, 100, 100));
		cross_paint.setColor(Color.rgb(70, 100, 70));
		outline_paint.setColor(Color.GREEN);
	}
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,	int height) {
		// TODO Auto-generated method stub
	}
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		plot_thread.setRunning(true);
		plot_thread.start();
	}
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		boolean retry = true;
		plot_thread.setRunning(false);
		while (retry){
			try{
				plot_thread.join();
				retry = false;
			}catch(InterruptedException e){
				
			}
		}
	}
	
	@Override
	public void onDraw(Canvas canvas){
		PlotPoints(canvas);
	}
	
	public void set_data(int[] data){
		int x;
		if(D) Log.i(TAG, "Принимаем данные и обрабатываем");
		plot_thread.setRunning(false);
		x = 0;
		while(x<width){
			if(x<(data.length)){
				if (data[x]>=0) {
					ch1_data[x] = (int) (heigth/2 - data[x]*Km);
				} else {
					ch1_data[x] = (int) (heigth/2 + Math.abs(data[x])*Km);
				}
			} else{
				ch1_data[x] = ch1_pos;
			}
			x++;
		}
		plot_thread.setRunning(true);
	}
	
	public void PlotPoints(Canvas canvas){
		if(D) Log.i(TAG, "Обновляем экран");
		// clear screen
		canvas.drawColor(Color.rgb(255, 255, 255));
		// draw grids
	    for(int vertical = 1; vertical<10; vertical++){
	    	canvas.drawLine(
	    			vertical*(width/10)+1, 1,
	    			vertical*(width/10)+1, heigth+1,
	    			grid_paint);
	    }
	    for(int horizontal = 1; horizontal<10; horizontal++){
	    	canvas.drawLine(
	    			1, horizontal*(heigth/10)+1,
	    			width+1, horizontal*(heigth/10)+1,
	    			grid_paint);
	    }
	        // draw center cross
		canvas.drawLine(0, (heigth/2)+1, width+1, (heigth/2)+1, cross_paint);
		canvas.drawLine((width/2)+1, 0, (width/2)+1, heigth+1, cross_paint);
		// draw outline
		canvas.drawLine(0, 0, (width+1), 0, outline_paint);	// top
		canvas.drawLine((width-1), 0, (width-1), (heigth+1), outline_paint); //right
		canvas.drawLine(0, (heigth+1), (width+1), (heigth+1), outline_paint); // bottom
		canvas.drawLine(0, 0, 0, (heigth+1), outline_paint); //left
		// plot data
		for(int x=0; x<(width-1); x++){
			canvas.drawLine(x+1, ch1_data[x], x+2, ch1_data[x+1], ch1_color);
		}
	}
}
Кусок MainActivity.java:

Код: Выделить всё

public class MainActivity extends Activity {
    
    private int MAX_SAMPLES = 320;
    private int[] ch1_data = new int[MAX_SAMPLES];
    private Handler mHandler = new Handler();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        int i;
        for(i=0; i < ch1_data.length; i++) {
        	ch1_data[i] = 0;
        }
        
        Thread t = new Thread(new Runnable() {
            public void run() {
              int i;
              try {
            	  while(ThreadCalc){
            	  TimeUnit.MILLISECONDS.sleep(1000/MAX_SAMPLES); // на экране отображаем данные, полученные за 1 сек.
            	  for(i=0; i < ch1_data.length; i++) {
              			if (i == MAX_SAMPLES - 1){
              				ch1_data[i] = new Random().nextInt(27500) + 5; // для теста заполняем массив случайными числами
                  		} else {
                  			ch1_data[i] = ch1_data[i + 1];
                  		}
                  }
                  mHandler.post(updateWaveform);
            	  }
              }
              catch (InterruptedException e) {
            	  e.printStackTrace();
              }
            }
          });
          t.start();
    }
    
    Runnable updateWaveform = new Runnable() {
        public void run() {
                if(D) Log.i(TAG, "Отправляем данные на отрисовку");
        	mWaveform.set_data(ch1_data); // отправляем собранные данные на отрисовку
        }
    };

    public synchronized void onPause() {
        super.onPause();
        mHandler.removeCallbacks(updateWaveform);
    }
}
После запуска приложения примерно первые полсекунды SurfaceView рисует график как положено, затем отрисовка прекращается.
Соответственно Логи выглядят следующим образом:
Первые ~полсекунды:
Отправляем данные на отрисовку
Принимаем данные и обрабатываем
Обновляем экран
Отправляем данные на отрисовку
Принимаем данные и обрабатываем
Обновляем экран и т.д. ...

А затем:
Отправляем данные на отрисовку
Принимаем данные и обрабатываем
Отправляем данные на отрисовку
Принимаем данные и обрабатываем
Отправляем данные на отрисовку
Принимаем данные и обрабатываем ...


Я так понимаю, что SurfaceView через ~500 мсек просто перестает успевать рисовать. Помогите разобраться.

Re: Проблема с рисованием динамического графика

Добавлено: 12 дек 2013, 14:49
KamiSempai
Проблема в методе set_data. А точнее в вызове plot_thread.setRunning(false);
Фактически вы останавливаете поток. Происходит это не сразу по тому, что потом в конце вызывается plot_thread.setRunning(true);
То-есть вы успеваете выключить и включить до того как работа потока дойдет до while(_run)

Вы же используете синхронизацию на holder, попробуйте ее же в set_data, а все вызовы setRunning в этом методе уберите.

PS: Советую почитать про остановку потока http://www.ibm.com/developerworks/ru/li ... -jtp05236/
Статья к вашему случаю относится косвенно, но думаю она поможет лучше понять как работать с потоками.

Re: Проблема с рисованием динамического графика

Добавлено: 12 дек 2013, 15:23
heartbeatMe
Всё получилось! Спасибо!