0

我有一个包含多个项目的 Listview,每个项目都有 2 个按钮。我想要做的是当我点击一个按钮时,它会通过 HttpPost 向服务器发送一些数据。我在我的 Adapter.getView() 中放置了一个线程,但它不起作用,因为适配器在异步线程中已经存在。你知道我怎样才能让它工作吗?

我的列表视图:

// ... OnCreate ListView ...
// ... new ListAppTask().execute();
// ...
public class ListAppTask extends AsyncTask<Void, Void, List<ProductExtended>> {

    protected List<ProductExtended> doInBackground(Void... args) {
        Product product = new Product();
        List<ProductExtended> products = product.getLastProducts(getApplicationContext());

        return products;
    }

    protected void onPostExecute(List<ProductExtended> result) {
        data.addAll(result);
        adapter.notifyDataSetChanged();
        if (progressDialog != null) {
            progressDialog.dismiss();
            progressDialog = null;
        }
    }
}

我的适配器:

    public class PackageAdapter extends BaseAdapter {

        private List<ProductExtended> data;
        private Context context;

        public PackageAdapter(Context context, List<ProductExtended> data) {
            this.context = context;
            this.data = data;
        }

        @Override
        public int getCount() {
            return data.size();
        }

        @Override
        public ProductExtended getItem(int position) {
            return data.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            final ProductExtended item = getItem(position);
            ViewHolder holder;
            if (convertView == null) {
                LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = li.inflate(R.layout.package_row, parent, false);
                holder = new ViewHolder();
                holder.btnBa = (LinearLayout) convertView.findViewById(R.id.idBonneaffaire);
                holder.btnJl = (LinearLayout) convertView.findViewById(R.id.idJelai);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }


            holder.btnBa.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {

                        new Thread() {

                            @Override
                            public void run() {
                                // ---------------- PROBLEM HERE -------------------
                                HttpClient httpclient = new DefaultHttpClient();
                                HttpPost httppost = new HttpPost("My URL");
                                // Execute HTTP Post Request
                                try {
                                    HttpResponse response = httpclient.execute(httppost);
                                    String reponse = EntityUtils.toString(response.getEntity());

                                } catch (UnknownHostException e) {
                                } catch (HttpHostConnectException e) {
                                } catch (ClientProtocolException e) {
                                } catch (IOException e) {
                                }
                            }
                        }.start();
                }
            });

          // Same for the second LinearLayout considered as Button



            return convertView;
        }

        static class ViewHolder {
            LinearLayout btnBa;
            LinearLayout btnJl;
        }

并显示错误:

Only the original thread that created a view hierarchy can touch its views. adapter

谢谢,阿克拉姆

4

2 回答 2

2

此错误主要描述您正在尝试从主线程之外的另一个线程更新 UI。如果您出于某种原因尝试在您的 onClickListener 提供的线程的 Run 方法中触摸您的视图,就会发生这种情况。它在您的示例中不可见,但您可以检查您在声明“// 第二个 LinearLayout 被视为按钮时所做的工作”吗?

为防止发生此异常,您可以在需要更新 UI 时使用 android.os.Handler 在主线程上执行 Runnable。

例如:

Handler mHandler = new Handler();

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;
    if(convertView == null){
        convertView = mInflater.inflate(R.layout.item_simple_row, parent, false);
        holder = new ViewHolder();
        holder.textView = (TextView) convertView.findViewById(R.id.txt);
        holder.button = (Button) convertView.findViewById(R.id.btn);
        convertView.setTag(holder);
    }else{
        holder = (ViewHolder) convertView.getTag();
    }

    final String url = getItemAtPosition(position);
    holder.textView.setText(url);
    holder.button.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            final Thread worker = new Thread(new Runnable(){

                @Override
                public void run()
                {
//WATCH OUT IF YOU TOUCH YOUR VIEW HERE WITHOUT USING A HANDLER YOU MAY GET THE ERROR YOUR ARE TALKING ABOUT

                    HttpGet request = new HttpGet(url);
                    DefaultHttpClient httpClient = new DefaultHttpClient();
                    try {
                        final HttpResponse response = httpClient.execute(request);

                        mHandler.post(new Runnable() {

                            @Override
                            public void run() {
//UPDATE YOUR UI HERE
                                holder.textView.setText(url+ " : " +response.getStatusLine());
                            }
                        });

                    } catch (ClientProtocolException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });

            worker.start();


        }
    });

    return convertView;
}
于 2013-07-01T22:38:11.393 回答
1

我更喜欢保持简单。ThreadHandler分别是重要的 java 和 android 概念,但是响应 UI 事件的 http 在 Android 中非常普遍,他们发明了一个名为AsyncTask的惊人助手。

使用对封闭 Activity 类的内部引用创建此类的匿名实例(即,实际上,在您的 Activity 中定义该类并且不要创建它static)。开发人员在设计 Android UI 和线程模式时考虑的正是这些 Java 特性。

从上面的文档:

当一个异步任务被执行时,该任务经过...

doInBackground(Params...),在完成执行后立即在后台线程上调用。onPreExecute()此步骤用于执行可能需要很长时间的后台计算。...

onPostExecute(Result),在后台计算完成后在UI 线程上调用。后台计算的结果作为参数传递给该步骤。

[我省略了额外的细节,也是我的重点]。

您的适配器getView应该引用一个异步任务,该任务将信息从一个执行的代码块中传递信息,该代码块执行所有同步 http 请求,离开 UI 线程,到 UI 线程上可用于触摸其视图的代码块(您更新您的列表视图在这里)。

注意不要忘记在任务完成后检查您的列表视图是否仍然与该数据相关,或者,由于AbsListView视图回收,您可能会将数据放入明显错误的单元格中。

最好的,

汤姆。

于 2013-07-01T22:56:21.350 回答