前言
首先说明,调整模块菜单次序,不是什么痛点功能。只是为了优化用户体验,这个东西,完全自定义拖拉式菜单也没有多大难点,但是作为一个懒人,还是遵从一点,能用google自带组件的,尽量用自带的,除非它真没有。
所以这次就只是在GridView|RecyclerView的基础上,加上拖拉调换次序的功能。以应付日常开发需求。本次以GridView为样例,先上效果图。
步骤
1.还是先梳理一下思路:
- 当用户长按选择一个item时,将该item隐藏,然后用WindowManager添加一个新的window,该window与所选择item一模一样,并且跟随用户手指滑动而不断改变位置。
- 当手指滑动到另一个item的位置的时候,将选择的item移出,并将此item插入到当前位置,选择的item在gridview中一直占位但不显示,因为你在windowManager创建的这个一模一样的item还在展示(否则就显示两个了)。
- 当用户手指抬起时,把window移除,显示Gridview中你选择item,使用
notifyDataSetChanged()
做出GridView更新。
2.代码部分:
- GridView的适配器,正常写,只是加入一些函数来处理滑动时候的事件。
public class BlockGridAdapter extends BaseAdapter { public static final int WAIT_POSITION = -101; public List<MainBean> mBlockList = new ArrayList<>(); private final Context mContext; private LayoutInflater inflater; private int pullItemPosition = WAIT_POSITION; public BlockGridAdapter(Context context, List<MainBean> list) { mBlockList.clear(); mBlockList.addAll(list); mContext = context; this.inflater = LayoutInflater.from(context); } @Override public View getView(int position, View convertView, ViewGroup parent) { //加载布局为一个视图 View view = inflater.inflate(R.layout.item_grid_block, null); MainBean block = getItem(position); FrameLayout block_layout = view.findViewById(R.id.block_layout); TextView block_name = (TextView) view.findViewById(R.id.block_name); block_layout.setBackgroundResource(block.getBackgroundId()); block_name.setText(block.getName()); if (position == pullItemPosition) { block_layout.setVisibility(View.INVISIBLE); } else { block_layout.setVisibility(View.VISIBLE); } //返回含有数据的view return view; } @Override public int getCount() { return mBlockList.size(); } @Override public MainBean getItem(int position) { return mBlockList.get(position); } @Override public long getItemId(int position) { return position; } public void pull2Position(int newPosition) { if (newPosition < 0 || newPosition == pullItemPosition) { return; } int position = pullItemPosition; MainBean currBlock = getItem(position); remove(position); mBlockList.add(newPosition, currBlock); pullItemPosition = newPosition; notifyDataSetChanged(); } public void pullStart(int position) { if (position < 0) { return; } pullItemPosition = position; notifyDataSetChanged(); } public void pullFinish() { pullItemPosition = WAIT_POSITION; notifyDataSetChanged(); saveBlockOrder(); } private void remove(int position) { MainBean item = getItem(position); Iterator<MainBean> iterator = mBlockList.iterator(); while (iterator.hasNext()) { MainBean bean = iterator.next(); if (item.getFunctionId() == bean.getFunctionId()) { iterator.remove(); break; } } } private void saveBlockOrder() { //保存次序位置 } }
- 触屏部分
private void onGridTouchEvent() { mGvItemGrid.setOnTouchListener(this); } private WindowManager mWindowManager; private WindowManager.LayoutParams mLayoutParams; private void initWindowsManager() { mWindowManager = (WindowManager) getActivity().getSystemService(WINDOW_SERVICE); mLayoutParams = new WindowManager.LayoutParams(); mLayoutParams.gravity = Gravity.START | Gravity.TOP; mLayoutParams.width = DensityUtil.dpToPx(getContext(), 120); mLayoutParams.height = DensityUtil.dpToPx(getContext(), 120); } private void addItemView(int x, int y, int position) { if (position < 0) { return; } mLayoutParams.x = x; mLayoutParams.y = y; Log.i(TAG, "addItemView: 坐标X = " + x); Log.i(TAG, "addItemView: 坐标Y = " + y); mFloatItemView = View.inflate(getContext(), R.layout.item_grid_block, null); MainBean bean = mGridAdapter.getItem(position); FrameLayout layout = mFloatItemView.findViewById(R.id.block_layout); TextView textView = mFloatItemView.findViewById(R.id.block_name); layout.setBackgroundResource(bean.getBackgroundId()); textView.setText(bean.getName()); mWindowManager.addView(mFloatItemView, mLayoutParams); } private void pullItemView(int x, int y) { mLayoutParams.x = x; mLayoutParams.y = y; mWindowManager.updateViewLayout(mFloatItemView, mLayoutParams); } private void removeItemView() { mWindowManager.removeView(mFloatItemView); } private float startX, startY;//起始点击位置 private int positionX, positionY;//起始显示位置 @Override public boolean onTouch(View v, MotionEvent event) { float x = event.getX(); float y = event.getY(); int position = getGridViewPositionByPoint(mGvItemGrid, x, y); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = x; startY = y; positionX = getItemX(position); positionY = getItemY(position); addItemView(positionX, positionY, position); mGridAdapter.pullStart(position); break; case MotionEvent.ACTION_MOVE: int plusX = (int) (x - startX); int plusY = (int) (y - startY); pullItemView(positionX + plusX, positionY + plusY); mGridAdapter.pull2Position(position); break; case MotionEvent.ACTION_UP: removeItemView(); mGridAdapter.pullFinish(); break; default: break; } return false; } private int getItemX(int position) { if (position < 0) { return 0; } View child = mGvItemGrid.getChildAt(position); return child.getLeft() + DensityUtil.dpToPx(getContext(), 40);//margin只设置了20,不知道这里为什么是40,可能是设置的间距造成的 } private int getItemY(int position) { if (position < 0) { return 0; } View child = mGvItemGrid.getChildAt(position); return child.getTop() + DensityUtil.dpToPx(getContext(), 66); } public int getGridViewPositionByPoint(GridView gridView, float x, float y) { // 获取GridView的ChildCount int childCount = gridView.getChildCount(); // 遍历子视图来找到点击的位置 for (int i = 0; i < childCount; i++) { View child = gridView.getChildAt(i); // 获取子视图的坐标 int left = child.getLeft(); int top = child.getTop(); int right = child.getRight(); int bottom = child.getBottom(); // 检查点击坐标是否在子视图的范围内 if (x >= left && x <= right && y >= top && y <= bottom) { // 点击坐标在子视图范围内,返回position return i; } if (i == childCount - 1) { if (x >= left && y >= top) { return i; } } } // 没有找到对应的position,可能是点击在GridView之外 return -1; }
结尾
好了,基本上没费什么功夫,既不会影响和担心Gridview的性能,还基本实现了拖拉次序。效果也还不错。同理可以实现拖拉式RecyclerView。
以上,供您参考
参考文献: