首先,最好将 RecyclerView 与 viewholder 一起使用,其次您最好在滚动侦听器上加载数据和一些技巧,例如您已经加载了 20 个项目,然后当您滚动列表时,当您滚动列表并滚动到时加载数据会很好例如底部的 18 项,在这里您开始您的异步任务,当您滚动到 20 时,您的加载将完成,您将更新 20 更多的列表,因此用户甚至不会看到您何时加载。
mVisibleThreshold = 2 // this is count items to bottom of current list when load will start.
这是我的 recyclerview 滚动监听器(可以调整到您的列表视图):
mProductsResultsList.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mVisibleItemCount = mProductsResultsList.getChildCount();
mTotalItemCount = mProductsResultsLayoutManager.getItemCount();
mFirstVisibleItem = mProductsResultsLayoutManager.findFirstVisibleItemPosition();
if (mLoadingInProgress) { // boolean set to true if you are already loading and false after you will update adaptor
if (mTotalItemCount > mPreviousTotal) {
mPreviousTotal = mTotalItemCount;
}
}
if (!mLoadingInProgress && (mTotalItemCount - mVisibleItemCount)
<= (mFirstVisibleItem + mVisibleThreshold)) {
mLoadingInProgress = true;
mLastPageRequest++; // current page to load and add
// Here you load data and update adapter
// if in async task then start it here and set mLoadingInProgress to true and onPostExecute add to list result and make notifyDatasetChanged on adapter then mLoadingInProgress to false.
}
}
});
还有一点:后台任务中不要碰任何视图(否则会卡住主线程),在 onPostExecute 中使用 RunOnUIThread 进行任务代码之后的所有更新和通知。
好的,因为您对此解决方案感兴趣会改进答案:
mLastPageRequest - int 我用来知道我加载了哪个页面以及接下来我必须加载哪个页面,它每次使用 ++ 递增 1
加载数据后(从数据库或网络请求,您应该将下载的项目添加到您的列表中)。
目前在我的项目中,我的列表是“mCategoryProducts”,这是我的适配器 mProductsResultsListAdapter。
你只需要添加你下载的所有下一个项目,以列出你连接到适配器的
mCategoryProducts.addAll(downloadedListProducts); // here you are adding items you just loaded to existing list to the end,
现在下一个代码:
public void displayGotResults(){
if(mCategoryProducts != null){
if(mProductsResultsListAdapter == null && mLastPageRequest==1){
mProductsResultsListAdapter = new ProductListRecyclerViewAdapter(this, mCategoryProducts, true);
mProductsResultsList.setAdapter(mProductsResultsListAdapter);
mProductsResultsListAdapter.setClickListener(this);
}else{
mProductsResultsListAdapter.notifyDataSetChanged();
//notifyItemInserted(mSearchList.size()-1);
}
if(mCategoryProducts.size()>0) {
mCategoryIsEmptyInfo.setVisibility(View.GONE);
}else{
mCategoryIsEmptyInfo.setVisibility(View.VISIBLE);
}
}else{
mCategoryIsEmptyInfo.setVisibility(View.VISIBLE);
}
}
如果适配器尚未初始化,那么我们创建它并将我们的列表 mCategoryProducts 附加到它,否则如果这是第二次加载,那么我们简单地通知适配器“嘿,新数据正在到来:)”:
mProductsResultsListAdapter.notifyDataSetChanged();
注意:mCategoryIsEmptyInfo - 是我在没有要显示的项目时显示的视图。
这是我的自定义适配器,带有可以在活动中处理的点击界面)
public class ProductListRecyclerViewAdapter extends RecyclerView.Adapter<ProductListRecyclerViewAdapter.ProductListRecyclerViewHolder> {
private LayoutInflater mInflater;
private Context mContext;
private DrawerLayout mNavigationDrawer;
private ClickListener mClickListener;
private LongClickListener mLongClickListener;
List<ProductModel> navigationData = Collections.emptyList();
private ImageLoader mImageLoader;
private VolleyServiceSingleton mVollayService;
private boolean mShowThumbNail;
//For animations
private int mLastPositiion = -1;
public ProductListRecyclerViewAdapter (Context context, List<ProductModel> navData, boolean showThumbNail){
mInflater = LayoutInflater.from(context);
this.navigationData = navData;
mContext = context;
mVollayService = VolleyServiceSingleton.getInstance();
mImageLoader = mVollayService.getImageLoader();
mShowThumbNail = showThumbNail;
}
@Override
public ProductListRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.list_item_product, parent, false);
ProductListRecyclerViewHolder holder = new ProductListRecyclerViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(final ProductListRecyclerViewHolder viewHolder, int position) {
ProductModel currentItem = navigationData.get(position);
viewHolder.productBrand.setText(currentItem.ManufacturerName);
viewHolder.productName.setText(currentItem.Name);
viewHolder.productCode.setText(currentItem.Code);
viewHolder.productPrice.setText(String.format(mContext.getString(R.string.money_sign), Utils.decimalWithCommas(String.valueOf(currentItem.DealerPrice))));
if(Constants.SHOW_IMAGE_THUMBNAILS_IN_LIST && mShowThumbNail){
if(currentItem.CatalogImage != null && currentItem.CatalogImage.contains("http")){
viewHolder.productThumbnail.setVisibility(View.VISIBLE);
mImageLoader.get(currentItem.CatalogImage, new ImageLoader.ImageListener() {
@Override
public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
viewHolder.productThumbnail.setImageBitmap(response.getBitmap());
}
@Override
public void onErrorResponse(VolleyError error) {
}
});
}
}else{
viewHolder.productThumbnail.setVisibility(View.GONE);
}
}
public void setAnimation(View viewToAnimate, int position)
{
// If the bound view wasn't previously displayed on screen, it's animated
if (position > mLastPositiion)
{
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
mLastPositiion = position;
}
}
@Override
public int getItemCount() {
return navigationData.size();
}
public void setClickListener(ClickListener clickListener){
this.mClickListener = clickListener;
}
public void setLongClickListener(LongClickListener lognClickListener){
this.mLongClickListener = lognClickListener;
}
public class ProductListRecyclerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
TextView productBrand;
TextView productName;
TextView productCode;
TextView productPrice;
ImageView productThumbnail;
public ProductListRecyclerViewHolder(View itemView) {
super(itemView);
productBrand = (TextView) itemView.findViewById(R.id.product_brand);
productName = (TextView) itemView.findViewById(R.id.product_name);
productCode = (TextView) itemView.findViewById(R.id.product_code);
productPrice = (TextView) itemView.findViewById(R.id.product_price);
productThumbnail = (ImageView) itemView.findViewById(R.id.product_thumbnail);
itemView.setOnClickListener(this);
itemView.setTag(this);
itemView.setOnLongClickListener(this);
}
@Override
public void onClick(View v) {
if(mClickListener!=null){
mClickListener.itemClicked(v, getAdapterPosition());
}
}
@Override
public boolean onLongClick(View v) {
if(mLongClickListener!=null){
mLongClickListener.itemLongClicked(v, getAdapterPosition());
}
return false;
}
}
public interface ClickListener{
void itemClicked(View view, int position);
}
public interface LongClickListener{
void itemLongClicked(View view, int position);
}
}
如果您不更新您的需求,它可能无法正常工作,但必须是很好的示例)
注意
mProductsResultsListAdapter.setClickListener(this);
然后在活动中,您可以通过以下方式捕获点击:
public class ProductListActivity extends ActionBarActivity implements ProductListRecyclerViewAdapter.ClickListener {
//.....
@Override
public void itemClicked(View view, int position) {
}
//.....
}
如果您需要捕获点击,例如在列表中的项目中的特定视图上(例如在视图持有者中的图像上)然后将点击侦听器设置为“this”并将标签设置为此视图和活动,那么您可以获得标签从视图中,你会知道你点击了哪里:)
您的加载来自数据库:
loading = true;
List<Log> newLogs = helper.getLogsRange(loadedEntriesCounter, LOAD_AMOUNT);
loadedEntriesCounter += LOAD_AMOUNT;
logAdapter.logs.addAll(newLogs);
loading = false; // somewhere here
但这不是最终正确的加载数据的方法,如果数据库太大,或者您将进行“重”选择,您可能会卡住主线程,因为您可能会卡住几毫秒。您必须在异步任务中完成所有数据加载,然后在任务完成通知适配器上完成。
在我的项目中,我从 webapi 加载数据,而不是从数据库加载数据,但无论如何必须通过异步任务以相同的方式完成,这是正确的方式)
class LoadNextPageTask extends AsyncTask<Integer, Void, List<ProductModel>> {
int pageToLoad;
public LoadNextPageTask(int pageToLoad){
this.pageToLoad = pageToLoad;
}
@Override
protected List<ProductModel> doInBackground(Integer... params) {
return helper.getLogsRange(loadedEntriesCounter, LOAD_AMOUNT); // here apply page to load??? not sure
}
@Override
protected void onPreExecute() {
mMainLoadingProgress.setVisibility(View.VISIBLE);
loading = true;
}
@Override
protected void onPostExecute(List<ProductModel> newLogs) {
loading = false;
loadedEntriesCounter += LOAD_AMOUNT;
logAdapter.logs.addAll(newLogs);
runOnUiThread(new Runnable() {
@Override
public void run() {
logAdapter.notifyDataSetChanged();
}
});
mMainLoadingProgress.setVisibility(View.GONE);
}
}
以上是异步任务,请根据您的需要更新
然后在数据加载简单的地方开始:
new LoadNextPageTask(pagenumber).execute(); // page is int