EndlessRecyclerOnScrollListener 与 Asynctask 的 Endless RecyclerView

Posted

技术标签:

【中文标题】EndlessRecyclerOnScrollListener 与 Asynctask 的 Endless RecyclerView【英文标题】:Endless RecyclerView with Asynctask using EndlessRecyclerOnScrollListener 【发布时间】:2017-11-20 04:23:56 【问题描述】:

我有一个搜索查询的活动(使用async 任务和服务器端php)并在recycler view 中给出一个列表,现在我想实现无限/无限滚动 当用户到达底部时。我曾尝试使用EndlessScrollListener 来做到这一点。有两个问题我需要帮助。

首先,新列表会重新创建自己,而不是附加到旧列表中。

其次,current_page int 变量保持上次搜索和滚动时的值,这意味着当第二次运行AsyncTask 时,current_page int 变量仍然保留第一次的值并且不会重置。

   public class MainActivity extends AppCompatActivity 

// CONNECTION_TIMEOUT and READ_TIMEOUT are in milliseconds
public static final int CONNECTION_TIMEOUT = 10000;
public static final int READ_TIMEOUT = 15000;
private RecyclerView mRVFish;
private AdapterFish mAdapter;
private  LinearLayoutManager mLayoutManager;
private String searchQuery;


SearchView searchView = null;


private String query;


private EndlessRecyclerOnScrollListener mScrollListener = null;
private SwipeRefreshLayout mSwipeRefreshLayout = null;


@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    mRVFish = (RecyclerView) findViewById(R.id.fishPriceList);
    mLayoutManager = new LinearLayoutManager(MainActivity.this);
    mRVFish.setLayoutManager(mLayoutManager);




@Override
public boolean onCreateOptionsMenu(Menu menu) 

    // adds item to action bar
    getMenuInflater().inflate(R.menu.search_main, menu);

    // Get Search item from action bar and Get Search service
    MenuItem searchItem = menu.findItem(R.id.action_search);
    SearchManager searchManager = (SearchManager) MainActivity.this.getSystemService(Context.SEARCH_SERVICE);
    if (searchItem != null) 
        searchView = (SearchView) searchItem.getActionView();
    
    if (searchView != null) 
        searchView.setSearchableInfo(searchManager.getSearchableInfo(MainActivity.this.getComponentName()));
        searchView.setIconified(false);
    

    return true;


@Override
public boolean onOptionsItemSelected(MenuItem item) 

    return super.onOptionsItemSelected(item);




// Every time when you press search button on keypad an Activity is recreated which in turn calls this function
@Override
protected void onNewIntent(Intent intent) 
    // Get search query and create object of class AsyncFetch
    if (Intent.ACTION_SEARCH.equals(intent.getAction())) 
          query = intent.getStringExtra(SearchManager.QUERY);
        if (searchView != null) 
            searchView.clearFocus();
        
       int startrow =0;
        String type="";
        String filetype="";

        AsyncFetch myTask = new AsyncFetch(query, startrow,  type,  filetype);


        myTask.execute();



        mScrollListener = new EndlessRecyclerOnScrollListener(mLayoutManager) 
            @Override
            public void onLoadMore(int current_page) 

                int startrow=current_page;
                String type="";
                String filetype="";
                AsyncFetch myTask = new AsyncFetch(query, startrow,  type,  filetype);

                myTask.execute();


            
        ;

        mRVFish.addOnScrollListener(mScrollListener);

        // enable pull down to refresh
        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() 
            @Override
            public void onRefresh() 

                int startrow =0;
                String type="";
                String filetype="";

                AsyncFetch myTask = new AsyncFetch(query, startrow,  type,  filetype);

                myTask.execute();

                // after refresh is done, remember to call the following code
                if (mSwipeRefreshLayout != null && mSwipeRefreshLayout.isRefreshing()) 
                    mSwipeRefreshLayout.setRefreshing(false);  // This hides the spinner
                
            
        );



    




// Create class AsyncFetch
private class AsyncFetch extends AsyncTask<String, String, String> 

    ProgressDialog pdLoading = new ProgressDialog(MainActivity.this);
    HttpURLConnection conn;
    URL url = null;
    String searchQuery;
    int startrow;
    String type;
    String filetype;


    public AsyncFetch(String searchQuery,  int startrow, String type, String filetype)
        this.searchQuery=searchQuery;
        this.startrow = startrow;
        this.type = type;
        this.filetype=filetype;
    

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

        //this method will be running on UI thread
        pdLoading.setMessage("\tLoading...");
        pdLoading.setCancelable(false);
        pdLoading.show();

    

    @Override
    protected String doInBackground(String... params) 
        try 

            // Enter URL address where your php file resides
            url = new URL("http://someurl/json/search.php");

         catch (MalformedURLException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
            return e.toString();
        
        try 

            // Setup HttpURLConnection class to send and receive data from php and mysql
            conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(READ_TIMEOUT);
            conn.setConnectTimeout(CONNECTION_TIMEOUT);
            conn.setRequestMethod("POST");

            // setDoInput and setDoOutput to true as we send and recieve data
            conn.setDoInput(true);
            conn.setDoOutput(true);

            // add parameter to our above url
            Uri.Builder builder = new Uri.Builder().appendQueryParameter("searchQuery", searchQuery).appendQueryParameter("startrow", String.valueOf(startrow));
            String query = builder.build().getEncodedQuery();

            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
            writer.write(query);
            writer.flush();
            writer.close();
            os.close();
            conn.connect();

         catch (IOException e1) 
            // TODO Auto-generated catch block
            e1.printStackTrace();
            return e1.toString();
        

        try 

            int response_code = conn.getResponseCode();

            // Check if successful connection made
            if (response_code == HttpURLConnection.HTTP_OK) 

                // Read data sent from server
                InputStream input = conn.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(input));
                StringBuilder result = new StringBuilder();
                String line;

                while ((line = reader.readLine()) != null) 
                    result.append(line);
                

                // Pass data to onPostExecute method
                return (result.toString());

             else 
                return("Connection error");
            

         catch (IOException e) 
            e.printStackTrace();
            return e.toString();
         finally 
            conn.disconnect();
        


    

    @Override
    protected void onPostExecute(String result) 


        //this method will be running on UI thread
        pdLoading.dismiss();

        List<DataFish> data=new ArrayList<>();

        pdLoading.dismiss();
        if(result.equals("no rows")) 
            Toast.makeText(MainActivity.this, "No Results found for entered query", Toast.LENGTH_LONG).show();
        else

            try 

                JSONArray jArray = new JSONArray(result);

                // Extract data from json and store into ArrayList as class objects
                for (int i = 0; i < jArray.length(); i++) 
                    JSONObject json_data = jArray.getJSONObject(i);
                    DataFish fishData = new DataFish();
                    try 
                        fishData.fileName = URLDecoder.decode(json_data.getString("file"), "UTF-8");
                     catch (Exception e) 

                        Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
                        fishData.fileName=json_data.getString("file");


                    

                    fishData.linkName = json_data.getString("link");
                    fishData.reg_date = json_data.getString("reg_date");
                    fishData.fileSize = json_data.getString("filesize");
                    data.add(fishData);
                

                mAdapter = new AdapterFish(MainActivity.this, data);

                   mRVFish.setAdapter(mAdapter);


                if(jArray.length()>19)
                mScrollListener.setLoading(false);
                else
                    mScrollListener.setLoading(true);

             catch (JSONException e) 
                // You to understand what actually error is and handle it appropriately
                Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
                Toast.makeText(MainActivity.this, result.toString(), Toast.LENGTH_LONG).show();
            





        


    




 




EndlessRecyclerOnScrollListener.java

public abstract class EndlessRecyclerOnScrollListener extends 
RecyclerView.OnScrollListener 
public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();

private int previousTotal = 0; // The total number of items in the dataset after the last load
private boolean loading = false; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 0; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;

private int current_page = 20;

private LinearLayoutManager mLayoutManager;

public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) 
    this.mLayoutManager = linearLayoutManager;


@Override
public void onScrolled(RecyclerView mRVFish, int dx, int dy) 
    super.onScrolled(mRVFish, dx, dy);

    if(dy < 0) 
        return;
    
    // check for scroll down only
    visibleItemCount = mRVFish.getChildCount();
    totalItemCount = mLayoutManager.getItemCount();
    firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();

    // to make sure only one onLoadMore is triggered
    synchronized (this) 
        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) 
            // End has been reached, Do something


            current_page=current_page+20;
            onLoadMore(current_page);
            loading = true;
        
    



public void setLoading(boolean loading) 
    this.loading = loading;


public abstract void onLoadMore(int current_page);


【问题讨论】:

【参考方案1】:

首先,新列表会重新创建自己,而不是附加到旧列表中。

如果您设置一个新的适配器,您将交换回收站视图的内容。我想你在下面的代码中做到了

mAdapter = new AdapterFish(MainActivity.this, data);
mRVFish.setAdapter(mAdapter); 

要附加数据,您需要将新元素添加到适配器的数据集(您获取数据以在适配器中绑定视图的数据集),然后调用 adapter.notifyDataSetChanged() 或更好的 adapter.notifyItemRangeInserted(int positionStart, int itemCount) 还有很多工具可以优化添加元素,比如DiffUtil

第二,current_page int 变量保持上次搜索和滚动时的值,表示第二次运行 AsyncTask 时

不确定库开发者的想法是什么,但最简单的方法是在 EndlessRecyclerOnScrollListener 中创建一个方法,类似于:

public void resetPage() 
    current_page = 20;

并在调用新搜索之前调用它。

【讨论】:

第一个问题已解决,感谢您。我无法解决current_page 问题。你能告诉我怎么做吗? 很难说,我建议你做的第一件事是在创建新的监听器之前用removeOnScrollListener(OnScrollListener) 删除旧的监听器并执行mRVFish.addOnScrollListener(mScrollListener);

以上是关于EndlessRecyclerOnScrollListener 与 Asynctask 的 Endless RecyclerView的主要内容,如果未能解决你的问题,请参考以下文章