Как показать и скрыть один вид из разных потоков в Java?
Я пишу свою первую Java-программу для Android, которая рисует некоторые 3D-функции (f(x,y)) с использованием OpenGL, и программа почти завершена, но у меня возникают проблемы с отображением и скрытием пользовательского индикатора выполнения, разработанного как LinearLayout (который я создал) с некоторым текстом и прогрессбаром. Мне нужно, чтобы сделать progressBarLL видимым до и во время расчета значений 3d-функций. Для этого я реализовал обработчик в Graph3dActivity:
public class Graph3dAct extends Activity {
(...)
static ProgressBar progressBarPB;
static LinearLayout progressBarLL;
public static Handler progressHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.e("HANDLER:",Integer.toString(msg.arg1));
//toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 500);
switch (msg.arg1)
{
case 1: progressBarLL.setVisibility(LinearLayout.VISIBLE);
break;
case 2: progressBarLL.setVisibility(LinearLayout.INVISIBLE);
break;
case 3: progressBarPB.setProgress(msg.arg2);
//to be implemented
break;
default: Log.e("Graph3dAct","Handler misused;");
}
progressBarLL.invalidate();
progressBarPB.invalidate();
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
(...)
progressBarPB = (ProgressBar) findViewById(R.id.surface3dPB);
progressBarLL = (LinearLayout) findViewById(R.id.progressLL);
(...)
final Button moreStepsB = (Button) findViewById(R.id.buttonMoreS);
moreStepsB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int max=stepsVals.length-1;
if (stepsIndex<max)
{
stepsIndex++;
steps=stepsVals[stepsIndex];
FuncSurf fs = new FuncSurf(steps);
renderer.funcSurf = fs;
Затем у меня есть FuncSurf с его конструктором, выполняющим длительный расчет поверхности. Мне нужно показать мой progressBarLL перед расчетами:
public class FuncSurf {
public FuncSurf(int steps_) {
Log.e("FuncSurf","CONSTRUCTOR");
Message msg = new Message();
msg.arg1=1; //instruct handler to show progress layout: progressBarLL
Graph3dAct.progressHandler.sendMessage(msg);
(...) HERE GOES LONG-LASTING CALCULATION
msg = new Message();
msg.arg1=2; //instruct handler to hide progress layout: progressBarLL
Graph3dAct.progressHandler.sendMessage(msg);
}
Проблема состоит в том, что когда я вызываю конструктор функции FuncSurf из события onClick, реализованного в вышеупомянутом Activity, то это приводит к отображению progressBarLL в течение очень короткого времени и, к сожалению, после выполнения расчета, а не до этого. Другими словами, обработчик обрабатывает сообщения после того, как конструктор FuncSurf завершается не одновременно. Это наверное потому что это одна и та же тема? Но когда конструктор FuncSurf() вызывается из Renderer, все в порядке:
class MyOpenGLRenderer implements Renderer {
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
funcSurf = new FuncSurf(steps); //progressBarLL shown OK
Мой вопрос заключается в том, как, возможно, аккуратно исправить код так, чтобы progressBarLL показывался перед вычислениями в конструкторе FuncSurf() независимо от того, где я вызываю этот конструктор? Я не могу напрямую использовать progressBarLL.setVisibility в funcSurf, потому что это не поток GUI, когда вызывается onSurfaceChanged(...) выше. Извините, я не смог объяснить проблему яснее. Я с нетерпением жду вашей помощи.
2 ответа
Использование AsyncTask
, он разработан именно для этого случая (и принимает случай взаимодействия потоков без необходимости сообщений или чего-то в этом роде).
onPreExecute()
работает в потоке пользовательского интерфейса, делает индикатор выполнения видимым.doInBackground()
выполняет длинный расчет в фоновом потоке. Регулярные звонкиpublishProgress()
уведомить о прогрессе.onProgressUpdate()
работает в потоке пользовательского интерфейса и продвигает индикатор выполнения.onPostExecute()
запускается в потоке пользовательского интерфейса и делает индикатор выполнения невидимым.
См. Документацию " Поддержание адаптивности вашего приложения", в которой приведен аналогичный пример (в котором долгосрочное задание является сетевой операцией).
Тем временем я нашел "конструкцию нити", которая решает проблему на данный момент. Он принудительно выполняет "долгосрочную задачу" в отдельном потоке, позволяя потоку графического интерфейса продолжить и обновить графический интерфейс обработчиком. Небольшая модификация первого фрагмента кода, и программа теперь работает нормально:
Runnable run = new Runnable()
{
@Override
public void run() {
FuncSurf fs = new FuncSurf(steps);
renderer.funcSurf = fs;
}
};
Thread thr = new Thread(run);
thr.start();