Android LoadMore-ListView [英] Android LoadMore-ListView

查看:92
本文介绍了Android LoadMore-ListView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个内部数据库(SQLite)有很多条目。所以我决定在用户​​开始活动时加载列表视图中的前20个条目,当他向下滚动时,如果还有条目,按下一个按钮可以再加载20个条目。



EDIT



  // onCreate()
acceptedLogs = helper.getLogsRange (0,LOAD_AMOUNT);
loadedEntriesCounter = LOAD_AMOUNT;

logAdapter = new LogAdapter(acceptedLogs);
logRecyclerView.setAdapter(logAdapter);
logRecyclerView.setHasFixedSize(false);
linearLayoutManager = new LinearLayoutManager(getContext());
logRecyclerView.setLayoutManager(linearLayoutManager);




@Override
public void onScrolled(RecyclerView recyclerView,int dx,int dy){
super.onScrolled(recyclerView, dx,dy);
visibleItemCount = logRecyclerView.getChildCount();
totalItemCount = logRecyclerView.getLayoutManager()。getItemCount();
int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
if(loading){//布尔值设置为true如果你已经加载,并且你将更新适配器后为false
if(totalItemCount> previousTotalCount){
previousTotalCount = totalItemCount;
}
}
if(!loading&&(totalItemCount - visibleItemCount)< =(firstVisibleItemPosition + VISIBLE_THRESHOLD)){
loading = true;
List< Log> newLogs = helper.getLogsRange(loadedEntriesCounter,LOAD_AMOUNT);
loadedEntriesCounter + = LOAD_AMOUNT;
logAdapter.logs.addAll(newLogs);
logAdapter.notifyDataSetChanged();

}
}

解决方案

首先,使用RecyclerView和viewholder,你最好在滚动时加载数据监听器和一些技巧,例如你加载了20项,然后当你滚动列表将有好的加载数据,当你滚动列表,你滚动到例如18项目在底部,这里你开始你的异步任务,当你滚动到你的加载将完成,你将更新列表与20更多,所以用户甚至不会看到什么时候你正在加载。

  mVisibleThreshold = 2 //这是在加载将开始时将项目计数到当前列表的底部。 

这里是我的滚动监听器为recyclerview(可以调整到你的listview):

  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){//布尔值设置为true,如果你已经加载和false后你将更新适配器
if(mTotalItemCount> mPreviousTotal){

mPreviousTotal = mTotalItemCount;
}
}
if(!mLoadingInProgress&&(mTotalItemCount - mVisibleItemCount)
< =(mFirstVisibleItem + mVisibleThreshold)){
mLoadingInProgress = true;
mLastPageRequest ++; //当前页面加载和添加
//这里你加载数据和更新适配器
//如果在异步任务中,然后在这里启动它并设置mLoadingInProgress为true和onPostExecute添加到列表结果,并使notifyDatasetChanged on适配器然后将mLoadingInProgress更改为false。


}
}
});

一个:在后台任务中不要触摸任何视图(否则会卡住主线程)使用RunOnUIThread在onPostExecute中执行任务代码后的所有更新和通知。



好,因为您对此解决方案感兴趣将改善答案:



mLastPageRequest - int我用来知道我加载了哪个页面,并且下一次它必须加载它在加载数据后每次增加1加上++



(从数据库或从Web请求,您应该将下载的项目添加到您的列表)。
目前在我的项目我的列表是mCategoryProducts,这是我的适配器mProductsResultsListAdapter。



所有U需要是添加所有下载的项目,附加到适配器与

  mCategoryProducts.addAll(downloadedListProducts); //这里你正在添加你刚刚加载到现有列表的项目到最后,

现在下一个代码:

  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;

//用于动画
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(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实现View.OnClickListener,View.OnLongClickListener {
TextView productBrand;
TextView productName;
TextView productCode;
TextView productPrice;
ImageView productThumbnail;
public ProductListRecyclerViewHolder(查看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并将标记设置为此视图,并且在活动中,您可以从视图中获取标记,您将知道您点击的位置:)

您的加载来自数据库: / p>

  loading = true; 
List< Log> newLogs = helper.getLogsRange(loadedEntriesCounter,LOAD_AMOUNT);
loadedEntriesCounter + = LOAD_AMOUNT;
logAdapter.logs.addAll(newLogs);
loading = false; //这里的某处

但是这不是最终正确的方式加载数据你可以卡住主线程coz if数据库会太大,或者你会做重选择,你可以卡住几毫秒。您必须在异步任务中执行所有数据加载,然后在任务完成通知适配器上执行。



在我的项目中从webapi加载数据,而不是从数据库加载数据,以同样的方式通过异步任务,这是正确的方式)

 类LoadNextPageTask扩展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); //这里应用页面加载???不确定
}

@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 


I have an internal-database(SQLite) with many entries. So I decided to load the first 20 entries in the listview when the user starts the activity and when he scrolls down he can load 20 more each time pressing a button if there are entries left.

EDIT

//onCreate()
acceptedLogs = helper.getLogsRange(0, LOAD_AMOUNT);
        loadedEntriesCounter = LOAD_AMOUNT;

        logAdapter = new LogAdapter(acceptedLogs);
        logRecyclerView.setAdapter(logAdapter);
        logRecyclerView.setHasFixedSize(false);
        linearLayoutManager = new LinearLayoutManager(getContext());
        logRecyclerView.setLayoutManager(linearLayoutManager);




@Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                visibleItemCount = logRecyclerView.getChildCount();
                totalItemCount = logRecyclerView.getLayoutManager().getItemCount();
                int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
                if (loading) { // boolean set to true if you are already loading and false after you will update adaptor
                    if (totalItemCount > previousTotalCount) {
                        previousTotalCount = totalItemCount;
                    }
                }
                if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItemPosition + VISIBLE_THRESHOLD)) {
                    loading = true;
                    List<Log> newLogs = helper.getLogsRange(loadedEntriesCounter, LOAD_AMOUNT);
                    loadedEntriesCounter += LOAD_AMOUNT;
                    logAdapter.logs.addAll(newLogs);
                    logAdapter.notifyDataSetChanged();

                }
            }

The loading only happens ones! Where do I have to set loading on false?

解决方案

First, better use RecyclerView with viewholder, second you better load data on scroll listener and some trick, for example you have loaded 20 items then when you scroll list there will be good to load data when you scroll list and you scrolled to for example to 18 item at bottom, here you start your async task and when you scroll to 20 your loading will finish and you will update list with 20 more so user will not even see when you are loading.

mVisibleThreshold = 2 // this is count items to bottom of current list when load will start.

here is my on scroll listener for recyclerview (can be adjusted to your listview):

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.


                }
            }
        });

One more: don't touch any views in background tasks (otherwise you will stuck main thread), make all updates and notifications after task code in onPostExecute with RunOnUIThread.

okay as you are interested in this solution will improve answer:

mLastPageRequest - int I use to know which page I was loaded and which i must load next it's incrementing each time by 1 with ++

after loading data (from database or from web request you should add downloaded items to your list). Currently in my project my list is "mCategoryProducts" and this is my adapter mProductsResultsListAdapter.

all U need is to add all next items you downloaded to list you attached to adapter with

mCategoryProducts.addAll(downloadedListProducts); // here you are adding items you just loaded to existing list to the end, 

now next code:

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);
        }
    }

Here if adapter not already initialised then we create it and attaching to it our list mCategoryProducts otherwise if this is second loading then we simple notify adapter that "Hey man new data is comming :)" with:

mProductsResultsListAdapter.notifyDataSetChanged();

Notes: mCategoryIsEmptyInfo - is view which i show when there is no items to display.

Here you are my custom adaptor with interface for clicks that can be handled in activity )

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);
    }
}

it will probably not work without you update to your needs, but nice sample how must be )

Make attention on

mProductsResultsListAdapter.setClickListener(this);

then in activity you can catch clicks with :

public class ProductListActivity extends ActionBarActivity implements ProductListRecyclerViewAdapter.ClickListener {

    //.....

    @Override
    public void itemClicked(View view, int position) {

    }

    //.....
}

One more thing if you need to catch click for example on specific view in item in list for example on image in view holder then set click listener to "this" and set tag to this view and in activity then you can get tag from view and you will know where you clicked :)

your loading is from database:

 loading = true;
                    List<Log> newLogs = helper.getLogsRange(loadedEntriesCounter, LOAD_AMOUNT);
                    loadedEntriesCounter += LOAD_AMOUNT;
                    logAdapter.logs.addAll(newLogs);
 loading = false; // somewhere here 

but this is not finally correct way to load data you can stuck main thread coz if database will be too big or you will make "heavy" selection you can get stuck for several milisec. you must do all data loading in async tasks and then on task done notify adapter.

in my project i load data from webapi, not from database but in anyway must be done in same way via async task, this is proper way )

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);

        }
    }

above is async task please update to your needs then where data loading simple start it with:

new LoadNextPageTask(pagenumber).execute(); // page is int

这篇关于Android LoadMore-ListView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆