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的主要内容,如果未能解决你的问题,请参考以下文章