1

我有一个 ImageButton 在单击时执行长时间运行的操作。所以我没有成功地尝试在onClick调用开始时更改图像并在结束时将其更改回来。

我已经尝试了下面的代码,但我发现android在onClick完成后重绘了按钮图像,所以它没有用。

如果可能的话,我也愿意接受其他方式来做到这一点。

下面是我使用的代码片段。Thread.sleep代表长时间运行的操作。我希望在执行到达睡眠时而不是在onClick完成后看到图像变化。

btnTest = (ImageButton) findViewById(R.id.btnTest);
btnTest.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
    System.out.println("start");
    btnTest.setImageResource(R.drawable.add);
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }

    System.out.println("end");
    btnTest.setImageResource(R.drawable.del);
}
});
4

2 回答 2

6

您的问题是您sleep()在(或以其他方式阻止)UI 线程。请理解主/UI 线程负责重绘和所有事件处理。如果你在那里睡觉onClick()在 UI 线程中执行),那么 Android 将无法做任何与用户界面相关的事情。

背景是多线程 GUI 效率极低。大多数与 UI 相关的调用,例如setBackground,简单地设置一些数据甚至安排更多的活动,都不会立即产生效果,并且只有当控件返回到主 UI 线程控制循环时才会产生结果,例如协调重绘。试想一下,如果您一个接一个地启动几个布局更改并且每个都将立即处理会发生什么——这将导致大量计算在下一刻变得无效。

所以主线程只不过是一个处理各种回调的巨大循环,包括Activity生命周期方法、事件侦听器回调等。所以如果你愿意的话,UI 是单线程的。

你需要做的是在一个AsyncTask或线程上执行你的长时间运行的操作。对于初学者,我会尝试一个AsyncTask. 在再次在 UI 线程上运行的 AsyncTaskonPostExecute()方法中,您可以随意操作用户界面。

为了快速成功,您还可以使用线程:

@Override
public void onClick(View v) {
    // on UI thread
    btnTest.setImageResource(R.drawable.add);
    // launch concurrent thread
    (new Thread(new Runnable() {
            @Override
            // the code of this method will run concurrently
            public void run() {
                System.out.println("start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {}
                System.out.println("end");
                // at this point, we need to get back to the UI thread
                // for simplicity, we use the view's convenience method for that
                v.post(new Runnable() {
                    @Override
                    // the code of this method will run on the UI thread
                    public void run() {
                       btnTest.setImageResource(R.drawable.del);
                    }
                })
            }
        })
    ).start();
}

AsyncTask看起来好多了,但它并不容易即时创建和使用。

请注意,上面的代码会创建一个Thread运行,直到它离开它的run()方法,或者 Android 杀死了托管您的应用程序的 Linux 进程。

另请注意,此线程包含对代码中许多对象的引用,因为它可以访问其周围范围内的所有内容。

结论是,如果您的 Thread 确实运行了很长时间,它可能会持有对不再可见的 View 的引用,因此僵尸内存对象可能会恶化您的应用程序的资源消耗。

另外,您提到您正在那里进行长期运行的操作。问题是,该操作是等待还是计算一些东西。如果它计算某些东西,您可能会发现它现在需要十倍的时间,除非您按照此处所述调整AsyncTask/Thread优先级

于 2013-04-16T08:41:16.357 回答
1

我希望你会在 onclick 中做一些操作,而不仅仅是 Thread.Sleep()。

在这种情况下,您可以使用以下方法使用 AsyncTask。

btnTest = (ImageButton) findViewById(R.id.btnTest);
btnTest.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {

       new SomeLongRunningOperationAsyncTask().execute();

   }
});

用于长时间运行的进程的 AsyncTask 类。

class SomeLongRunningOperationAsyncTask extends AsyncTask<Void, Void, Void>
{

    @Override
    protected void onPreExecute() {

        super.onPreExecute();
        System.out.println("start");
        btnTest.setImageResource(R.drawable.add);

    }

    @Override
    protected Void doInBackground(Void... params) {

        //your long running operation.
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {

        super.onPostExecute(result);
        System.out.println("end");
        btnTest.setImageResource(R.drawable.del);
    }
}
于 2013-04-16T08:51:44.983 回答