效果图:
1.lsitview中view元素的设置。
在源代码中设置了menu了,从而实现了listview的一系列应用效果。
1.menuItem类封装了每一行的各种属性
public MenuItem(int width, String text, int textSize, int textColor, Drawable icon, Drawable background, int direction) { this.width = width; this.text = text; this.textSize = textSize; this.textColor = textColor; this.icon = icon; this.background = background; this.direction = direction; }
其中,在该类文件中还有一个bulider类,用于快速新建一个Menuitem
public MenuItem build() { return new MenuItem(width, text, textSize, textColor, icon, background, direction); }
2.menu。
在listview中的每一行都是一个menu。menu中又有几个menuItem,用于实现,当手指向右滑动时,显现的小图标。
mLeftMenuItems = new ArrayList<>(); mRightMenuItems = new ArrayList<>();mLeftMenuItems是左边的小图标的集合,mRightMenuItems是右边的小图标的集合。
public Menu(Drawable itemBackGroundDrawable, boolean wannaOver, int menuViewType) { mItemBackGroundDrawable = itemBackGroundDrawable; mWannaOver = wannaOver; mLeftMenuItems = new ArrayList<>(); mRightMenuItems = new ArrayList<>(); mMenuViewType = menuViewType; }
menu中封装了增减item的函数。
public void addItem(MenuItem menuItem) { if (menuItem.direction == MenuItem.DIRECTION_LEFT) { mLeftMenuItems.add(menuItem); } else { mRightMenuItems.add(menuItem); } } public void addItem(MenuItem menuItem, int position) { if (menuItem.direction == MenuItem.DIRECTION_LEFT) { mLeftMenuItems.add(position, menuItem); } else { mRightMenuItems.add(position, menuItem); } } public boolean removeItem(MenuItem menuItem) { if (menuItem.direction == MenuItem.DIRECTION_LEFT) { return mLeftMenuItems.remove(menuItem); } else { return mRightMenuItems.remove(menuItem); } }
通过adapter中设置的来创建不同的Menu
List<Menu> menuList = new ArrayList<>(2);
Menu menu0 = new Menu(new ColorDrawable(Color.WHITE), true, 0);
menu0.addItem(new MenuItem.Builder().setWidth(90)//set Width
.setBackground(new ColorDrawable(Color.RED))// set background
.setText("One")//set text string
.setTextColor(Color.GRAY)//set text color
.setTextSize(20)//set text size
.build());
menu0.addItem(new MenuItem.Builder().setWidth(120)
.setBackground(new ColorDrawable(Color.BLACK))
.setDirection(MenuItem.DIRECTION_RIGHT)//set direction (default DIRECTION_LEFT)
.setIcon(getResources().getDrawable(R.drawable.ic_launcher))// set icon
.build());
Menu menu1 = new Menu(new ColorDrawable(Color.YELLOW), false, 1);
menu1.addItem(new MenuItem.Builder().setWidth(60)
.setBackground(new ColorDrawable(Color.RED))
.setText("Two")
.setTextColor(Color.GRAY)
.setTextSize(25)
.build());
menu1.addItem(new MenuItem.Builder().setWidth(70)
.setBackground(new ColorDrawable(Color.BLUE))
.setText("Three")
.setDirection(MenuItem.DIRECTION_RIGHT)
.setTextColor(Color.BLACK)
.setTextSize(20)
.build());
menuList.add(menu0);
menuList.add(menu1);
listView.setMenu(menuList)
public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //获取出坐标来 mXDown = (int) ev.getX(); mYDown = (int) ev.getY(); //当前state状态为按下 mState = STATE_DOWN; break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_2_DOWN: case MotionEvent.ACTION_POINTER_3_DOWN: removeLongClickMessage(); mState = STATE_MORE_FINGERS; //消耗掉,不传递下去了 return true; case MotionEvent.ACTION_MOVE: if (fingerNotMove(ev) && mState != STATE_SCROLL) {//手指的范围在50以内 sendLongClickMessage(pointToPosition(mXDown, mYDown)); mState = STATE_LONG_CLICK; } else if (fingerLeftAndRightMove(ev)) {//上下范围在50,主要检测左右滑动 removeLongClickMessage(); mState = STATE_SCROLL; //将当前想要滑动哪一个传递给wrapperAdapter int position = pointToPosition(mXDown, mYDown); if (position != AdapterView.INVALID_POSITION) { View view = getChildAt(position - getFirstVisiblePosition()); if (view instanceof ItemMainLayout) { mWrapperAdapter.setSlideItemPosition(position); //将事件传递下去 return super.dispatchTouchEvent(ev); } else { //消耗事件 return true; } } else { //消耗事件 return true; } } else { removeLongClickMessage(); } break; case MotionEvent.ACTION_UP: if (mState == STATE_DOWN || mState == STATE_LONG_CLICK) { int position = pointToPosition(mXDown, mYDown); //是否ScrollBack了,是的话就不去执行onListItemClick操作了 int scrollBackState = scrollBack(position, ev.getX()); if (scrollBackState == RETURN_SCROLL_BACK_NOTHING) { if (mOnListItemClickListener != null && mIsWannaTriggerClick) { View v = getChildAt(position - getFirstVisiblePosition()); mOnListItemClickListener.onListItemClick(v, position); } } } removeLongClickMessage(); mState = STATE_NOTHING; break; case MotionEvent.ACTION_POINTER_3_UP: case MotionEvent.ACTION_POINTER_2_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_CANCEL: mState = STATE_NOTHING; break; default: break; } return super.dispatchTouchEvent(ev); }
滑动功能主要用到了Android的Scroller类和View的scrollTo方法。在ItemMainLayout中的类中的onTouchEvent
public boolean onTouchEvent(MotionEvent ev) { getParent().requestDisallowInterceptTouchEvent(false); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mXDown = ev.getX(); mYDown = ev.getY(); //控件初始距离 mLeftDistance = mItemCustomLayout.getLeft(); //是否有要scroll的动向,目前没有 mIsMoving = false; break; case MotionEvent.ACTION_MOVE: if (fingerNotMove(ev) && !mIsMoving) {//手指的范围在50以内 //执行ListView的手势操作 getParent().requestDisallowInterceptTouchEvent(false); } else if (fingerLeftAndRightMove(ev) || mIsMoving) {//上下范围在50,主要检测左右滑动 //是否有要scroll的动向 mIsMoving = true; //执行控件的手势操作 getParent().requestDisallowInterceptTouchEvent(true); float moveDistance = ev.getX() - mXDown;//这个往右是正,往左是负 //判断意图 if (moveDistance > 0) {//往右 if (mLeftDistance == 0) {//关闭状态 mIntention = INTENTION_LEFT_OPEN; setBackGroundVisible(true, false); } else if (mLeftDistance < 0) {//右边的btn显示出来的 mIntention = INTENTION_RIGHT_CLOSE; } else if (mLeftDistance > 0) {//左边的btn显示出来的 mIntention = INTENTION_LEFT_ALREADY_OPEN; } } else if (moveDistance < 0) {//往左 if (mLeftDistance == 0) {//关闭状态 mIntention = INTENTION_RIGHT_OPEN; setBackGroundVisible(false, true); } else if (mLeftDistance < 0) {//右边的btn显示出来的 mIntention = INTENTION_RIGHT_ALREADY_OPEN; } else if (mLeftDistance > 0) {//左边的btn显示出来的 mIntention = INTENTION_LEFT_CLOSE; } } //计算出距离 switch (mIntention) { case INTENTION_LEFT_OPEN: case INTENTION_LEFT_ALREADY_OPEN: //此时moveDistance为正数,mLeftDistance为0 float distanceLeftOpen = mLeftDistance + moveDistance; if (!mWannaOver) { distanceLeftOpen = distanceLeftOpen > mBtnLeftTotalWidth ? mBtnLeftTotalWidth : distanceLeftOpen; } //滑动 mItemCustomLayout.layout((int) distanceLeftOpen, mItemCustomLayout.getTop(), mItemCustomLayout.getWidth() + (int) distanceLeftOpen, mItemCustomLayout.getBottom()); break; case INTENTION_LEFT_CLOSE: //此时moveDistance为负数,mLeftDistance为正数 float distanceLeftClose = mLeftDistance + moveDistance < 0 ? 0 : mLeftDistance + moveDistance; //滑动 mItemCustomLayout.layout((int) distanceLeftClose, mItemCustomLayout.getTop(), mItemCustomLayout.getWidth() + (int) distanceLeftClose, mItemCustomLayout.getBottom()); break; case INTENTION_RIGHT_OPEN: case INTENTION_RIGHT_ALREADY_OPEN: //此时moveDistance为负数,mLeftDistance为0 float distanceRightOpen = mLeftDistance + moveDistance; //distanceRightOpen为正数 if (!mWannaOver) { distanceRightOpen = -distanceRightOpen > mBtnRightTotalWidth ? -mBtnRightTotalWidth : distanceRightOpen; } //滑动 mItemCustomLayout.layout((int) distanceRightOpen, mItemCustomLayout.getTop(), mItemCustomLayout.getWidth() + (int) distanceRightOpen, mItemCustomLayout.getBottom()); break; case INTENTION_RIGHT_CLOSE: //此时moveDistance为正数,mLeftDistance为负数 float distanceRightClose = mLeftDistance + moveDistance > 0 ? 0 : mLeftDistance + moveDistance; //滑动 mItemCustomLayout.layout((int) distanceRightClose, mItemCustomLayout.getTop(), mItemCustomLayout.getWidth() + (int) distanceRightClose, mItemCustomLayout.getBottom()); break; } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: switch (mIntention) { case INTENTION_LEFT_CLOSE: case INTENTION_LEFT_OPEN: case INTENTION_LEFT_ALREADY_OPEN: //如果滑出的话,那么就滑到固定位置(只要滑出了 mBtnLeftTotalWidth / 2 ,就算滑出去了) if (Math.abs(mItemCustomLayout.getLeft()) > mBtnLeftTotalWidth / 2) { //滑出 mIntention = INTENTION_LEFT_OPEN; int delta = mBtnLeftTotalWidth - Math.abs(mItemCustomLayout.getLeft()); mScroller.startScroll(mItemCustomLayout.getLeft(), 0, delta, 0, SCROLL_TIME); if (mOnItemSlideListenerProxy != null && mScrollState != SCROLL_STATE_OPEN) { mOnItemSlideListenerProxy.onSlideOpen(this, MenuItem.DIRECTION_LEFT); } mScrollState = SCROLL_STATE_OPEN; } else { mIntention = INTENTION_LEFT_CLOSE; //滑回去,归位 mScroller.startScroll(mItemCustomLayout.getLeft(), 0, -mItemCustomLayout.getLeft(), 0, SCROLL_TIME); if (mOnItemSlideListenerProxy != null && mScrollState != SCROLL_STATE_CLOSE) { mOnItemSlideListenerProxy.onSlideClose(this, MenuItem.DIRECTION_LEFT); } mScrollState = SCROLL_STATE_CLOSE; } break; case INTENTION_RIGHT_CLOSE: case INTENTION_RIGHT_OPEN: case INTENTION_RIGHT_ALREADY_OPEN: if (Math.abs(mItemCustomLayout.getLeft()) > mBtnRightTotalWidth / 2) { //滑出 mIntention = INTENTION_RIGHT_OPEN; int delta = mBtnRightTotalWidth - Math.abs(mItemCustomLayout.getLeft()); mScroller.startScroll(mItemCustomLayout.getLeft(), 0, -delta, 0, SCROLL_TIME); if (mOnItemSlideListenerProxy != null && mScrollState != SCROLL_STATE_OPEN) { mOnItemSlideListenerProxy.onSlideOpen(this, MenuItem.DIRECTION_RIGHT); } mScrollState = SCROLL_STATE_OPEN; } else { mIntention = INTENTION_RIGHT_CLOSE; mScroller.startScroll(mItemCustomLayout.getLeft(), 0, -mItemCustomLayout.getLeft(), 0, SCROLL_TIME); //滑回去,归位 if (mOnItemSlideListenerProxy != null && mScrollState != SCROLL_STATE_CLOSE) { mOnItemSlideListenerProxy.onSlideClose(this, MenuItem.DIRECTION_RIGHT); } mScrollState = SCROLL_STATE_CLOSE; } break; } mIntention = INTENTION_ZERO; postInvalidate(); mIsMoving = false; break; default: break; } return true; }
2.拖拽功能
public void onDragViewMoving(int position)
.参数 position
是被拖动的item的现在所在的位置,同时onDragViewMoving这个方法会被不停的调用,因为一直在拖动,同时position也会改变。
public void onDragViewDown(int position)
. 参数 position
是被拖动的item被放下的时候在SDLV中的位置。
public boolean onDrag(View v, DragEvent event) { final int action = event.getAction(); switch (action) { case DragEvent.ACTION_DRAG_STARTED: return true; case DragEvent.ACTION_DRAG_ENTERED: return true; case DragEvent.ACTION_DRAG_LOCATION: //当前移动的item在ListView中的position int position = pointToPosition((int) event.getX(), (int) event.getY()); //如果位置发生了改变 if (mBeforeCurrentPosition != position) { //有时候得到的position是-1(AdapterView.INVALID_POSITION),忽略掉 if (position >= 0) { //判断是往上了还是往下了 mUp = position - mBeforeCurrentPosition <= 0; //记录移动之后上一次的位置 mBeforeBeforePosition = mBeforeCurrentPosition; //记录当前位置 mBeforeCurrentPosition = position; } } moveListViewUpOrDown(position); //有时候为-1(AdapterView.INVALID_POSITION)的情况,忽略掉 if (position >= 0) { //判断是不是已经换过位置了,如果没有换过,则进去换 if (position != mCurrentPosition) { if (mUp) {//往上 int realPosition = position - getHeaderViewsCount(); if (realPosition >= 0 && realPosition < mDataList.size()) {//这里判断就忽略了drag到header的情况 //只是移动了一格 if (position - mBeforeBeforePosition == -1) { T t = mDataList.get(realPosition); mDataList.set(realPosition, mDataList.get(realPosition + 1)); mDataList.set(realPosition + 1, t); } else {//一下子移动了好几个位置,其实可以和上面那个方法合并起来的 T t = mDataList.get(mBeforeBeforePosition - getHeaderViewsCount()); for (int i = mBeforeBeforePosition - getHeaderViewsCount(); i > realPosition; i--) { mDataList.set(i, mDataList.get(i - 1)); } mDataList.set(realPosition, t); } mSDAdapter.notifyDataSetChanged(); //更新位置 mCurrentPosition = position; } } else { //header部分不算,footer部分不算 int realPosition = position - getHeaderViewsCount(); if (realPosition > 0 && realPosition < mDataList.size()) { if (position - mBeforeBeforePosition == 1) { T t = mDataList.get(realPosition); mDataList.set(realPosition, mDataList.get(realPosition - 1)); mDataList.set(realPosition - 1, t); } else { T t = mDataList.get(mBeforeBeforePosition - getHeaderViewsCount()); for (int i = mBeforeBeforePosition - getHeaderViewsCount(); i < realPosition; i++) { mDataList.set(i, mDataList.get(i + 1)); } mDataList.set(realPosition, t); } mSDAdapter.notifyDataSetChanged(); //更新位置 mCurrentPosition = position; } } } } if (mOnDragListener != null) { mOnDragListener.onDragViewMoving(mCurrentPosition); } return true; case DragEvent.ACTION_DRAG_EXITED: return true; case DragEvent.ACTION_DROP: mSDAdapter.notifyDataSetChanged(); for (int i = 0; i < getLastVisiblePosition() - getFirstVisiblePosition(); i++) { if (getChildAt(i) instanceof ItemMainLayout) { ItemMainLayout view = (ItemMainLayout) getChildAt(i); setItemVisible(view); } } if (mOnDragListener != null) { mOnDragListener.onDragViewDown(mCurrentPosition); } return true; case DragEvent.ACTION_DRAG_ENDED: return true; default: break; } return false; }
3.拖动item往上或往下:ListView的smoothScrollToPosition方法。
private void moveListViewUpOrDown(int position) { //ListView中最上面的显示的位置 int firstPosition = getFirstVisiblePosition(); //ListView中最下面的显示的位置 int lastPosition = getLastVisiblePosition(); //能够往上的话往上 if ((position == firstPosition || position == firstPosition + 1) && firstPosition != 0) { smoothScrollToPosition(firstPosition - 1); } //能够往下的话往下 if ((position == lastPosition || position == lastPosition - 1) && lastPosition != getCount() - 1) { smoothScrollToPosition(lastPosition + 1); } }
//Item滑动监听器 SlideAndDragListView.OnSlideListener() { @Override public void onSlideOpen(View view, View parentView, int position, int direction) { } @Override public void onSlideClose(View view, View parentView, int position, int direction) { } }); //Item删除监听器 slideAndDragListView.setOnItemDeleteListener(new SlideAndDragListView.OnItemDeleteListener() { @Override public void onItemDelete(View view, int position) { } });