http://blog.csdn.net/yujun411522/article/details/46226001
本文出自:【yujun411522的博客】
@Override public View getView( int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from( mContext).inflate(R.layout. listview_item , null ); TextView index = (TextView)view.findViewById(R.id.my_music_listview_item_index ); TextView title = (TextView)view.findViewById(R.id.my_music_listview_item_title ); index.setText( "" +(position+1)); title.setText( mData.get(position)); Log. v( "MyListViewBase", "getView " + position + " ," + convertView+" ," ); return view; }
if (convertView== null ){ //一开始的创建时为空,因为recycler中就没有可以复用的view,这时需要建立新的view对象。 convertView= LayoutInflater.from(mContext).inflate(R.layout. listview_item , null ); } //如果converview是新建的或者是复用的,都要重新渲染view中的数据。 TextView index = (TextView)convertView.findViewById(R.id. my_music_listview_item_index ); TextView title = (TextView)convertView.findViewById(R.id. my_music_listview_item_title ); index.setText( "" +(position)); title.setText( mData.get(position)); return convertView;
if (convertView== null ){ //一开始的创建时为空,因为recycler中就没有可以复用的view,这时需要建立新的view对象。 convertView= LayoutInflater.from( mContext).inflate(R.layout. listview_item , null ); //如果converview是新建的或者是复用的,都要重新渲染view中的数据。 TextView index = (TextView)convertView.findViewById(R.id. my_music_listview_item_index ); TextView title = (TextView)convertView.findViewById(R.id. my_music_listview_item_title ); index.setText( "" +(position)); title.setText( mData.get(position)); } return convertView;
if (convertView== null ){ //一开始的创建时为空,因为recycler中就没有可以复用的view,这时需要建立新的view对象。 convertView= LayoutInflater.from( mContext).inflate(R.layout. listview_item , null ); } //不管converview是新建的或者是复用的,都要重新渲染view中的数据。 TextView index = (TextView)convertView.findViewById(R.id. my_music_listview_item_index ); TextView title = (TextView)convertView.findViewById(R.id. my_music_listview_item_title ); index.setText( "" +(position)); title.setText( mData.get(position)); return convertView;
@Override public View getView( int position, View convertView, ViewGroup parent) { Log. v( "MyListViewBase", "getView " + position + " ," + convertView); ViewHolder holder; if (convertView== null){ convertView= LayoutInflater.from( mContext).inflate(R.layout. listview_item , null ); //通过这个模式,将view中的子view的引用保存起来,下次直接能获得viewHolder对象即可,避免重复调用findViewById方法。 //现在viewHolder就等价于view中的各个子view了。 holder = new ViewHolder(); holder. index = (TextView)convertView.findViewById(R.id. my_music_listview_item_index); holder. title =(TextView)convertView.findViewById(R.id. my_music_listview_item_title); //将viewHolder绑定到view中,使得下次可以通过view获取viewHolder,也就可以操作复用view的子view了 convertView.setTag(holder); } //如果是复用的view,直接取出复用view中的viewHolder,这样就可以直接通过viewHolder来访问子view了。 else { holder = (ViewHolder)convertView.getTag(); } holder. index.setText( "" +(position)); holder. title.setText( mData.get(position)); return convertView; } static class ViewHolder{ TextView index; TextView title; }
CompoundButton.OnCheckedChangeListener changeListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Log. v("MyListViewBase", "setOnCheckedChangeListener:" +position + " ," + isChecked); mCheckStatusData.put(position, isChecked); } }; holder.cb1.setOnCheckedChangeListener(changeListener); if(mCheckStatusData .get(position)){ holder. cb1.setChecked( true); } else{ holder. cb1.setChecked( false); }
public int getViewTypeCount() { return 1; }
/** * 一共有多少个view类型 * */ private int viewTypeCount = 3; /** * 每一个 viewtype对应的值,便于getItemViewType函数返回 * */ private final int viewType1= 0; private final int viewType2=1; private final int viewType3=2; @Override public int getItemViewType(int position) { //一定要注意,这里的返回值在0至getViewTypeCount()-1之间。 if (position % viewTypeCount == 0) { return viewType1 ; } else if (position % viewTypeCount == 1) { return viewType2 ; } else return viewType3 ; } @Override public int getViewTypeCount() { return viewTypeCount ; } 定义了三种viewType,就需要三种ViewHolder: //这里只做演示,所以每一个view中的子view差别不大。 class ViewHolder1 { TextView index; TextView title; } class ViewHolder2 { TextView index; TextView title; } class ViewHolder3 { TextView index; TextView title; }
/** * 一共有多少个view类型 * */ private int viewTypeCount = 3; /** * 每一个 viewtype对应的值,便于getItemViewType函数返回 * */ private final int viewType1= 0; private final int viewType2=1; private final int viewType3=2; @Override public int getItemViewType(int position) { //一定要注意,这里的返回值在0至getViewTypeCount()-1之间。 if (position % viewTypeCount == 0) { return viewType1 ; } else if (position % viewTypeCount == 1) { return viewType2 ; } else return viewType3 ; } @Override public int getViewTypeCount() { return viewTypeCount ; } 定义了三种viewType,就需要三种ViewHolder: //这里只做演示,所以每一个view中的子view差别不大。 class ViewHolder1 { TextView index; TextView title; } class ViewHolder2 { TextView index; TextView title; } class ViewHolder3 { TextView index; TextView title; }
public void setAdapter(ListAdapter apater){ ... //调用AbsListView.RecycleBin中的setViewTypeCount方法 mRecycler.setViewTypeCount(mAdapter.getViewTypeCount()); mAdapter.getViewTypeCount()=3 ... } AbsListView.RecycleBin public void setViewTypeCount( int viewTypeCount) {//viewTypeCount=3 if (viewTypeCount < 1) { throw new IllegalArgumentException( "Can't have a viewTypeCount < 1"); } //noinspection unchecked //创建了一个长度为3的ArrayList<View>[]类型的数组 ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount]; for (int i = 0; i < viewTypeCount; i++) { scrapViews[i] = new ArrayList<View>(); } mViewTypeCount = viewTypeCount; mCurrentScrap = scrapViews[0]; mScrapViews = scrapViews; } AbsListView.RecycleBin.java void addScrapView(View scrap,int position){ AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); if (lp == null ) { return ; } // Don't put header or footer views or views that should be ignored // into the scrap heap int viewType = lp.viewType ; if (mViewTypeCount == 1) { scrap.dispatchStartTemporaryDetach(); mCurrentScrap .add(scrap); } else { scrap.dispatchStartTemporaryDetach(); 正是这一句,导致了异常的发生。 mScrapViews [viewType].add(scrap);//mScrapViews[3]肯定是数字越界了(0-2才是合法的) } }
public abstract class Subject { /** * 保存所有的observer * */ private ArrayList<Observer> observers = new ArrayList<Observer>(); /** * 为subject添加一个observer * */ public void attach(Observer observer) { observers.add(observer); System. out.println(observer+" is attached" ); } /** * 为subject删除一个observer * */ public void detach(Observer observer) { observers.remove(observer); System. out.println(observer+" is detached" ); } public void notifyObserver(String state) { for (Observer temp : observers ) { temp.update(state); } } }
public interface Observer { /** * 定义一个更新自己的方法,当subject发送消息时执行此方法 * @param state 主题的状态变化 * */ public void update(String state); }
public class ConcreteSubject extends Subject { private String state; public String getState() { return state ; } public void change(String newState){ state = newState; System. out.println("主题新状态:" +state ); //状态发生了变化,通知所有观察者更新 super.notifyObserver(state ); } }
public class ConcreteObserver implements Observer { /** * 观察者的状态 * */ private String observerState; /** * 具体的update方法 * */ @Override public void update(String state) { //记录主题的状态 observerState = state; System. out.println(this +" update itself" ); } }main函数:
public static void main(String[] args) { ConcreteSubject concreteSubject = new ConcreteSubject(); ConcreteObserver observer1 = new ConcreteObserver(); ConcreteObserver observer2 = new ConcreteObserver(); ConcreteObserver observer3 = new ConcreteObserver(); ConcreteObserver observer4 = new ConcreteObserver(); //一个subject可以注册多个observer concreteSubject.attach(observer1); concreteSubject.attach(observer2); concreteSubject.attach(observer3); concreteSubject.attach(observer4); concreteSubject.notifyObserver( "新状态"); }
/** * Provides methods for (un)registering arbitrary observers in an ArrayList. */ public abstract class Observable<T> { /** * The list of observers. An observer can be in the list at most once and will never be null. */ protected final ArrayList<T> mObservers = new ArrayList<T>(); /** * Adds an observer to the list. The observer cannot be null and it must not already be registered. * @param observer the observer to register * @throws IllegalArgumentException the observer is null * @throws IllegalStateException the observer is already registered */ public void registerObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { if (mObservers.contains(observer)) { throw new IllegalStateException("Observer " + observer + " is already registered."); } mObservers.add(observer); } } /** * Removes a previously registered observer. The observer must not be null and it must already have been registered. * @param observer the observer to unregister * @throws IllegalArgumentException the observer is null * @throws IllegalStateException the observer is not yet registered */ public void unregisterObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { int index = mObservers.indexOf(observer); if (index == -1) { throw new IllegalStateException("Observer " + observer + " was not registered."); } mObservers.remove(index); } } /** * Remove all registered observer */ public void unregisterAll() { synchronized(mObservers) { mObservers.clear(); } } }
public abstract class DataSetObserver{ /** * This method is called when the entire data set has changed, * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}. */ public void onChanged() { // Do nothing } /** * This method is called when the entire data becomes invalid, * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a * {@link Cursor} . */ public void onInvalidated() { // Do nothing } }
public abstract class DataSetObserver{ /** * This method is called when the entire data set has changed, * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}. */ public void onChanged() { // Do nothing } /** * This method is called when the entire data becomes invalid, * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a * {@link Cursor} . */ public void onInvalidated() { // Do nothing } }
public class AlphabetIndexer extends DataSetObserver{ @Override public void onChanged() { //观察到数据变化,观察者做自己该做的事情 super.onChanged(); mAlphaMap.clear(); } }
class AdapterDataSetObserver extends DataSetObserver { private Parcelable mInstanceState = null; @Override public void onChanged() { mDataChanged = true; mOldItemCount = mItemCount; mItemCount = getAdapter().getCount(); ... // Detect the case where a cursor that was previously invalidated has // been repopulated with new data. if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null && mOldItemCount == 0 && mItemCount > 0) { AdapterView.this.onRestoreInstanceState(mInstanceState); mInstanceState = null; } else { rememberSyncState(); } checkFocus(); requestLayout(); } @Override public void onInvalidated() { mDataChanged = true; ... if (AdapterView.this.getAdapter().hasStableIds()) { // Remember the current state for the case where our hosting activity is being // stopped and later restarted mInstanceState = AdapterView.this.onSaveInstanceState(); } // Data is invalid so we should reset our state mOldItemCount = mItemCount; mItemCount = 0; mSelectedPosition = INVALID_POSITION; mSelectedRowId = INVALID_ROW_ID; mNextSelectedPosition = INVALID_POSITION; mNextSelectedRowId = INVALID_ROW_ID; mNeedSync = false; checkFocus(); requestLayout(); } }
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver { @Override public void onChanged() { super.onChanged(); if (mFastScroller != null) { mFastScroller.onSectionsChanged(); } } @Override public void onInvalidated() { super.onInvalidated(); if (mFastScroller != null) { mFastScroller.onSectionsChanged(); } } }
@Override public void setAdapter(ListAdapter adapter) { if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } ... if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } super.setAdapter(adapter); if (mAdapter != null) { mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); mOldItemCount = mItemCount; mItemCount = mAdapter.getCount(); checkFocus(); // 在这里为BaseAdapter注册一个AdapterDataSetObserver 对象。 所以可以通过notifyDataSetChanged中的方法操作 mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); .. } } else { ... } }
public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable .registerObserver(observer); }
public void notifyDataSetChanged() { mDataSetObservable .notifyChanged(); }
//The data set used to store unused views that should be reused during the next layout to avoid creating new ones final RecycleBin mRecycler = new RecycleBin();//用来存储不再使用且可以重用的的view,避免下一个layout时重新创建一个新的。
The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the start of a layout. By construction, they are displaying current information. At the end of layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that could potentially be used by the adapter to avoid allocating views unnecessarily. recyclebin方便了view的重用,有两级存储:ActiveView和ScrapView。ActiveViews是屏幕上可见的view集合,ScrapViews则是可以被重用的旧view,避免没有必要的分配view。
private View[] mActiveViews=new View[0]: // Views that were on screen at the start of layout. This array is populated at the start of layout, and at the end of layout all view in mActiveViews are moved to mScrapViews. //Views in mActiveViews represent a contiguous range of Views, with position of the first view store in mFirstActivePosition. //一开始layout时在屏幕上的views,结束layout之后所有的view会移动到mScrapViews中。 private ArrayList<View>[] mScrapViews ; // Unsorted views that can be used by the adapter as a convert view. 可以被用来当做convertView的view集合,注意,这里使用的是ArrayList<View>的数组。因为这里可以有多个不同viewType的view,每一种类的view用一个ArrayList保存。 private int mViewTypeCount://view的种类,前面已经介绍过了 private ArrayList<View> mCurrentScrap://一个ArrayList,主要是为了方便当mViewTypeCount==1时的操作,当mViewTypeCount==1时,mCurrentScarp=mScrapViews[0] private int mFirstActivePosition:mActiveViews中第一个view的位置
public void setViewTypeCount(int viewTypeCount) { if (viewTypeCount < 1) { throw new IllegalArgumentException("Can't have a viewTypeCount < 1"); } //noinspection unchecked ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount]; //根据viewTypeCount的值,将mViewTypeCount初始化为长度为viewTypeCount的ArrayList数组 for (int i = 0; i < viewTypeCount; i++) { scrapViews[i] = new ArrayList<View>(); } mViewTypeCount = viewTypeCount; mCurrentScrap = scrapViews[0]; mScrapViews = scrapViews; }
public void markChildrenDirty() { if (mViewTypeCount == 1) { final ArrayList<View> scrap = mCurrentScrap ; final int scrapCount = scrap.size(); for (int i = 0; i < scrapCount; i++) { scrap.get(i).forceLayout(); } } else { final int typeCount = mViewTypeCount; for (int i = 0; i < typeCount; i++) { final ArrayList<View> scrap = mScrapViews [i]; final int scrapCount = scrap.size(); for (int j = 0; j < scrapCount; j++) { //mScrapViews 中的view.forceLayout(),forceLayout此方法只是做一个标示 scrap.get(j).forceLayout(); } } } }
public void forceLayout() { mPrivateFlags |= FORCE_LAYOUT ; mPrivateFlags |= INVALIDATED ; }
/** * Clears the scrap heap. */ //调用将mScrapViews 中所有的view清除,并调用removeDetachedView方法回收 void clear() { if (mViewTypeCount == 1) { final ArrayList<View> scrap = mCurrentScrap ; final int scrapCount = scrap.size(); for (int i = 0; i < scrapCount; i++) { removeDetachedView(scrap.remove(scrapCount - 1 - i), false ); } } else { final int typeCount = mViewTypeCount; for (int i = 0; i < typeCount; i++) { final ArrayList<View> scrap = mScrapViews [i]; final int scrapCount = scrap.size(); for (int j = 0; j < scrapCount; j++) { removeDetachedView(scrap.remove(scrapCount - 1 - j), false ); } } } }
// Fill ActiveViews with all of the children of the AbsListView. 用ABSListView的所有child填充ActiveViews void fillActiveViews(int childCount, int firstActivePosition) { if (mActiveViews .length < childCount) { mActiveViews = new View[childCount]; } mFirstActivePosition = firstActivePosition; final View[] activeViews = mActiveViews ; for (int i = 0; i < childCount; i++) { View child = getChildAt(i); AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); // Don't put header or footer views into the scrap heap if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER ) { // Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views. // However, we will NOT place them into scrap views. activeViews[i] = child;// 用ABSListView的所有child填充ActiveViews } } }
//Get the view corresponding to the specified position. The view will be removed from mActiveViews if it is found. 根据position返回mActiveViews中的view,如果找到,将该view从mActiveViews中移除。如果没找到return null View getActiveView( int position) { int index = position - mFirstActivePosition ; final View[] activeViews = mActiveViews ; if (index >=0 && index < activeViews. length) { final View match = activeViews[index]; activeViews[index] = null ;//将该view从mActiveViews中移除,对应的位置设为null return match; } return null; }
// @return A view from the ScrapViews collection. These are unordered. View getScrapView( int position) { if (mViewTypeCount == 1) { return retrieveFromScrap( mCurrentScrap , position); } else { int whichScrap = mAdapter .getItemViewType(position); if (whichScrap >= 0 && whichScrap < mScrapViews .length ) { return retrieveFromScrap( mScrapViews[whichScrap], position); } } return null; }
static View retrieveFromScrap(ArrayList<View> scrapViews, int position) { int size = scrapViews.size(); if (size > 0) { // See if we still have a view for this position. for (int i=0; i<size; i++) { View view = scrapViews.get(i); if (((AbsListView.LayoutParams)view.getLayoutParams()). scrappedFromPosition == position) { scrapViews.remove(i); // 一种情况:如果在mScrapViews中一个scarppedFromPosition和position一样返回的view,返回且从mScrapViews中删除该view。 return view; } } // 第二种情况:如果在mScrapViews中找不到scarppedFromPosition==position的view,返回所在type的ArrayList最后一个View并删除该view return scrapViews.remove(size - 1); } else { // 第三种情况:如果该type 的scrapView size为0,返回null。 return null ; } }
void addScrapView(View scrap, int position) { AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); .. int viewType = lp.viewType ; //将position赋值给 scrappedFromPosition属性 lp.scrappedFromPosition= position; if (mViewTypeCount == 1) { scrap.dispatchStartTemporaryDetach(); mCurrentScrap .add(scrap); } else { scrap.dispatchStartTemporaryDetach(); //添加到该type的ArrayList缓存数据中。 //这个地方也是之前发生数组越界的根源。 mScrapViews [viewType].add(scrap); } }
protected void layoutChildren(){ ... final RecycleBin recycleBin = mRecycler; if (dataChanged) { for (int i = 0; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); } } else { recycleBin.fillActiveViews(childCount, firstPosition); } }
private void scrollListItemsBy( int amount) { final int listBottom = getHeight() - mListPadding.bottom; final int listTop = mListPadding.top; final AbsListView.RecycleBin recycleBin = mRecycler; if (amount < 0) { .. // top views may be panned off screen View first = getChildAt(0); while (first.getBottom() < listTop) { //顶端item滚出屏幕时加入到scrapView中 AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams(); if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) { detachViewFromParent(first); recycleBin.addScrapView(first, mFirstPosition); } else { removeViewInLayout(first); } first = getChildAt(0); mFirstPosition++; } } else { // bottom view may be panned off screen while (last.getTop() > listBottom) { //底端item滚出屏幕时加入到scrapView中 AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams(); if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) { detachViewFromParent(last); recycleBin.addScrapView(last, mFirstPosition+lastIndex); } else { removeViewInLayout(last); } last = getChildAt(--lastIndex); } } }
private View makeAndAddView( int position, int y, boolean flow, int childrenLeft, boolean selected) { View child; if (!mDataChanged) { // Try to use an existing view for this position child = mRecycler.getActiveView(position); if (child != null) { setupChild(child, position, y, flow, childrenLeft, selected, true ); return child; } } // Make a new view for this position, or convert an unused view if possible child = obtainView(position, mIsScrap); setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; }
// Get a view and have it show the data associated with the specified position. This is called when we have already discovered that the view is not available for reuse in the recycle bin. The only choices left are // converting an old view or making a new one. View obtainView( int position, boolean [] isScrap) { isScrap[0] = false ; View scrapView; scrapView = mRecycler .getScrapView(position); View child; if (scrapView != null) { ... // 如果从缓存view中取出来的view不为空,说可以拿到一个可以复用的view:scrapView 这时convertView的值就是scrapView child = mAdapter.getView(position, scrapView, this ); .. if (child != scrapView) { // 如果getView中convertView的值与getView的返回值不一样,依然将scrapView加入scrapView中。 mRecycler .addScrapView(scrapView, position); .. } else { getView中convertView的值与getView的返回值一样,(convertView!=null我们通常都是返回convertView),设置 isScrap[0] = true ; isScrap[0] = true ; child.dispatchFinishTemporaryDetach(); } } else { 如果在scarpView中没有找到可回收的,则convertView ==null,这时需要自己创建view。 child = mAdapter.getView(position, null , this ); } return child; }
public interface OnScrollListener { /** * The view is not scrolling. */ //view此时没有滚动 public static int SCROLL_STATE_IDLE = 0; /** * The user is scrolling using touch, and their finger is still on the screen */ //view在滚动,且手指在还在屏幕上 public static int SCROLL_STATE_TOUCH_SCROLL = 1; /** * The user had previously been scrolling using touch and had performed a fling. */ //view在滚动,但是手指在还在屏幕。用力一划,迅速离开屏幕 public static int SCROLL_STATE_FLING = 2; /** * Callback method to be invoked while the list view or grid view is being scrolled. If the * view is being scrolled, this method will be called before the next frame of the scroll is * rendered. In particular, it will be called before any calls to * {@link Adapter#getView(int, View, ViewGroup)} . * * @param view The view whose scroll state is being reported //是哪一个View的scrollState发生了变化 * * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE} , * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}. */该view对应的scrollState //view的ScrollState状态发生变化时调用。在getView方法之前调用 public void onScrollStateChanged(AbsListView view, int scrollState); /** * @param view The view whose scroll state is being reported//同上 * @param firstVisibleItem the index of the first visible cell // 第一个可见的Item的index * @param visibleItemCount the number of visible cells//屏幕上可见的item个数 * @param totalItemCount the number of items in the list adaptor//adapter中的item数量,getCount值 */ //view在Scroll调用 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount); }
private OnScrollListener onScrollListener = new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (view.getId() == R.id.lv1) { //滑到底端时开启一个异步任务加载数据.. if(firstVisibleItem+visibleItemCount==totalItemCount){ new LoadMoreDataTask().execute(); } } } };
listview.setOnScrollListener(onScrollListener);
class LoadMoreDataTask extends AsyncTask<Void, Void, Void> { @Override protected void onPreExecute() { progressDialog.show();//给用户提示正在加载数据 } @Override protected Void doInBackground(Void... params) { getMoreData(); try { Thread. sleep(2000);//模拟耗时操作 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null ; } @Override protected void onPostExecute(Void result) { if (progressDialog != null) { progressDialog.dismiss(); } //更新adapter数据源,并提示数据集发生变化,观察者更新数据 adapter.setData(data); adapter.notifyDataSetChanged(); } }