本文主要是介绍Android自定义View——可拖拽的ListView,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
有时时候需要对ListView的Item进行手动拖拽排序,如安桌系统中的对通知栏的开关排序,因此需要自定义一个可拖拽的ListView,效果如下:
使用
gradle:
allprojects {repositories {...maven { url 'https://jitpack.io' }}
}dependencies {compile 'com.github.1993hzw:Androids:1.2.4'
}
布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#454545"android:orientation="vertical"><cn.forward.androids.views.DragListViewandroid:id="@+id/draglist"android:layout_width="match_parent"android:layout_height="match_parent"android:cacheColorHint="@android:color/transparent"android:divider="@null"android:dividerHeight="0px"android:listSelector="@android:color/transparent"/>
</LinearLayout>
代码设置:
代码设置参考
原理分析
可见,该ListView只有已添加栏可以拖动,同时可以拖动到未添加栏中,且拖动到顶部或底部时,会自动滚动列表。实现的基本原理为:
1.当点击列表时,获取点击的itemView
int position = pointToPosition(x, y);
View itemView = getChildAt(position - getFirstVisiblePosition());
2.获取itemView的DrawingCache
itemView.setDrawingCacheEnabled(true); // 开启cache.
mBitmap = Bitmap.createBitmap(itemView.getDrawingCache()); // 根据cache创建一个新的bitmap对象.
itemView.setDrawingCacheEnabled(false);
3.根据拖拽的位置绘制DrawingCache
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
// 绘制拖拽的itemView,mLastY为触摸点的y坐标,mDragViewOffset为触摸点在itemView中的坐标y
if (mBitmap != null && !mBitmap.isRecycled()) {canvas.drawBitmap(mBitmap, 0, mLastY - mDragViewOffset, null);
}
}
4.拖拽的过程中,判断是否交换位置
private void checkExchange(int y) {if (mCurrentPosition != mLastPosition) { // // 数据交换if (mDragItemListener != null) {if (mDragItemListener.canExchange(mLastPosition, mCurrentPosition)) { // 进行数据交换,true则表示交换成功View lastView = mItemView;mItemView = getChildAt(mCurrentPosition - getFirstVisiblePosition());// 通知交换数据成功,可在此时设置交换的动画效果mDragItemListener.onExchange(mLastPosition, mCurrentPosition, lastView, mItemView);mLastPosition = mCurrentPosition;}}}}
5.当移动到底部时,ListView向上滑动,当移动到顶部时,ListView要向下滑动
public void checkScroller(final int y) {int offset = 0;if (y < mAutoScrollUpY) { // 拖动到顶部,ListView需要下滑if (y <= mDownY - mTouchSlop) {offset = dp2px(getContext(), 6); // 滑动的距离}} else if (y > mAutoScrollDownY) { // 拖动到底部,ListView需要上滑if (y >= mDownY + mTouchSlop) {offset = -dp2px(getContext(), 6); // 滑动的距离}}if (offset != 0) {View view = getChildAt(mCurrentPosition - getFirstVisiblePosition());if (view != null) {// 滚动列表setSelectionFromTop(mCurrentPosition, view.getTop() + offset);if (!mScrolling) {mScrolling = true;long passed = System.currentTimeMillis() - mLastScrollTime;postDelayed(mScrollRunnable, passed > 15 ? 15 : 15 - passed);}}}}
为了兼容不同的交互需求,定义了DragItemListener,把跟交互相关的代码交给外部。
public interface DragItemListener {/*** 是否进行数据交换** @param srcPosition* @param position 当前拖拽的view的索引* @return 返回true,则确认数据交换;返回false则表示放弃*/boolean canExchange(int srcPosition, int position);/*** 当完成数据交换时回调** @param srcPosition* @param position 当前拖拽的view的索引* @param srcItemView* @param itemView 当前拖拽的view*/void onExchange(int srcPosition, int position, View srcItemView, View itemView);/*** 释放手指** @param position*/void onRelease(int position, View itemView, int itemViewY, int releaseX, int releaseY);/*** 是否可以拖拽** @param itemView* @param x 当前触摸的坐标* @param y* @return*/boolean canDrag(View itemView, int x, int y);/*** 开始拖拽** @param position*/void startDrag(int position, View itemView);/*** 在生成拖影(itemView.getDrawingCache())之前** @param itemView*/void beforeDrawingCache(View itemView);/*** 在生成拖影(itemView.getDrawingCache())之后** @param itemView* @param bitmap 由itemView.getDrawingCache()生成* @return 最终显示的拖影,如果返回为空则使用itemView.getDrawingCache()*/Bitmap afterDrawingCache(View itemView, Bitmap bitmap);}
实践中会不断的改进的代码,请大家关注最新完整的代码:https://github.com/1993hzw/Androids
这篇关于Android自定义View——可拖拽的ListView的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!