1

Horizo​​ntalListView ( http://www.dev-smart.com/archives/34 ) を ListViewに追加しようとしています。ListView はアダプターを使用して、継続的な垂直スクロールのために新しい Horizo​​ntalListView 行を動的にアタッチします。

ほとんど機能します: 問題は、Horizo​​ntalListView がスクロールされると、ListView が自動的に上下にスクロールし、Horizo​​ntalListView がどちらに近いかに応じて、ビューポートの上端または下端に配置されることです。スクロール時に Horizo​​ntalListView 行が所定の位置に収まらないようにするにはどうすればよいですか?

「VerticalScrollView」のリファレンスとして Horizo​​ntalListView を使用しました。

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

編集: カスタムの VerticalScrollView の代わりにプレーンな ListView を使用することで、これを完全に機能させることができます。ユーザーが下にスクロールすると、ネットワークから ListView に値が入力されます。メモリ不足にならないようにリスト アイテム ビューを管理するにはどうすればよいですか?

4

1 に答える 1

0

Horizo​​ntalListViews を ListView にアタッチすることで、必要な動作を得ることができました。次に、Horizo​​ntalListViews を作成するアダプターを EndlessAdapter でラップして、連続スクロールを行います。

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

メモリの問題に対処するために、アダプターの getView メソッドでビューをリサイクルするようにしました。ただし、モデル レイヤーがヒープのスペースを取りすぎると、メモリ不足になる可能性があります。

モデルオブジェクトが画面から消えたときにカリングし、再度表示する必要があるときに再読み込みするための優れたフレームワークを知っている人はいますか?

于 2013-03-03T19:26:01.860 に答える