2013-03-03 58 views
1

我想将一个Horizo​​ntalListView(http://www.dev-smart.com/archives/34)添加到一个ListView。 ListView使用适配器来动态附加新的Horizo​​ntalListView行以进行连续垂直滚动。ListView内的Horizo​​ntalListView

它几乎可以工作:问题是,当Horizo​​ntalListView滚动时,ListView将自动向上或向下滚动,并将Horizo​​ntalListView放置在视口的顶部或底部边缘,具体取决于它最接近哪个边缘。如何停止Horizo​​ntalListView行在滚动时捕捉到位?

我用Horizo​​ntalListView作为我的“VerticalScrollView”参考:

public class VerticalScrollView extends ListView { 

public static interface ScrollListener { 
    void onScrollEnd(); 
} 

public VerticalScrollView(Context context) { 
    super(context); 
    initView(); 
} 

public VerticalScrollView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    initView(); 
} 

private synchronized void initView() { 
    mTopViewIndex = -1; 
    mBottomViewIndex = 0; 
    mDisplayOffset = 0; 
    mCurrentY = 0; 
    mNextY = 0; 
    mMaxY = Integer.MAX_VALUE; 
    mScroller = new Scroller(getContext()); 
    setFadingEdgeLength(0); 
    setItemsCanFocus(false); 
} 

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    switch (ev.getAction()) { 
    case MotionEvent.ACTION_DOWN: 
     mXDistance = mYDistance = 0f; 
     mLastX = ev.getX(); 
     mLastY = ev.getY(); 
     break; 
    case MotionEvent.ACTION_MOVE: 
     final float curX = ev.getX(); 
     final float curY = ev.getY(); 
     mXDistance += Math.abs(curX - mLastX); 
     mYDistance += Math.abs(curY - mLastY); 
     mLastX = curX; 
     mLastY = curY; 
     if(mXDistance > mYDistance) { 
      return false; 
     } 
    } 
    return super.onInterceptTouchEvent(ev); 
} 

@Override 
public ListAdapter getAdapter() { 
    return mAdapter; 
} 

@Override 
public View getSelectedView() { 
    //TODO: implement 
    return null; 
} 

@Override 
public void setAdapter(ListAdapter adapter) { 
    if(mAdapter != null) { 
     mAdapter.unregisterDataSetObserver(mDataObserver); 
    } 
    mAdapter = adapter; 
    mAdapter.registerDataSetObserver(mDataObserver); 
    reset(); 
    super.setAdapter(adapter); 
} 

private synchronized void reset(){ 
    initView(); 
    removeAllViewsInLayout(); 
    requestLayout(); 
} 

@Override 
public void setSelection(int position) { 
    //TODO: implement 
} 

private void addAndMeasureChild(final View child, int viewPos) { 
    android.view.ViewGroup.LayoutParams params = child.getLayoutParams(); 
    if(params == null) { 
     params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
    } 

    addViewInLayout(child, viewPos, params, true); 
    child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), 
      MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); 
} 

@Override 
protected synchronized void onLayout(
     boolean changed, 
     int left, 
     int top, 
     int right, 
     int bottom) { 
    super.onLayout(changed, left, top, right, bottom); 

    if(mAdapter == null){ 
     return; 
    } 

    if(mDataChanged){ 
     int oldCurrentY = mCurrentY; 
     initView(); 
     removeAllViewsInLayout(); 
     mNextY = oldCurrentY; 
     mDataChanged = false; 
    } 

    if(mScroller.computeScrollOffset()){ 
     int scrollY = mScroller.getCurrY(); 
     mNextY = scrollY; 
    } 

    if(mNextY <= 0){ 
     mNextY = 0; 
     mScroller.forceFinished(true); 
    } 
    if(mNextY >= mMaxY) { 
     mNextY = mMaxY; 
     mScroller.forceFinished(true); 
    } 

    int dy = mCurrentY - mNextY; 

    removeNonVisibleItems(dy); 
    fillList(dy); 
    positionItems(dy); 

    mCurrentY = mNextY; 

    if(!mScroller.isFinished()){ 
     post(mRequestLayoutRunnable); 
    } 
} 

private void fillList(final int dy) { 
    int edge = 0; 
    View child = getChildAt(getChildCount()-1); 
    if(child != null) { 
     edge = child.getBottom(); 
    } 
    fillListBottom(edge, dy); 

    edge = 0; 
    child = getChildAt(0); 
    if(child != null) { 
     edge = child.getTop(); 
    } 
    fillListTop(edge, dy); 
} 

private void fillListBottom(int bottomEdge, final int dy) { 
    while(bottomEdge + dy < getHeight() && mBottomViewIndex < mAdapter.getCount()) { 

     View child = mAdapter.getView(mBottomViewIndex, mRemovedViewQueue.poll(), this); 
     addAndMeasureChild(child, -1); 
     bottomEdge += child.getMeasuredHeight(); 

     if(mBottomViewIndex == mAdapter.getCount()-1) { 
      mMaxY = mCurrentY + bottomEdge - getHeight(); 
     } 

     if (mMaxY < 0) { 
      mMaxY = 0; 
     } 
     mBottomViewIndex++; 
    } 
} 

private void fillListTop(int topEdge, final int dy) { 
    while(topEdge + dy > 0 && mTopViewIndex >= 0) { 
     View child = mAdapter.getView(mTopViewIndex, mRemovedViewQueue.poll(), this); 
     addAndMeasureChild(child, 0); 
     topEdge -= child.getMeasuredHeight(); 
     mTopViewIndex--; 
     mDisplayOffset -= child.getMeasuredHeight(); 
    } 
} 

private void removeNonVisibleItems(final int dy) { 
    View child = getChildAt(0); 
    while(child != null && child.getBottom() + dy <= 0) { 
     mDisplayOffset += child.getMeasuredHeight(); 
     mRemovedViewQueue.offer(child); 
     removeViewInLayout(child); 
     mTopViewIndex++; 
     child = getChildAt(0); 

    } 

    child = getChildAt(getChildCount()-1); 
    while(child != null && child.getLeft() + dy >= getHeight()) { 
     mRemovedViewQueue.offer(child); 
     removeViewInLayout(child); 
     mBottomViewIndex--; 
     child = getChildAt(getChildCount()-1); 
    } 
} 

private void positionItems(final int dy) { 
    if(getChildCount() > 0){ 
     mDisplayOffset += dy; 
     int top = mDisplayOffset; 
     for(int i=0;i<getChildCount();i++){ 
      View child = getChildAt(i); 
      int childHeight = child.getMeasuredHeight(); 
      child.layout(0, top, child.getMeasuredWidth(), top + childHeight); 
      top += childHeight + child.getPaddingBottom(); 
     } 
    } 
} 

public synchronized void scrollTo(int y) { 
    mScroller.startScroll(0, mNextY, 0, y - mNextY); 
    requestLayout(); 
} 

public boolean mAlwaysOverrideTouch = true; 
protected ListAdapter mAdapter; 
private int mTopViewIndex = -1; 
private int mBottomViewIndex = 0; 
protected int mCurrentY; 
protected int mNextY; 
private int mMaxY = Integer.MAX_VALUE; 
private int mDisplayOffset = 0; 
protected Scroller mScroller; 
private Queue<View> mRemovedViewQueue = new LinkedList<View>(); 
private boolean mDataChanged = false; 
private float mXDistance; 
private float mYDistance; 
private float mLastX; 
private float mLastY; 

private Runnable mRequestLayoutRunnable = new Runnable(){ 

    @Override 
    public void run() { 
     requestLayout(); 
    } 
}; 

private DataSetObserver mDataObserver = new DataSetObserver() { 

    @Override 
    public void onChanged() { 
     synchronized(VerticalScrollView.this){ 
      mDataChanged = true; 
     } 
     invalidate(); 
     requestLayout(); 
    } 

    @Override 
    public void onInvalidated() { 
     reset(); 
     invalidate(); 
     requestLayout(); 
    } 
}; 

编辑:我能得到这个用普通的ListView,而不是我的自定义VerticalScrollView很好地工作。我将在用户向下滚动时从网络填充ListView。我如何管理列表项视图,以便不会耗尽内存?

回答

0

我能够通过将Horizo​​ntalListViews附加到ListView来获得我想要的行为。然后我包裹着我的适配器,用于连续滚动的EndlessAdapter创建Horizo​​ntalListViews:

https://github.com/commonsguy/cwac-endless

我确信回收在我的适配器的getView方法的观点来解决内存问题。尽管如此,如果我的模型层在堆上占用太多空间,我仍然可能会耗尽内存。

有人知道一个好的框架,当他们离开屏幕时剔除模型对象,并重新加载它们,当他们需要再次显示?

相关问题