我正在尝试将HorizontalListView(http://www.dev-smart.com/archives/34)添加到ListView。 ListView使用适配器动态附加新的HorizontalListView行以进行连续垂直滚动。
它几乎可以工作:问题是当滚动HorizontalListView时,ListView会自动向上或向下滚动并将HorizontalListView放在视口的顶部或底部边缘,具体取决于它最接近的位置。如何阻止HorizontalListView行在滚动时捕捉到位?
我使用HorizontalListView作为我的“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 :(得分:0)
通过将HorizontalListViews附加到ListView,我能够获得我想要的行为。然后我将我的适配器包装起来,创建了带有EndlessAdapter的HorizontalListViews以进行连续滚动:
https://github.com/commonsguy/cwac-endless
我确保在我的适配器的getView方法中回收视图以解决内存问题。但是,如果我的模型层在堆上占用太多空间,我仍然会耗尽内存。
有人知道一个好的框架,当它们离开屏幕时剔除模型对象,并在需要再次显示它们时重新加载它们吗?