文章目录
-
-
- 前言
- 效果图
- 实现原理
- 核心代码
- 非核心代码部分
- 项目地址
-
前言
这篇博客只写了拖动排序,具体的订阅功能,有需要的小伙伴也可以看下我之前写的关于订阅功能实现的一篇博文👇👇👇(写的比较早,现在有了更简便的方法,下次再写一篇新的)!
Android应用分类订阅功能(新闻个性化分类订阅)
效果图
这里可以看到,长按某个Item以后,进行拖动,可以实现将此Item拖动到该GridView中其他位置的功能
实现原理
通过实现自定义GridView重写onTouchEvent方法以及实现onItemLongClick接口来实现长按一个item然后用窗口显示出来,根据手指的移动来移动显示的窗口,最后通过判断手指离开屏幕的位置来确定最终item移动到的位置。
核心代码
DragGridView.java
首先是继承自GridView,并且实现OnItemLongClickListener接口,再重写一下onTouchEvent方法;
整体逻辑(理解万岁):
1、长按某个Item时:在onTouchEvent中获取当前手指在屏幕上的位置(记录坐标);在OnItemLongClickListener中通过获取当前Item的图像资源放入ImageView中,然后通过WindowManager窗口将这个图像方法1.2倍显示在屏幕上,并且隐藏掉长按的Item;
2、长按以后拖动时:让Windowmanager窗口随着手指移动,并且判断移动的过程中有没有移动到别的Item上,如果有那就进行排序;
3、停止操作以后:将隐藏掉的Item显示回来,并且调用WindowManager的
.removeView()
方法移除ImageView图像
这里该写的注释基本上都写了,这里面有几个Adapter里面写的方法,Adapter的代码在这个的下面;
public class DragGridView extends GridView implements AdapterView.OnItemLongClickListener {
private ImageView dragIiewView;//ImageView,图像容器
private WindowManager windowManager;//窗口
private WindowManager.LayoutParams dragParams;//用于记录窗口展示的位置
private int oldPos;//用于记录拖动的item的position
private int rawX;//用于记录初始坐标X
private int rawY;//用于记录初始坐标Y
private boolean isDrag;//用来判断当前是否是在拖动的状态
public DragGridView(Context context) {
super(context);
initView();
}
public DragGridView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public DragGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
setOnItemLongClickListener(this);
dragIiewView = new ImageView(getContext());
windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
dragParams = new WindowManager.LayoutParams();
}
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
//记下item的position
oldPos = i;
//获取长按item的DrawingCache
view.destroyDrawingCache();
view.setDrawingCacheEnabled(true);
//获取item的Bitmap
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
//设置拖动参数、以及设置显示出来可以拖动的item放大1.2倍
dragParams.gravity = Gravity.TOP | Gravity.START;
dragParams.width = (int) (1.2f * bitmap.getWidth());
dragParams.height = (int) (1.2f * bitmap.getHeight());
//获取拖动的中心点
dragParams.x = rawX - dragParams.width / 2;
dragParams.y = rawY - dragParams.height / 2;
dragParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
dragParams.format = PixelFormat.TRANSLUCENT;
dragParams.windowAnimations = 0;
//设置ImageView的图像为本次长按的item的图像
dragIiewView.setImageBitmap(bitmap);
//将ImageView显示到屏幕上
windowManager.addView(dragIiewView, dragParams);
//设置当前状态为true
isDrag = true;
//设置长按的item隐藏
((DragGridViewAdapter) getAdapter()).hideView(i);
return true;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//当手指触碰屏幕时,记录下坐标
rawX = (int) ev.getRawX();
rawY = (int) ev.getRawY();
} else if (isDrag && ev.getAction() == MotionEvent.ACTION_MOVE) {
//item被拖动时,记录拖动过程中的(当前)坐标
dragParams.x = (int) (ev.getRawX() - dragIiewView.getWidth() / 2);
dragParams.y = (int) (ev.getRawY() - dragIiewView.getHeight() / 2);
//更新窗口显示
windowManager.updateViewLayout(dragIiewView, dragParams);
//获取拖动过程中,触摸点所在GridView中item的位置(position)
int newPos = pointToPosition(((int) ev.getX()), (int) ev.getY());
//如果当前位置不等于上次停留的位置,则交换两次位置的item
if (newPos != AdapterView.INVALID_POSITION && newPos != oldPos) {
((DragGridViewAdapter) getAdapter()).updataView(oldPos, newPos);
oldPos = newPos;
}
} else if (isDrag && ev.getAction() == MotionEvent.ACTION_UP) {
//操作停止时(手指离开屏幕),回归正常显示状态
((DragGridViewAdapter) getAdapter()).showHideView();
//清空窗口
windowManager.removeView(dragIiewView);
//状态改成false
isDrag = false;
}
return super.onTouchEvent(ev);
}
}
DragGridViewAdapter.java
整个适配器除了加了几个方法以外,和正常的Adapter没啥区别,这里主要有三个方法:
1、
hideView
,这个方法用于隐藏长按的那个Item(如果你的Item比较复杂,这个隐藏的方法可以改写,这里不具体讲怎么改)
2、
showHideView
,这个方法用于显示
hideView
方法隐藏掉的Item(用于拖动操作进行完了以后)
3、
updataView
,更新GridView的视图显示,主要逻辑是:通过判断移动前Item的position和移动后Item的position,进行比较再进行相应的数据排序(这里排序的实质是进行数据的插入和移除)
public class DragGridViewAdapter extends BaseAdapter {
private List<String> list;
private int hidePos = AdapterView.INVALID_POSITION;
public DragGridViewAdapter(List<String> list) {
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return list.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
class ViewHolder {
TextView txv;
}
//隐藏item
public void hideView(int pos) {
hidePos = pos;
notifyDataSetChanged();
}
//显示隐藏了的item
public void showHideView() {
hidePos = AdapterView.INVALID_POSITION;
notifyDataSetChanged();
}
//处理当item的位置发生改变时,更新显示
public void updataView(int oldPos, int newPos) {
if (oldPos > newPos) {//item从后往前拖动
list.add(newPos, list.get(oldPos));
list.remove(oldPos + 1);
} else if (oldPos < newPos) {//item从前往后拖动
list.add(newPos + 1, list.get(oldPos));
list.remove(oldPos);
}
hidePos = newPos;
notifyDataSetChanged();
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder = new ViewHolder();
if (view == null) {
view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.drag_grid_item, null);
viewHolder.txv = (TextView) view.findViewById(R.id.txv);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
if (i != hidePos) {
viewHolder.txv.setText(list.get(i));
} else {
viewHolder.txv.setText("");
}
return view;
}
}
写到这里,会写GridView的小伙伴可以忽略下面的非核心代码部分,Activity中和正常写GridView一样(加数据、实例化适配器、GridView调用setAdapter()方法)
非核心代码部分
DragGridViewActivity.java
这里就简单的加了20组数据,然后实例化Adapter、调用
setAdapter()
方法
public class DragGridViewActivity extends AppCompatActivity {
private DragGridView gvDrag;
private List<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_grid_view);
initView();
list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
list.add("小黑" + i);
}
DragGridViewAdapter adapter = new DragGridViewAdapter(list);
gvDrag.setAdapter(adapter);
}
private void initView() {
gvDrag = (DragGridView) findViewById(R.id.gv_drag);
}
}
activity_drag_grid_view.xml
这里就是一个简单的自己写的DragGridView(自定义的那个GridView)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DragGridViewActivity">
<com.example.asyu.draggridview.DragGridView
android:id="@+id/gv_drag"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="5" />
</LinearLayout>
drag_grid_item.xml
这里也是随便写了个Item
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txv"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="#619db4"
android:gravity="center" />
</LinearLayout>
项目地址
项目地址:DragGridView项目地址(GitHub)