为啥我不能在 AsyncTask 中为另一个类的 ListView 设置适配器?

Posted

技术标签:

【中文标题】为啥我不能在 AsyncTask 中为另一个类的 ListView 设置适配器?【英文标题】:Why I can't set adapter in AsyncTask for ListView from another class?为什么我不能在 AsyncTask 中为另一个类的 ListView 设置适配器? 【发布时间】:2016-04-09 17:17:47 【问题描述】:

我有 MainActivity 类和 AsyncTask 类。我有工作版本的代码,但后来我决定将 AsyncTask 类分离到另一个包中。在此之后,我遇到了一些问题,我正在努力解决它们。这是我的代码的最后一个工作版本。

    public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, SwipeRefreshLayout.OnRefreshListener 

    private static final String TAG = "MainActivity";
    private static final int LAYOUT = R.layout.activity_main;
    private static final String URL = "http://killpls.me";

    private Toolbar toolbar;
    private DrawerLayout drawerLayout;
    private NavigationView navigationView;
    private ActionBarDrawerToggle toggle;
    private FloatingActionButton fab;
    private ProgressDialog progressDialog;
    private SwipeRefreshLayout mSwipeRefreshLayout;

    private NewPostsAsyncTask newPostsAsyncTask;

    public Elements content;
    public ArrayList<String> titleList = new ArrayList<String>();
    public ArrayAdapter<String> adapter;
    public ListView listView;

    private int navigationDrawerItemId;
    private boolean isNavigationDrawerItemEnabled = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(LAYOUT);

        initToolbar();
        initNavigationView();
        initActionBarDrawerToggle(); // Добавляет возможность открыть NavigationDrawer через значок
        initFloatingActionButton();
        initSwipeRefreshLayout();

        listView = (ListView) findViewById(R.id.listView);
    

    private void initToolbar() 
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() 
            @Override
            public boolean onMenuItemClick(MenuItem item) 
                return false;
            
        );
    

    private void initNavigationView() 
        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    

    private void initActionBarDrawerToggle() 
        toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawerLayout.setDrawerListener(toggle);
        toggle.syncState();
    

    private void initFloatingActionButton() 
        fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            
        );
    

    private void initSwipeRefreshLayout() 
        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);
    

    @Override
    public void onBackPressed() 
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) 
            drawer.closeDrawer(GravityCompat.START);
         else 
            super.onBackPressed();
        
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) 
            return true;
        

        return super.onOptionsItemSelected(item);
    

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) 
        // Handle navigation view item clicks here.
        navigationDrawerItemId = item.getItemId();

        if (navigationDrawerItemId == R.id.new_posts) 
            Log.i(TAG, "Выбрано раздел \"Новые\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            newPostsAsyncTask = new NewPostsAsyncTask();
            newPostsAsyncTask.execute();
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
         else if (navigationDrawerItemId == R.id.moderation) 
            Log.i(TAG, "Выбрано раздел \"Модерация\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
         else if (navigationDrawerItemId == R.id.tell_story) 
            Log.i(TAG, "Выбрано раздел \"Рассказать историю\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
         else if (navigationDrawerItemId == R.id.most_terrible_stories) 
            Log.i(TAG, "Выбрано раздел \"Самые страшные\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
         else if (navigationDrawerItemId == R.id.random_story) 
            Log.i(TAG, "Выбрано раздел \"Случайная\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
         else if (navigationDrawerItemId == R.id.happy_end) 
            Log.i(TAG, "Выбрано раздел \"Happy end\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
         else if (navigationDrawerItemId == R.id.about_project) 
            Log.i(TAG, "Выбрано раздел \"О проекте\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
         else if (navigationDrawerItemId == R.id.help_all) 
            Log.i(TAG, "Выбрано раздел \"Хочу помочь всем\" в Navigation Drawer");
            adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item, titleList);
            if (!adapter.isEmpty()) adapter.clear();
            isNavigationDrawerItemEnabled = true;
        

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    

    @Override
    public void onRefresh() 
        new Handler().postDelayed(new Runnable() 
            @Override
            public void run() 
                if (navigationDrawerItemId == R.id.new_posts) 
                    Log.i(TAG, "Обновленно раздел \"Новые\" в Navigation Drawer");
                    newPostsAsyncTask = new NewPostsAsyncTask();
                    newPostsAsyncTask.execute();
                 else if (navigationDrawerItemId == R.id.moderation) 
                    Log.i(TAG, "Обновленно раздел \"Модерация\" в Navigation Drawer");
                 else if (navigationDrawerItemId == R.id.tell_story) 
                    Log.i(TAG, "Обновленно раздел \"Рассказать историю\" в Navigation Drawer");
                 else if (navigationDrawerItemId == R.id.most_terrible_stories) 
                    Log.i(TAG, "Обновленно раздел \"Самые страшные\" в Navigation Drawer");
                 else if (navigationDrawerItemId == R.id.random_story) 
                    Log.i(TAG, "Обновленно раздел \"Случайная\" в Navigation Drawer");
                 else if (navigationDrawerItemId == R.id.happy_end) 
                    Log.i(TAG, "Обновленно раздел \"Happy end\" в Navigation Drawer");
                 else if (navigationDrawerItemId == R.id.about_project) 
                    Log.i(TAG, "Обновленно раздел \"О проекте\" в Navigation Drawer");
                 else if (navigationDrawerItemId == R.id.help_all) 
                    Log.i(TAG, "Обновленно раздел \"Хочу помочь всем\" в Navigation Drawer");
                 else 
                    Log.i(TAG, "Попытка обновить главную страницу");
                

                // Когда обновление закончено, вызываем метод setRefreshing(boolean) и передаем ему false.
                mSwipeRefreshLayout.setRefreshing(false);
            
        , 2000);

        if (!adapter.isEmpty()) adapter.clear();
    

    class NewPostsAsyncTask extends AsyncTask<String, Void, String> 

        @Override
        protected void onPreExecute() 
            super.onPreExecute();

            if (!isNavigationDrawerItemEnabled) 
                progressDialog = new ProgressDialog(MainActivity.this);
                progressDialog.setTitle("Новые");
                progressDialog.setMessage("Загрузка...");
                progressDialog.setIndeterminate(false);
                progressDialog.show();
            
        

        @Override
        protected String doInBackground(String... params) 
            Document doc;
            try 
                doc = Jsoup.connect(URL).get(); // Считываем заголовок страницы

                // Получение номера страницы селектором и преобразование его в число
                Elements pageSpan = doc.select("div.paginator > span:first-child");
                int pageCount = Integer.parseInt(pageSpan.first().text());
                // Стоит еще проверить, что элементы нашлись, вызовом !pageSpan.isEmpty(),
                // first() для пустого списка возвращает null.

                for (int i = pageCount; i > 0; i--) 
                    String pageCountString = Integer.toString(i);
                    doc = Jsoup.connect("http://killpls.me/page/" + pageCountString).get();
                    parseDocument(doc);
                    if (i == 1697) 
                        break; // Ограничение до 1697 страницы, чтобы не лагало. Надо исправить.
                    
                

             catch (IOException e) 
                e.printStackTrace(); // Если не получилось считать
            
            return null;
        

        @Override
        protected void onPostExecute(String s) 
            super.onPostExecute(s);
            listView.setAdapter(adapter);
            progressDialog.dismiss();
        
    

    public void parseDocument(Document doc) 

        // Парсит посты на странице
        content = doc.select("[style=margin:0.5em 0;line-height:1.785em]");

        for (Element contents : content) 
            if (!contents.text().contains("18+")) 
                // Выводит только посты без ссылки на 18+
                titleList.add(contents.text());
            
        
    

我在使用 MainActivity 类的适配器时遇到问题。这是另一个包中的新 AsyncTask 类的代码。我在mainActivity.listView.setAdapter(mainActivity.adapter); 有 NullPointerException,你能告诉我为什么吗?

    public class NewPostsAsyncTask extends AsyncTask<String, Void, String> 

    private static final String URL = "http://killpls.me";
    private ProgressDialog progressDialog;
    private Elements content;

    private Context context;

    public NewPostsAsyncTask(Context context) 
        this.context = context;
    

    MainActivity mainActivity = new MainActivity();

    @Override
    protected void onPreExecute() 
        super.onPreExecute();

        if (!MainActivity.isNavigationDrawerItemEnabled) 
            progressDialog = new ProgressDialog(context); //MainActivity.this
            progressDialog.setTitle("Новые");
            progressDialog.setMessage("Загрузка...");
            progressDialog.setIndeterminate(false);
            progressDialog.show();
        
    

    @Override
    protected String doInBackground(String... params) 
        Document doc;
        try 
            doc = Jsoup.connect(URL).get(); // Считываем заголовок страницы

            // Получение номера страницы селектором и преобразование его в число
            Elements pageSpan = doc.select("div.paginator > span:first-child");
            int pageCount = Integer.parseInt(pageSpan.first().text());
            // Стоит еще проверить, что элементы нашлись, вызовом !pageSpan.isEmpty(),
            // first() для пустого списка возвращает null.

            for (int i = pageCount; i > 0; i--) 
                String pageCountString = Integer.toString(i);
                doc = Jsoup.connect("http://killpls.me/page/" + pageCountString).get();
                parseDocument(doc);
                if (i == 1697) 
                    break; // Ограничение до 1697 страницы, чтобы не лагало. Надо исправить.
                
            

         catch (IOException e) 
            e.printStackTrace(); // Если не получилось считать
        
        return null;
    

    private void parseDocument(Document doc) 
        // Парсит посты на странице
        content = doc.select("[style=margin:0.5em 0;line-height:1.785em]");

        for (Element contents : content) 
            if (!contents.text().contains("18+")) 
                // Выводит только посты без ссылки на 18+

                mainActivity.titleList.add(contents.text());
                //titleList.add(contents.text());
            
        
    

    @Override
    protected void onPostExecute(String s) 
        super.onPostExecute(s);

        mainActivity.listView.setAdapter(mainActivity.adapter);
        //listView.setAdapter(adapter);
        progressDialog.dismiss();
    

【问题讨论】:

首先,你不能通过调用new MainActivity()来实例化MainActivity。 Android 不能那样工作。其次,listView 无论如何都是私有的,这意味着您无法在 MainActivity 类之外访问它。您需要做的是编写一个回调,将AsyncTask 的结果发送回您的 MainActivity。这是完成您正在尝试做的事情的正确方法。 @NoChinDeluxe 哦,对不起。我从我的 github 复制了这段代码。现在在我的项目中,我有公共字段: public ArrayList titleList = new ArrayList();公共 ArrayAdapter 适配器;公共列表视图列表视图;但它不起作用。我应该如何正确地将此回调与结果写入我的 MainActivity? 【参考方案1】:

因为您在创建 MainActivity 构造函数时直接从 AsyncTask 引用 MainActivity 的成员字段,这是错误的。

只需在 AsyncTask 的构造函数中传递您想要的任何成员字段并使用那个,

喜欢,

private Context context;
private ListView mListView;
private ArrayAdapter<String> adapter;
private List<String> titleList = new ArrayList<String>();

public NewPostsAsyncTask(Context context, ListView listview) 
        this.context = context;
        this.mListView = listview;
    

private void parseDocument(Document doc) 
        // Парсит посты на странице
        content = doc.select("[style=margin:0.5em 0;line-height:1.785em]");

        for (Element contents : content) 
            if (!contents.text().contains("18+")) 
                // Выводит только посты без ссылки на 18+
                titleList.add(contents.text());
            
        
    


    @Override
    protected void onPostExecute(String s) 
        super.onPostExecute(s);
        adapter = new ArrayAdapter<String>(context, R.layout.list_item, R.id.item, titleList);
        mListView.setAdapter(adapter);
        progressDialog.dismiss();
    

然后删除,MainActivity mainActivity = new MainActivity();

现在像这样从 Activity 调用 AsyncTask,

newPostsAsyncTask = new NewPostsAsyncTask(MainActivity.this, listView);
newPostsAsyncTask.execute();

【讨论】:

以上是关于为啥我不能在 AsyncTask 中为另一个类的 ListView 设置适配器?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 AsyncTask 不能正常工作?

为啥我不能正确使用扩展类的方法?

为啥 PRIVATE 成员函数不能成为另一个类的友元函数?

为啥我不能为另一个类提取方法?

为啥我不能将泛型类型的一个实例转换为另一个实例?

为啥我可以为一个用户上传图库图片,但不能为另一个用户上传?