本文源码已经托管在GitHub上,欢迎Fork多多star。地址
CSDN地址
最近重构一个项目,增加了一个新需求,要类似汽车之家的图文混排发帖,图片文字可自由移动位置(如效果图)
功能:图文混排,自由排列文字与图片的位置,图片之间自动加入输入框,两个输入框若相邻且有一个为空,则删除一个保留另外一个,若都有内容则不删除,删除文字时,若输入框内容为空,则删除整个输入框
实现:RecyclerView + ItemTouchHelper(为什么用RecyclerView,不用ListView,这个个人觉得RecyclerView的效果要比ListView好,汽车之家应该是ListView(猜测))
代码: 首先要改造一下ItemTouchHelper这个类,这是Android提供的拖拽帮助类,在android.support.v7.widget.helper包下。 改造的原因:
图中的Callback,在下面的处理过程中需要使用,但是他是protected的,所以我们要想要拿出来就要这样:
新建一个package 包名如图要一样,然后新建一个class 集成ItemTouchHelper这个类。
public class MosrItemTouchHelper extends ItemTouchHelper {
public MosrItemTouchHelper(Callback callback) {
super(callback);
}
public Callback getCallback() {
return mCallback;
}
}
加上get方法就可以了。
public class DefaultItemTouchHelper extends MosrItemTouchHelper {
private DefaultItemTouchHelpCallback itemTouchHelpCallback;
public DefaultItemTouchHelper(DefaultItemTouchHelpCallback.OnItemTouchCallbackListener onItemTouchCallbackListener) {
super(new DefaultItemTouchHelpCallback(onItemTouchCallbackListener));
itemTouchHelpCallback = (DefaultItemTouchHelpCallback) getCallback();
}
/**
* 设置是否可以被拖拽
*
* @param canDrag 是true,否false
*/
public void setDragEnable(boolean canDrag) {
itemTouchHelpCallback.setDragEnable(canDrag);
}
/**
* 设置是否可以被滑动
*
* @param canSwipe 是true,否false
*/
public void setSwipeEnable(boolean canSwipe) {
itemTouchHelpCallback.setSwipeEnable(canSwipe);
}
}
这个类用户设置拖拽,滑动动作的可用性。
public class DefaultItemTouchHelpCallback extends ItemTouchHelper.Callback {
/**
* Item操作的回调
*/
private OnItemTouchCallbackListener onItemTouchCallbackListener;
/**
* 是否可以拖拽
*/
private boolean isCanDrag = false;
/**
* 是否可以被滑动
*/
private boolean isCanSwipe = false;
public DefaultItemTouchHelpCallback(OnItemTouchCallbackListener onItemTouchCallbackListener) {
this.onItemTouchCallbackListener = onItemTouchCallbackListener;
}
/**
* 设置Item操作的回调,去更新UI和数据源
*
* @param onItemTouchCallbackListener
*/
public void setOnItemTouchCallbackListener(OnItemTouchCallbackListener onItemTouchCallbackListener) {
this.onItemTouchCallbackListener = onItemTouchCallbackListener;
}
/**
* 设置是否可以被拖拽
*
* @param canDrag 是true,否false
*/
public void setDragEnable(boolean canDrag) {
isCanDrag = canDrag;
}
/**
* 设置是否可以被滑动
*
* @param canSwipe 是true,否false
*/
public void setSwipeEnable(boolean canSwipe) {
isCanSwipe = canSwipe;
}
/**
* 当Item被长按的时候是否可以被拖拽
*
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return isCanDrag;
}
/**
* Item是否可以被滑动(H:左右滑动,V:上下滑动)
*
* @return
*/
@Override
public boolean isItemViewSwipeEnabled() {
return isCanSwipe;
}
/**
* 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向
*
* @param recyclerView
* @param viewHolder
*
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {// GridLayoutManager
// flag如果值是0,相当于这个功能被关闭
int dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlag = 0;
// create make
return makeMovementFlags(dragFlag, swipeFlag);
} else if (layoutManager instanceof LinearLayoutManager) {// linearLayoutManager
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int orientation = linearLayoutManager.getOrientation();
int dragFlag = 0;
int swipeFlag = 0;
// 为了方便理解,相当于分为横着的ListView和竖着的ListView
if (orientation == LinearLayoutManager.HORIZONTAL) {// 如果是横向的布局
swipeFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else if (orientation == LinearLayoutManager.VERTICAL) {// 如果是竖向的布局,相当于ListView
dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return makeMovementFlags(dragFlag, swipeFlag);
}
return 0;
}
/**
* 当Item被拖拽的时候被回调
*
* @param recyclerView recyclerView
* @param srcViewHolder 拖拽的ViewHolder
* @param targetViewHolder 目的地的viewHolder
*
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder srcViewHolder, RecyclerView.ViewHolder targetViewHolder) {
if (onItemTouchCallbackListener != null) {
return onItemTouchCallbackListener.onMove(srcViewHolder.getAdapterPosition(), targetViewHolder.getAdapterPosition());
}
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if (onItemTouchCallbackListener != null) {
onItemTouchCallbackListener.onSwiped(viewHolder.getAdapterPosition());
}
}
public interface OnItemTouchCallbackListener {
/**
* 当某个Item被滑动删除的时候
*
* @param adapterPosition item的position
*/
void onSwiped(int adapterPosition);
/**
* 当两个Item位置互换的时候被回调
*
* @param srcPosition 拖拽的item的position
* @param targetPosition 目的地的Item的position
*
* @return 开发者处理了操作应该返回true,开发者没有处理就返回false
*/
boolean onMove(int srcPosition, int targetPosition);
}
}
这个类用户处理拖拽,滑动等动作。
接下来,创建一个RecyclerView布局,初始化,设置布局管理器,设置适配器, 加入如下代码
// 把ItemTouchHelper和itemTouchHelper绑定
itemTouchHelper = new DefaultItemTouchHelper(onItemTouchCallbackListener);
itemTouchHelper.attachToRecyclerView(recyclerView);
mainAdapter.setItemTouchHelper(itemTouchHelper);
itemTouchHelper.setDragEnable(false);
itemTouchHelper.setSwipeEnable(false);
private DefaultItemTouchHelpCallback.OnItemTouchCallbackListener onItemTouchCallbackListener = new DefaultItemTouchHelpCallback.OnItemTouchCallbackListener() {
@Override
public void onSwiped(int adapterPosition) {
if (userInfoList != null) {
userInfoList.remove(adapterPosition);
mainAdapter.notifyItemRemoved(adapterPosition);
}
}
@Override
public synchronized boolean onMove(int srcPosition, int targetPosition) {
mSrcPosition = srcPosition;
mTargetPosition = targetPosition;
Log.e("mosr", "srcPosition: " + srcPosition);
Log.e("mosr", "targetPosition: " + targetPosition);
if (userInfoList != null) {
// 更换数据源中的数据Item的位置
Collections.swap(userInfoList, srcPosition, targetPosition);
// 更新UI中的Item的位置,主要是给用户看到交互效果
mainAdapter.notifyItemMoved(srcPosition, targetPosition);
mSelectPostion = targetPosition;
isMoveing = true;
return true;
}
return false;
}
};
Adapter代码处理 指定View 设置onLongClickListener
public void setHeight(int mHeight, RecyclerView.ViewHolder mViewHolder) {
if (this.mHeight == mHeight)
return;
this.mHeight = mHeight;
if (mHeight == 400)
try {
for (MainContentViewHolder viewHolder : mList) {
viewHolder.setData();
}
} catch (Exception e) {
}
else
notifyDataSetChanged();
mList.clear();
if (null != mViewHolder)
itemTouchHelper.startDrag(mViewHolder);
}
!!!因为拖拽时需求让图片的高度减少,所以要动态设置图片View 的高度,上述代码可见变小时不是用notifyDataSetChanged 而是通过ViewHolder的SetDate方法,是因为notifyDataSetChanged 会重新绘制布局导致ItemToucHelper失去焦点,谨记!!!
当手离开屏幕时还原图片高度
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
notifyItemView();
mainAdapter.setHeight(700, null);
break;
}
return false;
}
处理图片之间的输入框,输入框与输入框之间的关系
private void notifyItemView() {
if (!isMoveing)
return;
try {
if (userInfoList.get(0).getIsPic() != 1)//第一行
userInfoList.add(0, new InvitationInfo("", "", 1));
if (userInfoList.get(userInfoList.size() - 1).getIsPic() != 1) //最后一行
userInfoList.add(new InvitationInfo("", "", 1));
for (int i = 0; i < userInfoList.size(); i++) {
if (i > 0 && userInfoList.get(i).getIsPic() == 0 && userInfoList.get(i).getIsPic() == userInfoList.get(i - 1).getIsPic()) {
userInfoList.add(i, new InvitationInfo("", "", 1));
mSelectPostion = mSelectPostion < i ? mSelectPostion : mSelectPostion++;
i++;
}
}
for (int i = 0; i < userInfoList.size(); i++) {
if (userInfoList.get(i).getIsPic() == 1 && i > 0 && userInfoList.get(i).getIsPic() == userInfoList.get(i - 1).getIsPic())
if (TextUtils.isEmpty(userInfoList.get(i).getText())) {
userInfoList.remove(i);
mSelectPostion = mSelectPostion < i ? mSelectPostion : mSelectPostion--;
i--;
} else if (TextUtils.isEmpty(userInfoList.get(i - 1).getText())) {
userInfoList.remove(i - 1);
mSelectPostion = mSelectPostion < i - 1 ? mSelectPostion : mSelectPostion--;
i--;
}
}
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
} finally {
mainAdapter.notifyDataSetChanged();
recyclerView.smoothScrollToPosition(mSelectPostion);
isMoveing = false;
}
}
核心部分都在上面了,由于是调研时写的Demo 所以很多地方不严谨,逻辑不清晰,借鉴者多加留意,参考我在GitHub上托管的源码。 如有问题Q我1515789527