android - 拖放 View 的 ListView

标签 android listview android-listview drag-and-drop

您好,我有 Listview,其中每个项目都有不同的布局。我想在这个 listview 上执行拖放操作。我已经搜索了很多示例并尝试过,但所有都适用于字符串 ListView 或类似的东西,没有一个在工作在 View 的 ListView 上。最后我决定去 DevBytes: ListView Cell Dragging and Rearranging this one.i implemented dynaliclistview 但它正在崩溃,因为这也在 listview 中使用字符串。以下是我的 listview 适配器

public class ListViewPagerAdapter extends BaseAdapter {

ViewPagerAdapter mViewPagerAdapter;
private Context context;
private int selectedIndex;
  FragmentManager mFragmentManager;
  static ViewPager vp;
  LayoutInflater inflater;
  private ArrayList<Integer> mContent;
public ListViewPagerAdapter(Context context,FragmentManager fg) 
{
    super();
    this.context = context;
    mFragmentManager = fg;
}

@Override
public int getCount() {

    return 4;
}

public void setSelectedIndex(int position) {
    selectedIndex = position;
    notifyDataSetChanged();
}



@Override
public long getItemId(int position) {

    return position;
}



@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    if (convertView == null) {

         inflater = (LayoutInflater) context
                .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        if(position==0)
        {
            convertView = inflater.inflate(R.layout.titlebar, null);
        }

    }
     if(position==0)
     {
        convertView = inflater.inflate(R.layout.titlebar, null);
     }
    if(position==1)
    {LayoutInflater inflater1 = (LayoutInflater) context
    .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        convertView = inflater1.inflate(R.layout.calendarwidget_layout, null);

    }
     if(position==2)
     {
         LayoutInflater inflater2 = (LayoutInflater) context
                    .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
     convertView = inflater2.inflate(R.layout.view_pager_list_view, null);
     vp = (ViewPager) convertView.findViewById(R.id.list_pager);  


     mViewPagerAdapter = new ViewPagerAdapter(mFragmentManager);
     vp.setAdapter(mViewPagerAdapter);

     }
     if(position==3)
     {
         LayoutInflater inflater2 = (LayoutInflater) context
                    .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
     convertView = inflater2.inflate(R.layout.view_pager_list_view, null);
     vp = (ViewPager) convertView.findViewById(R.id.list_pager);  


     mViewPagerAdapter = new ViewPagerAdapter(mFragmentManager);
     vp.setAdapter(mViewPagerAdapter);
     mViewPagerAdapter.notifyDataSetChanged();


     }


    return convertView;
}

@Override
public Object getItem(int position) {
    // TODO Auto-generated method stub
    return null;
}
}

以下是 dynamiclisview 的代码(与上述示例相同)

public class DynamicListView extends ListView {

private final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 15;
private final int MOVE_DURATION = 150;
private final int LINE_THICKNESS = 15;

public ArrayList<String> mCheeseList;

private int mLastEventY = -1;

private int mDownY = -1;
private int mDownX = -1;

private int mTotalOffset = 0;

private boolean mCellIsMobile = false;
private boolean mIsMobileScrolling = false;
private int mSmoothScrollAmountAtEdge = 0;

private final int INVALID_ID = -1;
private long mAboveItemId = INVALID_ID;
private long mMobileItemId = INVALID_ID;
private long mBelowItemId = INVALID_ID;

private BitmapDrawable mHoverCell;
private Rect mHoverCellCurrentBounds;
private Rect mHoverCellOriginalBounds;

private final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;

private boolean mIsWaitingForScrollFinish = false;
private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;

public DynamicListView(Context context) {
    super(context);
    init(context);
}

public DynamicListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(context);
}

public DynamicListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

public void init(Context context) {
    setOnItemLongClickListener(mOnItemLongClickListener);
    setOnScrollListener(mScrollListener);
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    mSmoothScrollAmountAtEdge = (int)(SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
}

/**
 * Listens for long clicks on any items in the listview. When a cell has
 * been selected, the hover cell is created and set up.
 */
private AdapterView.OnItemLongClickListener mOnItemLongClickListener =
        new AdapterView.OnItemLongClickListener() {
            public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int pos, long id) {
                mTotalOffset = 0;

                int position = pointToPosition(mDownX, mDownY);
                int itemNum = position - getFirstVisiblePosition();

                View selectedView = getChildAt(itemNum);
                mMobileItemId = getAdapter().getItemId(position);
                mHoverCell = getAndAddHoverView(selectedView);
                selectedView.setVisibility(INVISIBLE);

                mCellIsMobile = true;

                updateNeighborViewsForID(mMobileItemId);

                return true;
            }
        };

/**
 * Creates the hover cell with the appropriate bitmap and of appropriate
 * size. The hover cell's BitmapDrawable is drawn on top of the bitmap every
 * single time an invalidate call is made.
 */
private BitmapDrawable getAndAddHoverView(View v) {

    int w = v.getWidth();
    int h = v.getHeight();
    int top = v.getTop();
    int left = v.getLeft();

    Bitmap b = getBitmapWithBorder(v);

    BitmapDrawable drawable = new BitmapDrawable(getResources(), b);

    mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);
    mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);

    drawable.setBounds(mHoverCellCurrentBounds);

    return drawable;
}

/** Draws a black border over the screenshot of the view passed in. */
private Bitmap getBitmapWithBorder(View v) {
    Bitmap bitmap = getBitmapFromView(v);
    Canvas can = new Canvas(bitmap);

    Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

    Paint paint = new Paint();
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(LINE_THICKNESS);
    paint.setColor(Color.BLACK);

    can.drawBitmap(bitmap, 0, 0, null);
    can.drawRect(rect, paint);

    return bitmap;
}

/** Returns a bitmap showing a screenshot of the view passed in. */
private Bitmap getBitmapFromView(View v) {
    Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas (bitmap);
    v.draw(canvas);
    return bitmap;
}

/**
 * Stores a reference to the views above and below the item currently
 * corresponding to the hover cell. It is important to note that if this
 * item is either at the top or bottom of the list, mAboveItemId or mBelowItemId
 * may be invalid.
 */
private void updateNeighborViewsForID(long itemID) {
    int position = getPositionForID(itemID);
    ListViewPagerAdapter adapter = ((ListViewPagerAdapter)getAdapter());
    mAboveItemId = adapter.getItemId(position - 1);
    mBelowItemId = adapter.getItemId(position + 1);
}

/** Retrieves the view in the list corresponding to itemID */
public View getViewForID (long itemID) {
    int firstVisiblePosition = getFirstVisiblePosition();
    ListViewPagerAdapter adapter = ((ListViewPagerAdapter)getAdapter());
    for(int i = 0; i < getChildCount(); i++) {
        View v = getChildAt(i);
        int position = firstVisiblePosition + i;
        long id = adapter.getItemId(position);
        if (id == itemID) {
            return v;
        }
    }
    return null;
}

/** Retrieves the position in the list corresponding to itemID */
public int getPositionForID (long itemID) {
    View v = getViewForID(itemID);
    if (v == null) {
        return -1;
    } else {
        return getPositionForView(v);
    }
}

/**
 *  dispatchDraw gets invoked when all the child views are about to be drawn.
 *  By overriding this method, the hover cell (BitmapDrawable) can be drawn
 *  over the listview's items whenever the listview is redrawn.
 */
@Override
protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (mHoverCell != null) {
        mHoverCell.draw(canvas);
    }
}

@Override
public boolean onTouchEvent (MotionEvent event) {

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mDownX = (int)event.getX();
            mDownY = (int)event.getY();
            mActivePointerId = event.getPointerId(0);
            break;
        case MotionEvent.ACTION_MOVE:
            if (mActivePointerId == INVALID_POINTER_ID) {
                break;
            }

            int pointerIndex = event.findPointerIndex(mActivePointerId);

            mLastEventY = (int) event.getY(pointerIndex);
            int deltaY = mLastEventY - mDownY;

            if (mCellIsMobile) {
                mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left,
                        mHoverCellOriginalBounds.top + deltaY + mTotalOffset);
                mHoverCell.setBounds(mHoverCellCurrentBounds);
                invalidate();

                handleCellSwitch();

                mIsMobileScrolling = false;
                handleMobileCellScroll();

                return false;
            }
            break;
        case MotionEvent.ACTION_UP:
            touchEventsEnded();
            break;
        case MotionEvent.ACTION_CANCEL:
            touchEventsCancelled();
            break;
        case MotionEvent.ACTION_POINTER_UP:
            /* If a multitouch event took place and the original touch dictating
             * the movement of the hover cell has ended, then the dragging event
             * ends and the hover cell is animated to its corresponding position
             * in the listview. */
            pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
                    MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = event.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
                touchEventsEnded();
            }
            break;
        default:
            break;
    }

    return super.onTouchEvent(event);
}

/**
 * This method determines whether the hover cell has been shifted far enough
 * to invoke a cell swap. If so, then the respective cell swap candidate is
 * determined and the data set is changed. Upon posting a notification of the
 * data set change, a layout is invoked to place the cells in the right place.
 * Using a ViewTreeObserver and a corresponding OnPreDrawListener, we can
 * offset the cell being swapped to where it previously was and then animate it to
 * its new position.
 */
private void handleCellSwitch() {
    final int deltaY = mLastEventY - mDownY;
    int deltaYTotal = mHoverCellOriginalBounds.top + mTotalOffset + deltaY;

    View belowView = getViewForID(mBelowItemId);
    View mobileView = getViewForID(mMobileItemId);
    View aboveView = getViewForID(mAboveItemId);

    boolean isBelow = (belowView != null) && (deltaYTotal > belowView.getTop());
    boolean isAbove = (aboveView != null) && (deltaYTotal < aboveView.getTop());

    if (isBelow || isAbove) {

        final long switchItemID = isBelow ? mBelowItemId : mAboveItemId;
        View switchView = isBelow ? belowView : aboveView;
        final int originalItem = getPositionForView(mobileView);

        if (switchView == null) {
            updateNeighborViewsForID(mMobileItemId);
            return;
        }

        swapElements(mCheeseList, originalItem, getPositionForView(switchView));

        ((BaseAdapter) getAdapter()).notifyDataSetChanged();

        mDownY = mLastEventY;

        final int switchViewStartTop = switchView.getTop();

        mobileView.setVisibility(View.VISIBLE);
        switchView.setVisibility(View.INVISIBLE);

        updateNeighborViewsForID(mMobileItemId);

        final ViewTreeObserver observer = getViewTreeObserver();
        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @SuppressLint("NewApi")
            public boolean onPreDraw() {
                observer.removeOnPreDrawListener(this);

                View switchView = getViewForID(switchItemID);

                mTotalOffset += deltaY;

                int switchViewNewTop = switchView.getTop();
                int delta = switchViewStartTop - switchViewNewTop;

                switchView.setTranslationY(delta);

                ObjectAnimator animator = ObjectAnimator.ofFloat(switchView,
                        View.TRANSLATION_Y, 0);
                animator.setDuration(MOVE_DURATION);
                animator.start();

                return true;
            }
        });
    }
}

private void swapElements(ArrayList arrayList, int indexOne, int indexTwo) {
    Object temp = arrayList.get(indexOne);
    arrayList.set(indexOne, arrayList.get(indexTwo));
    arrayList.set(indexTwo, temp);
}


/**
 * Resets all the appropriate fields to a default state while also animating
 * the hover cell back to its correct location.
 */
private void touchEventsEnded () {
    final View mobileView = getViewForID(mMobileItemId);
    if (mCellIsMobile|| mIsWaitingForScrollFinish) {
        mCellIsMobile = false;
        mIsWaitingForScrollFinish = false;
        mIsMobileScrolling = false;
        mActivePointerId = INVALID_POINTER_ID;

        // If the autoscroller has not completed scrolling, we need to wait for it to
        // finish in order to determine the final location of where the hover cell
        // should be animated to.
        if (mScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
            mIsWaitingForScrollFinish = true;
            return;
        }

        mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mobileView.getTop());

        ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, "bounds",
                sBoundEvaluator, mHoverCellCurrentBounds);
        hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                invalidate();
            }
        });
        hoverViewAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                setEnabled(false);
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mAboveItemId = INVALID_ID;
                mMobileItemId = INVALID_ID;
                mBelowItemId = INVALID_ID;
                mobileView.setVisibility(VISIBLE);
                mHoverCell = null;
                setEnabled(true);
                invalidate();
            }
        });
        hoverViewAnimator.start();
    } else {
        touchEventsCancelled();
    }
}

/**
 * Resets all the appropriate fields to a default state.
 */
private void touchEventsCancelled () {
    View mobileView = getViewForID(mMobileItemId);
    if (mCellIsMobile) {
        mAboveItemId = INVALID_ID;
        mMobileItemId = INVALID_ID;
        mBelowItemId = INVALID_ID;
        mobileView.setVisibility(VISIBLE);
        mHoverCell = null;
        invalidate();
    }
    mCellIsMobile = false;
    mIsMobileScrolling = false;
    mActivePointerId = INVALID_POINTER_ID;
}

/**
 * This TypeEvaluator is used to animate the BitmapDrawable back to its
 * final location when the user lifts his finger by modifying the
 * BitmapDrawable's bounds.
 */
private final static TypeEvaluator<Rect> sBoundEvaluator = new TypeEvaluator<Rect>() {
    public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
        return new Rect(interpolate(startValue.left, endValue.left, fraction),
                interpolate(startValue.top, endValue.top, fraction),
                interpolate(startValue.right, endValue.right, fraction),
                interpolate(startValue.bottom, endValue.bottom, fraction));
    }

    public int interpolate(int start, int end, float fraction) {
        return (int)(start + fraction * (end - start));
    }
};

/**
 *  Determines whether this listview is in a scrolling state invoked
 *  by the fact that the hover cell is out of the bounds of the listview;
 */
private void handleMobileCellScroll() {
    mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
}

/**
 * This method is in charge of determining if the hover cell is above
 * or below the bounds of the listview. If so, the listview does an appropriate
 * upward or downward smooth scroll so as to reveal new items.
 */
public boolean handleMobileCellScroll(Rect r) {
    int offset = computeVerticalScrollOffset();
    int height = getHeight();
    int extent = computeVerticalScrollExtent();
    int range = computeVerticalScrollRange();
    int hoverViewTop = r.top;
    int hoverHeight = r.height();

    if (hoverViewTop <= 0 && offset > 0) {
        smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);
        return true;
    }

    if (hoverViewTop + hoverHeight >= height && (offset + extent) < range) {
        smoothScrollBy(mSmoothScrollAmountAtEdge, 0);
        return true;
    }

    return false;
}

public void setCheeseList(ArrayList<String> cheeseList) {
    mCheeseList = cheeseList;
}

/**
 * This scroll listener is added to the listview in order to handle cell swapping
 * when the cell is either at the top or bottom edge of the listview. If the hover
 * cell is at either edge of the listview, the listview will begin scrolling. As
 * scrolling takes place, the listview continuously checks if new cells became visible
 * and determines whether they are potential candidates for a cell swap.
 */
private AbsListView.OnScrollListener mScrollListener = new AbsListView.OnScrollListener () {

    private int mPreviousFirstVisibleItem = -1;
    private int mPreviousVisibleItemCount = -1;
    private int mCurrentFirstVisibleItem;
    private int mCurrentVisibleItemCount;
    private int mCurrentScrollState;

    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                         int totalItemCount) {
        mCurrentFirstVisibleItem = firstVisibleItem;
        mCurrentVisibleItemCount = visibleItemCount;

        mPreviousFirstVisibleItem = (mPreviousFirstVisibleItem == -1) ? mCurrentFirstVisibleItem
                : mPreviousFirstVisibleItem;
        mPreviousVisibleItemCount = (mPreviousVisibleItemCount == -1) ? mCurrentVisibleItemCount
                : mPreviousVisibleItemCount;

        checkAndHandleFirstVisibleCellChange();
        checkAndHandleLastVisibleCellChange();

        mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;
        mPreviousVisibleItemCount = mCurrentVisibleItemCount;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;
        mScrollState = scrollState;
        isScrollCompleted();
    }

    /**
     * This method is in charge of invoking 1 of 2 actions. Firstly, if the listview
     * is in a state of scrolling invoked by the hover cell being outside the bounds
     * of the listview, then this scrolling event is continued. Secondly, if the hover
     * cell has already been released, this invokes the animation for the hover cell
     * to return to its correct position after the listview has entered an idle scroll
     * state.
     */
    private void isScrollCompleted() {
        if (mCurrentVisibleItemCount > 0 && mCurrentScrollState == SCROLL_STATE_IDLE) {
            if (mCellIsMobile && mIsMobileScrolling) {
                handleMobileCellScroll();
            } else if (mIsWaitingForScrollFinish) {
                touchEventsEnded();
            }
        }
    }

    /**
     * Determines if the listview scrolled up enough to reveal a new cell at the
     * top of the list. If so, then the appropriate parameters are updated.
     */
    public void checkAndHandleFirstVisibleCellChange() {
        if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) {
            if (mCellIsMobile && mMobileItemId != INVALID_ID) {
                updateNeighborViewsForID(mMobileItemId);
                handleCellSwitch();
            }
        }
    }

    /**
     * Determines if the listview scrolled down enough to reveal a new cell at the
     * bottom of the list. If so, then the appropriate parameters are updated.
     */
    public void checkAndHandleLastVisibleCellChange() {
        int currentLastVisibleItem = mCurrentFirstVisibleItem + mCurrentVisibleItemCount;
        int previousLastVisibleItem = mPreviousFirstVisibleItem + mPreviousVisibleItemCount;
        if (currentLastVisibleItem != previousLastVisibleItem) {
            if (mCellIsMobile && mMobileItemId != INVALID_ID) {
                updateNeighborViewsForID(mMobileItemId);
                handleCellSwitch();
            }
        }
    }
};

下面是list.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >

<com.bin.widget.DynamicListView
    android:id="@+id/campaignListView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
                android:background="#0000"
     >
</com.bin.widget.DynamicListView>

</RelativeLayout>

请帮帮我...提前致谢

最佳答案

嗨,我终于自己解决了这个问题,上面的代码通过对 ListView 的适配器类进行一些更改来工作。在适配器的 public long getItemId(int position) 中进行必要的更改,以便它会为您提供传递的位置 View 的 ID。不要只是返回位置。此外,在 dynamicListview 类中,在 listview 的交换位置刷新之后,在 listadapter 的 getview() 方法中完成,我们需要进行相应的更改。 就是这样,然后它就像魅力一样工作。希望这也能帮助其他人。

关于android - 拖放 View 的 ListView ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26451731/

相关文章:

java - 关于安卓项目

android - Play商店上Android应用中的“应用权限”页面

android - 在 ListView 中查找按钮的位置

android - 使用 CardsUI 在 onContextItemSelected 中获取 ID

java - 自定义adapterView多次调用getView函数更新 ListView 导致应用速度变慢

android - 动态设置 ImageView 的边距

java - 行未在 SQLite 中创建并检索 AUTOINCRMENT 值 - Android

c# - 在 WPF ListView 中以编程方式选择项目

带有复选框删除/获取行 ID 的 Android ListView

android - 使用ViewBinder更新 `ListView`行中的图像