Android拖拽的ExpandableListView

正如我们知道的listview在Android中是一个常用的控件,很多app都喜欢在Listview上面做文章,拖动排序就是一中常见的,github上面有一个很好的demo。ExpandableListView顾名思义就是Listview的一个扩展,用来分组Listview,比如QQ好友列表,既然Listview可以拖拽排序,ExpandableListView当然也是可以排序的,看到了一些demo觉得github这个还是不错的DragNDropListView。代码比较简洁清晰,但它和常见的可以拖拽ExpandableListView一样支持跨组拖动,跨组拖动在有些时候是不必要的,因此我也是在它的代码上面改了下控制不可以跨组拖动

     首先我们需要知道ExpandableListView的两个方法getPackedPositionGroup 返回当前条目所在的组的位置

getPackedPositionChild返回当前条目在改组中的位置

 /**
     * Gets the group position from a packed position. See
     * {@link #getPackedPositionForChild(int, int)}.
     * 
     * @param packedPosition The packed position from which the group position
     *            will be returned.
     * @return The group position portion of the packed position. If this does
     *         not contain a group, returns -1.
     */
    public static int getPackedPositionGroup(long packedPosition) {
        // Null
        if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;
        
        return (int) ((packedPosition & PACKED_POSITION_MASK_GROUP) >> PACKED_POSITION_SHIFT_GROUP);
    }
/**
     * Gets the child position from a packed position that is of
     * {@link #PACKED_POSITION_TYPE_CHILD} type (use {@link #getPackedPositionType(long)}).
     * To get the group that this child belongs to, use
     * {@link #getPackedPositionGroup(long)}. See
     * {@link #getPackedPositionForChild(int, int)}.
     * 
     * @param packedPosition The packed position from which the child position
     *            will be returned.
     * @return The child position portion of the packed position. If this does
     *         not contain a child, returns -1.
     */
    public static int getPackedPositionChild(long packedPosition) {
        // Null
        if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;
        
        // Group since a group type clears this bit
        if ((packedPosition & PACKED_POSITION_MASK_TYPE) != PACKED_POSITION_MASK_TYPE) return -1;

        return (int) (packedPosition & PACKED_POSITION_MASK_CHILD);
    }
知道这两个方法就很好办了,说白了当遇到两个交换的条目不再同一组中就不支持拖动,也就是拖动无效。直接列修改的 ExpandableListView的代码了,注释在代码中

/*
 * Copyright (C) 2012 Sreekumar SH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dragdrop.listview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;

import com.android.dragdrop.R;

/**
 * Custom ExpandableListView which enables drag and drop
 * 
 * @author <a href="http://sreekumar.sh" >Sreekumar SH </a>
 *         (sreekumar.sh@gmail.com)
 * 
 */
public class DragNDropListView extends ExpandableListView {
	public interface DragNDropListeners {

		public void onDrag(float x, float y);

		/**
		 * Event fired when an item is picked. position[0] - group position
		 * position[1] - child position
		 * 
		 * @param position
		 */
		public void onPick(int[] position);

		/**
		 * Event fired when an item is dropped. from - position from where item
		 * is picked. to - position at which item is dropped. from[0] - group
		 * position from[1] - child position
		 * 
		 * @param position
		 */
		public void onDrop(int[] from, int[] to);
	}

	private static final String TAG = "DragNDropListView";
	private boolean mDragMode;
	private boolean limitHorizontalDrag = true;
	private int[] mStartPosition = new int[2];
	private int[] mEndPosition = new int[2];
	private int mDragPointOffset; // Used to adjust drag view location
	private int mStartFlatPosition;
	private int prevY = -1;
	private int backgroundColor = 0xe0103010; // different color to identify
	private int defaultBackgroundColor;
	private float screenHeight;
	private float dragRatio;//计算拖动速度
	private ImageView mDragView;
	private DragNDropAdapter adapter;
	private DragNDropListeners listeners;
	private int dragOffset = 50;
	private boolean dragOnLongPress;
	private boolean pressedItem;
	private Handler handler = new Handler();

	public DragNDropListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		Display display = wm.getDefaultDisplay();
		screenHeight = display.getHeight();
	}

	public void setSelectedBackgroud(int color) {
		backgroundColor = color;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return touchHandler(event);
	}

	private boolean touchHandler(final MotionEvent event) {
		final int action = event.getAction();
		final int x = (int) event.getX();
		final int y = (int) event.getY();
		if (prevY < 0) {
			prevY = y;
		}
		Log.d(TAG, "Motion event " + event.getAction());
		int flatPosition = pointToPosition(x, y);
		dragRatio = getHeight() / screenHeight;
		long packagedPosition = getExpandableListPosition(flatPosition);

		if (action == MotionEvent.ACTION_DOWN
				&& getPackedPositionType(packagedPosition) == 1) {
			if (dragOnLongPress) {
				if (pressedItem) {
					mDragMode = true;
					pressedItem = false;
				} else {
					pressedItem = true;
					Runnable r = new Runnable() {
						@Override
						public void run() {
							// y coordinate is changing for no reason ??
							event.setLocation(x, y);
							touchHandler(event);
						}
					};
					handler.postDelayed(r, 200);
					return true;
				}
			} else if (x < dragOffset) {
				mDragMode = true;
			}
		}

		if (!mDragMode) {
			/** when user action on other areas */
			if ((pressedItem && Math.abs(prevY - y) > 30)
					|| event.getAction() != MotionEvent.ACTION_MOVE) {
				pressedItem = false;
				handler.removeCallbacksAndMessages(null);
			}
			return super.onTouchEvent(event);
		}
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			mStartFlatPosition = flatPosition;
			mStartPosition[0] = getPackedPositionGroup(packagedPosition);
			System.out.println("packagedPosition第" + packagedPosition + "组");
			mStartPosition[1] = getPackedPositionChild(packagedPosition);
			if (packagedPosition != PACKED_POSITION_VALUE_NULL) {

				int mItemPosition = flatPosition - getFirstVisiblePosition();
				mDragPointOffset = y - getChildAt(mItemPosition).getTop();
				mDragPointOffset -= ((int) event.getRawY()) - y;
				startDrag(mItemPosition, y);
				if (listeners != null) {
					listeners.onPick(mStartPosition);
					System.out.println("group" + mStartPosition[0]);
					System.out.println("child" + mStartPosition[0]);

				}
				drag(x, y);
			}
			break;
		case MotionEvent.ACTION_MOVE:
			int speed = (int) ((y - prevY) * dragRatio);
			if (getLastVisiblePosition() < getCount() && speed > 0) {
				smoothScrollBy(speed, 1);
			}
			if (getFirstVisiblePosition() > 0 && speed < 0) {
				smoothScrollBy(speed, 1);
			}
			drag(x, y);// replace 0 with x if desired
			if (listeners != null) {
				listeners.onDrag(x, y);
			}
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
		default:

			mDragMode = false;
			if (getPackedPositionType(packagedPosition) == 0) {
				mEndPosition[0] = getPackedPositionGroup(packagedPosition);// 返回值为组的id
				mEndPosition[1] = 0;
			} else {
				mEndPosition[0] = getPackedPositionGroup(packagedPosition);
				mEndPosition[1] = getPackedPositionChild(packagedPosition);// 返回值为子元素的id
			}

			stopDrag(mStartFlatPosition);
			if (packagedPosition != PACKED_POSITION_VALUE_NULL) {
				if (adapter != null) {
					if (mStartPosition[0] == mEndPosition[0]) {// 只有在同一组中可以拖动,去掉条件就是可以跨组拖动
						adapter.onDrop(mStartPosition, mEndPosition);
						System.out.println("group" + mStartPosition[0]);
						System.out.println("child" + mStartPosition[0]);
					}
				}
				if (listeners != null) {
					if (mStartPosition[0] == mEndPosition[0]) {// 只有在同一组中可以拖动,去掉条件就是可以跨组拖动
						listeners.onDrop(mStartPosition, mEndPosition);
						System.out.println("group" + mStartPosition[0]);
						System.out.println("child" + mStartPosition[0]);
					}
				}
			}
			break;
		}
		prevY = y;
		return true;
	}

	// move the drag view
	private void drag(int x, int y) {
		if (mDragView != null) {
			WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mDragView
					.getLayoutParams();
			if (!limitHorizontalDrag) {// no need to move if horizontal drag is
										// limited
				layoutParams.x = x;
			}
			if (dragOnLongPress) {
				// to show that item is detached from the list
				layoutParams.y = y - mDragPointOffset - 20;
			} else {
				layoutParams.y = y - mDragPointOffset;
			}

			WindowManager mWindowManager = (WindowManager) getContext()
					.getSystemService(Context.WINDOW_SERVICE);
			mWindowManager.updateViewLayout(mDragView, layoutParams);
		}
	}

	// enable the drag view for dragging
	private void startDrag(int itemIndex, int y) {
		// stopDrag(itemIndex);

		View item = getChildAt(itemIndex);
		if (item == null)
			return;
		hideItem(item, mStartPosition);

		// Create a copy of the drawing cache so that it does not get recycled
		// by the framework when the list tries to clean up memory
		Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
		item.setBackgroundColor(defaultBackgroundColor);
		WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams();
		mWindowParams.gravity = Gravity.TOP;
		mWindowParams.x = 0;
		mWindowParams.y = y - mDragPointOffset;

		mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
		mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
		mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
				| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
				| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
				| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
				| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
		mWindowParams.format = PixelFormat.TRANSLUCENT;
		mWindowParams.windowAnimations = 0;

		Context context = getContext();
		ImageView v = new ImageView(context);
		v.setImageBitmap(bitmap);

		WindowManager mWindowManager = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		mWindowManager.addView(v, mWindowParams);
		mDragView = v;
	}

	@Override
	public void setAdapter(ExpandableListAdapter adapter) {
		// TODO Auto-generated method stub
		super.setAdapter(adapter);
		this.adapter = (DragNDropAdapter) adapter;
	}

	private void hideItem(View itemView, int[] position) {
		if (adapter != null) {
			adapter.onPick(position);
		}
		itemView.setVisibility(View.INVISIBLE); // make the item invisible as we
												// have picked it
		itemView.setDrawingCacheEnabled(true);
		defaultBackgroundColor = itemView.getDrawingCacheBackgroundColor();
		itemView.setBackgroundColor(backgroundColor);
		ImageView iv = (ImageView) itemView
				.findViewById(R.id.move_icon_customizer_item);
		if (iv != null)
			iv.setVisibility(View.INVISIBLE);
	}

	public void showItem(View itemView) {
		if (itemView != null) {
			itemView.setVisibility(View.VISIBLE);
			itemView.setBackgroundColor(defaultBackgroundColor);
			itemView.setDrawingCacheEnabled(false);
			ImageView iv = (ImageView) itemView
					.findViewById(R.id.move_icon_customizer_item);
			if (iv != null)
				iv.setVisibility(View.VISIBLE);
		}

	}

	/**
	 * destroy the drag view
	 * 
	 * @param itemIndex
	 *            Index of the item
	 */

	private void stopDrag(int itemIndex) {
		int firstPosition = getFirstVisiblePosition() - getHeaderViewsCount();
		int wantedChild = itemIndex - firstPosition;
		if (mDragView != null) {
			if (wantedChild < 0 || wantedChild >= getChildCount()) {
				// no need to do anything
			} else {
				showItem(getChildAt(wantedChild));
			}
			mDragView.setVisibility(GONE);
			WindowManager wm = (WindowManager) getContext().getSystemService(
					Context.WINDOW_SERVICE);
			wm.removeView(mDragView);
			mDragView.setImageDrawable(null);
			mDragView = null;
		}
	}

	/**
	 * @return the limitHorizontalDrag
	 */
	public boolean isLimitHorizontalDrag() {
		return limitHorizontalDrag;
	}

	/**
	 * @param limitHorizontalDrag
	 *            the limitHorizontalDrag to set
	 */
	public void setLimitHorizontalDrag(boolean limitHorizontalDrag) {
		this.limitHorizontalDrag = limitHorizontalDrag;
	}

	/**
	 * @return the listeners
	 */
	public DragNDropListeners getListeners() {
		return listeners;
	}

	/**
	 * @param listeners
	 *            the listeners to set
	 */
	public void setListeners(DragNDropListeners listeners) {
		this.listeners = listeners;
	}

	/**
	 * @return the dragOffset
	 */
	public int getDragOffset() {
		return dragOffset;
	}

	/**
	 * @param dragOffset
	 *            the dragOffset to set
	 */
	public void setDragOffset(int dragOffset) {
		this.dragOffset = dragOffset;
	}

	public boolean isDragOnLongPress() {
		return dragOnLongPress;
	}

	/**
	 * set this to drag an item by long press
	 * 
	 * @param flag
	 */
	public void setDragOnLongPress(boolean flag) {
		dragOnLongPress = flag;
		if (flag) {

		}
	}

}

源码下载




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值