片段中的同步呼叫在第一次启动时没有运行并且获取错误数据

问题描述:

我正在使用OkHttp获取HTML字符串并在RecyclerView中的片段中列​​出信息。片段中的同步呼叫在第一次启动时没有运行并且获取错误数据

但是,当应用程序运行时,首次启动时不显示列表信息。

当我点击其他片段页面并返回到这个片段页面后,它显示列表。

但是当我向下滑动以查看更多列表时,该列表重复两次(或更多次?)和项目背景颜色混乱。

我该如何解决?谢谢!

我的适配器

public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> { 
    private List<NewsModel> mNewsList; 
    class ViewHolder extends RecyclerView.ViewHolder { 
     TextView newsNameText; 
     TextView newsDataText; 
     View listView; 
     public ViewHolder(View newsView) { 
      super(newsView); 
      newsNameText = (TextView) newsView.findViewById(R.id.news_Name); 
      newsDataText = (TextView) newsView.findViewById(R.id.news_Data); 
      listView = newsView; 
     } 
    } 
    public NewsAdapter(List<NewsModel> newsList) { 
     mNewsList = newsList; 
    } 
    @Override 
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false); 
     final ViewHolder holder = new ViewHolder(view); 
     return holder; 
    } 
    public void setData(List<NewsModel> viewData) { 
     mNewsList.clear(); 
     mNewsList.addAll(viewData); 
     notifyDataSetChanged(); 
    } 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) { 
     NewsModel news = mNewsList.get(position); 
     holder.setIsRecyclable(true); 
     if(position % 2 == 0){ 
      holder.listView.setBackgroundColor(0x80E0EEEE); 
     } 
     holder.newsNameText.setText(news.getName()); 
     holder.newsDataText.setText(news.getData()); 
    } 
    @Override 
    public int getItemCount() { 
     return mNewsList.size(); 
    } 

} 

我的片段

public class NewsFragment extends Fragment { 
    List<NewsModel> resultList = new ArrayList<>(); 
    List<NewsModel> htmlList = new ArrayList<>(); 
    NewsAdapter adapter; 
    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     View newsView = inflater.inflate(R.layout.fragment_news, container, false); 
     RecyclerView newsRecyclerView = (RecyclerView) newsView.findViewById(R.id.news_list); 
     LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 
     newsRecyclerView.setLayoutManager(layoutManager); 
     adapter = new NewsAdapter(getNews()); 
     newsRecyclerView.setAdapter(adapter); 
     return newsView; 

    } 
    private List<NewsModel> getNews() { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        OkHttpClient client = new OkHttpClient(); 
        Request request = new Request.Builder() 
          .url("http://www.career.fudan.edu.cn/jsp/career_talk_list.jsp?count=50&list=true") 
          .build(); 
        Response response = client.newCall(request).execute(); 
        String resultString = response.body().string(); 
        resultList.clear(); 
        resultList.addAll(getResult(resultString)); 
        adapter.notifyDataSetChanged(); 
        /*new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { 
         @Override 
         public void run() { 
          adapter.setData(resultList); 
          adapter.notifyDataSetChanged(); 
         } 
        });//postdelayed (runnable long) cannot be applied to runnable*/ 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }).start(); 
     return resultList; 
    } 

    private List<NewsModel> getResult(final String response) { 
     XXXXXX 
     return htmlList; 
    } 
} 
+0

首先,Okhttp不应该在像这样的线程中运行。使用'client.newCall(request).enqueue' –

的问题是,也许你正在添加许多条目,因为recyclerview试图重用这些意见,而不是再次重新划分和再次这导致有时看法不一致。这是问题discussed如果您遇到同样的问题,请查看它。 禁用它或者启用它在你的bindViewHolder(

setIsRecyclable(Boolean enable)

使用和你的第二个问题是,onCreateView()从另一个片段回来造成加时被称为第二次再次复制数据,因此您需要在添加任何新条目之前清除列表,并且为什么它在第一次运行时不显示,因为您已经在另一个线程中添加了数据并且没有更新适配器。

List<NewsModel> resultList = new ArrayList<>(); 
NewsAdapter adapter ; 
public class NewsFragment extends Fragment { 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
         Bundle savedInstanceState) { 
    View newsView = inflater.inflate(R.layout.fragment_news, container, false); 
    RecyclerView newsRecyclerView = (RecyclerView) newsView.findViewById(R.id.news_list); 
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 
    newsRecyclerView.setLayoutManager(layoutManager); 
    adapter = new NewsAdapter(resultList); 
    newsRecyclerView.setAdapter(adapter); 
    getNews(); 
    return newsView; 
} 

    private void getNews(){ 
     AsyncTask<Void,Void,String> asyncTask = new AsyncTask<Void, Void, String>() { 
      @Override 
      protected String doInBackground(final Void... voids) { 
       String resultString = null; 
       OkHttpClient client = new OkHttpClient(); 
       Request request = new Request.Builder() 
         .url("http://www.career.fudan.edu.cn/jsp/career_talk_list.jsp?count=50&list=true") 
         .build(); 
       Response response = null; 
       try { 
        response = client.newCall(request).execute(); 
        resultString = response.body().string(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 

       return resultString; 
      } 

      @Override 
      protected void onPostExecute(final String resultString) { 
       super.onPostExecute(resultString); 
       resultList.clear(); 
       resultList.addAll(getResult(resultString)); 
       adapter.notifyDataSetChanged(); 
      } 
     }.execute(); 
    } 
+0

谢谢。我已经尝试过你的方法。但是这个清单变成了空白......无论我尝试过什么。 –

+0

嗨@ AlexLi空列表的原因是我忘了在onCreateView()中调用getNews()。所以你现在可以检查我已经更新了我的答案,还包括一个加载新闻数据的异步任务,这比我们以前做的更好。 –

+0

相应地更新您的getNews()并在onCreatView()中调用getNews()以查看更改。让我知道你是否有任何问题。 –

线程将改变一些延迟resultList,但在此之前,已经通过旧数据。 请尝试以下...

public class NewsFragment extends Fragment { 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
         Bundle savedInstanceState) { 
    View newsView = inflater.inflate(R.layout.fragment_news, container, false); 
    RecyclerView newsRecyclerView = (RecyclerView) newsView.findViewById(R.id.news_list); 
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 
    newsRecyclerView.setLayoutManager(layoutManager); 
    getNews(newsRecyclerView); 
    return newsView; 
} 

private void getNews(final RecyclerView newsRecyclerView){ 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      try { 
       OkHttpClient client = new OkHttpClient(); 
       Request request = new Request.Builder() 
         .url("http://www.career.fudan.edu.cn/jsp/career_talk_list.jsp?count=50&list=true") 
         .build(); 
       Response response = client.newCall(request).execute(); 
       String resultString = response.body().string(); 
       List<NewsModel> resultList = getResult(resultString); 
       NewsAdapter adapter = new NewsAdapter(resultList); 
       newsRecyclerView.setAdapter(adapter); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    }).start(); 
} 

private List<NewsModel> getResult(final String response) { 
    XXXXXXXXX 
    return List; 
} 

主要模式是坏的。 此代码最简单的方法是在适配器下载并调用notifyDataSetChanged()时设置(不添加)适配器中的项目 - 所有单元格都将重绘。

所以使适配器字段,添加方法适配器使用setData()和:

... 
Response response = client.newCall(request).execute(); 
String resultString = response.body().string(); 
resultList = getResult(resultString); 
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { 
     @Override 
     public void run() { 
      adapter.setData(results); 
      adapter.notifyDataSetChanged(); 
     } 
    }); 
+0

你不需要一个单独的setData方法。这个答案是内建模式。 https://*.com/a/47610769/2308683 –

+0

@ mac229 new Handler Alerted:postdelayed(runnable long)无法应用于可运行 –

+0

@ cricket_007我尝试过vikas的方法。该列表是空的。 –