这篇文章主要介绍了 Android 自定义 ListView 实现仿 QQ 可拖拽列表功能, 非常不错,具有参考借鉴价值,需要的朋友可以参考下
Android 是一种基于 Linux 的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由 Google 公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用 "安卓" 或 "安致"。
我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的 ListView 的数据,但是他的 Adapter,我们可以用系统封装好的,然后传递进去一个实体类,最后自定义一个 listview 去操作,所以我们先把准备的工作做好,比如?
list_item.xml
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageView
- android:id="@+id/iv_logo"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:layout_alignParentLeft="true"
- android:layout_centerInParent="true"
- android:layout_marginLeft="10dp"/>
- <TextView
- android:id="@+id/textView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:layout_marginLeft="10dp"
- android:layout_toRightOf="@id/iv_logo"/>
- </RelativeLayout>
这就只有一个头像和一句话了,然后我们把实体类也给写完了
DragBean
- package com.liuguilin.draglistviewsample.entity;
- /*
- * 项目名: DragListViewSample
- * 包名: com.liuguilin.draglistviewsample.entity
- * 文件名: DragBean
- * 创建者: LGL
- * 创建时间: 2016/8/29 22:49
- * 描述: 实体类
- */
- public class DragBean {
- private int ivId;
- private String text;
- public DragBean() {
- }
- public DragBean(int ivId, String text) {
- this.ivId = ivId;
- this.text = text;
- }
- public int getIvId() {
- return ivId;
- }
- public String getText() {
- return text;
- }
- }
ok, 其实很简单,id 是图片,然后是文本,这样我们就可以来实现一个 Adapter 了,这里我用的是 ArrayAdapter 这样能让我们插入和删除很轻松
DragAdapter
- package com.liuguilin.draglistviewsample.adapter;
- /*
- * 项目名: DragListViewSample
- * 包名: com.liuguilin.draglistviewsample.adapter
- * 文件名: DragAdapter
- * 创建者: LGL
- * 创建时间: 2016/8/29 22:41
- * 描述: 拖拽列表的数据源
- */
- import android.content.Context;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.ArrayAdapter;
- import android.widget.ImageView;
- import android.widget.TextView;
- import com.liuguilin.draglistviewsample.R;
- import com.liuguilin.draglistviewsample.entity.DragBean;
- import java.util.List;
- public class DragAdapter extends ArrayAdapter<DragBean> {
- /**
- * 构造方法
- *
- * @param context
- * @param mList
- */
- public DragAdapter(Context context, List<DragBean> mList) {
- super(context, 0, mList);
- }
- /**
- * 实现View
- *
- * @param position
- * @param convertView
- * @param parent
- * @return
- */
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View view;
- ViewHolder viewHolder;
- if (convertView == null) {
- view = View.inflate(getContext(), R.layout.list_item, null);
- viewHolder = new ViewHolder();
- viewHolder.imageView = (ImageView) view
- .findViewById(R.id.iv_logo);
- viewHolder.textView = (TextView) view.findViewById(R.id.textView);
- view.setTag(viewHolder);
- } else {
- view = convertView;
- viewHolder = (ViewHolder) view.getTag();
- }
- viewHolder.imageView.setImageResource(getItem(position).getIvId());
- viewHolder.textView.setText(getItem(position).getText());
- return view;
- }
- /**
- * 缓存
- */
- static class ViewHolder {
- ImageView imageView;
- TextView textView;
- }
- }
好的,其实到这里,他就是一个最普通的 ListView 了,我们给他填充点数据
MainActivity
- package com.liuguilin.draglistviewsample;
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import com.liuguilin.draglistviewsample.adapter.DragAdapter;
- import com.liuguilin.draglistviewsample.entity.DragBean;
- import com.liuguilin.draglistviewsample.view.DragListView;
- import java.util.ArrayList;
- import java.util.List;
- public class MainActivity extends AppCompatActivity {
- //列表
- private DragListView mListView;
- //数据
- private List<DragBean> mList = new ArrayList<>();
- //数据源
- private DragAdapter adapter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initView();
- }
- /**
- * 初始化View
- */
- private void initView() {
- mListView = (DragListView) findViewById(R.id.mListView);
- //新增數據
- for (int i = 0; i < 30; i++) {
- DragBean bean = new DragBean(R.mipmap.ic_launcher, "刘某人程序员" + i);
- mList.add(bean);
- }
- //初始化数据源
- adapter = new DragAdapter(this,mList);
- mListView.setAdapter(adapter);
- }
- }
现在可以看看实际的效果了
现在我们可以重写我们的 ListView 了
我们首先拦截他的事件
- /**
- * 获取触点所在条目的位置
- * 获取选中条目的图片
- * 事件的拦截机制
- *
- * @param ev
- * @return
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- //识别动作
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- //获取触点的坐标
- int x = (int) ev.getX();
- int y = (int) ev.getY();
- //这样就可以计算我按到哪个条目上了
- mStartPosition = mEndPosition = pointToPosition(x, y);
- //判断触点是否在logo的区域
- ViewGroup itemView = (ViewGroup) getChildAt(mStartPosition - getFirstVisiblePosition());
- //记录手指在条目中的相对Y坐标
- dragPoint = y - itemView.getTop();
- //ListView在屏幕中的Y坐标
- dragOffset = (int) (ev.getRawY() - y);
- //拖动的图标
- View dragger = itemView.findViewById(R.id.iv_logo);
- //判断触点是否在logo区域
- if (dragger != null && x < dragger.getRight() + 10) {
- //定义ListView的滚动条目
- //上
- upScroll = getHeight() / 3;
- //下
- downScroll = getHeight() * 2 / 3;
- //获取选中的图片/截图
- itemView.setDrawingCacheEnabled(true);
- //获取截图
- Bitmap bitMap = itemView.getDrawingCache();
- //图片滚动
- startDrag(bitMap, y);
- }
- break;
- }
- //还会传递事件到子View
- return false;
- }
获取到他的 position 之后我们直接截图,并且显示我们的 window, 这里做的事情就比较多了我们还要判断是否点击的是头像才去显示 window
- /**
- * 图片在Y轴,也就是上下可滚动
- *
- * @param bitMap
- * @param y
- */
- private void startDrag(Bitmap bitMap, int y) {
- //窗体仿照
- wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
- //设置窗体参数
- lParams = new WindowManager.LayoutParams();
- //设置宽高
- lParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
- lParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
- //属性
- lParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
- //设置半透明
- lParams.alpha = 0.8f;
- //设置居中
- lParams.gravity = Gravity.TOP;
- //设置xy
- lParams.x = 0;
- lParams.y = y-dragPoint + dragOffset;
- //属性
- lParams.format = PixelFormat.TRANSLUCENT;
- //设置动画
- lParams.windowAnimations = 0;
- //图片
- dragImageView = new ImageView(getContext());
- //设置截图
- dragImageView.setImageBitmap(bitMap);
- //添加显示窗体
- wm.addView(dragImageView, lParams);
- }
好的,我们初始化一下 window,光显示还不行呢,我们还要可以滑动,怎么监听?onTouchEvent 的 ACTION_MOVE 事件是可以做到的
- /**
- * 触摸事件
- *
- * @param ev
- * @return
- */
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- //错误的位置
- if (dragImageView != null && mEndPosition != INVALID_POSITION) {
- //在滑动事件中控制上下滑动
- switch (ev.getAction()) {
- case MotionEvent.ACTION_MOVE:
- //直接获取到Y坐标进行移动
- int moveY = (int) ev.getY();
- doDrag(moveY);
- break;
- //停止拖动成像
- case MotionEvent.ACTION_UP:
- int upY = (int) ev.getY();
- stopDrag();
- onDrag(upY);
- break;
- }
- }
- //拦截到事件
- return true;
- }
我们在移动的时候不断的去更新他的位置
- /**
- * 控制窗体移动
- *
- * @param moveY
- */
- private void doDrag(int moveY) {
- if (dragImageView != null) {
- lParams.y = moveY - dragPoint + dragOffset;
- wm.updateViewLayout(dragImageView, lParams);
- }
- //判断移动到分割线 返回-1
- int tempLine = pointToPosition(0, moveY);
- //我们处理他
- if (tempLine != INVALID_POSITION) {
- //只要你不移动到分割线 我才处理
- mEndPosition = tempLine;
- }
- //拖拽时滚动、滚动速度
- int scrollSpeed = 0;
- //上滚
- if (moveY < upScroll) {
- scrollSpeed = 10;
- //下滚
- } else if (moveY > downScroll) {
- scrollSpeed = -10;
- }
- //开始滚动
- if (scrollSpeed != 0) {
- //计算条目的Y坐标
- int dragItemY = getChildAt(mEndPosition - getFirstVisiblePosition()).getTop();
- //当前速度
- int dy = dragItemY + scrollSpeed;
- //设置
- setSelectionFromTop(mEndPosition, dy);
- }
- }
当你移动完成之后,我就可以停止你的 window 体了
- /**
- * 停止的位置
- */
- private void stopDrag() {
- //直接移除窗体
- if (dragImageView != null) {
- wm.removeView(dragImageView);
- dragImageView = null;
- }
- }
这样,我就直接拼接你的 position 达到一个拖拽的效果
- /**
- * 最终开始成像
- *
- * @param upY
- */
- private void onDrag(int upY) {
- //分割线的处理
- //判断移动到分割线 返回-1
- int tempLine = pointToPosition(0, upY);
- //我们处理他
- if (tempLine != INVALID_POSITION) {
- //只要你不移动到分割线 我才处理
- mEndPosition = tempLine;
- }
- /**
- * 你在最上方就直接落在第一个最下方就直接最下面一个
- */
- //上边界处理
- if (upY < getChildAt(1).getTop()) {
- mEndPosition = 1;
- //下边界处理
- } else if (upY > getChildAt(getChildCount() - 1).getTop()) {
- mEndPosition = getAdapter().getCount() - 1;
- }
- //开始更新item顺序
- if (mEndPosition > 0 && mEndPosition < getAdapter().getCount()) {
- DragAdapter adapter = (DragAdapter) getAdapter();
- //删除原来的条目
- adapter.remove(adapter.getItem(mStartPosition));
- //更新
- adapter.insert(adapter.getItem(mStartPosition), mEndPosition);
- }
- }
全部代码贴上
DragListView
- package com.liuguilin.draglistviewsample.view;
- /*
- * 项目名: DragListViewSample
- * 包名: com.liuguilin.draglistviewsample.view
- * 文件名: DragListView
- * 创建者: LGL
- * 创建时间: 2016/8/29 20:50
- * 描述: 自定义高仿QQ列表可拖拽的ListView
- */
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.PixelFormat;
- import android.util.AttributeSet;
- import android.view.Gravity;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.WindowManager;
- import android.widget.ImageView;
- import android.widget.ListView;
- import com.liuguilin.draglistviewsample.R;
- import com.liuguilin.draglistviewsample.adapter.DragAdapter;
- public class DragListView extends ListView {
- //按下选中的position
- private int mStartPosition;
- //需要达到的position
- private int mEndPosition;
- //手指在条目中的相对Y坐标
- private int dragPoint;
- //ListView在屏幕中的Y坐标
- private int dragOffset;
- //上
- private int upScroll;
- //下
- private int downScroll;
- //窗体
- private WindowManager wm;
- //显示的截图
- private ImageView dragImageView;
- //窗体参数
- private WindowManager.LayoutParams lParams;
- //构造方法
- public DragListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- /**
- * 获取触点所在条目的位置
- * 获取选中条目的图片
- * 事件的拦截机制
- *
- * @param ev
- * @return
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- //识别动作
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- //获取触点的坐标
- int x = (int) ev.getX();
- int y = (int) ev.getY();
- //这样就可以计算我按到哪个条目上了
- mStartPosition = mEndPosition = pointToPosition(x, y);
- //判断触点是否在logo的区域
- ViewGroup itemView = (ViewGroup) getChildAt(mStartPosition - getFirstVisiblePosition());
- //记录手指在条目中的相对Y坐标
- dragPoint = y - itemView.getTop();
- //ListView在屏幕中的Y坐标
- dragOffset = (int) (ev.getRawY() - y);
- //拖动的图标
- View dragger = itemView.findViewById(R.id.iv_logo);
- //判断触点是否在logo区域
- if (dragger != null && x < dragger.getRight() + 10) {
- //定义ListView的滚动条目
- //上
- upScroll = getHeight() / 3;
- //下
- downScroll = getHeight() * 2 / 3;
- //获取选中的图片/截图
- itemView.setDrawingCacheEnabled(true);
- //获取截图
- Bitmap bitMap = itemView.getDrawingCache();
- //图片滚动
- startDrag(bitMap, y);
- }
- break;
- }
- //还会传递事件到子View
- return false;
- }
- /**
- * 图片在Y轴,也就是上下可滚动
- *
- * @param bitMap
- * @param y
- */
- private void startDrag(Bitmap bitMap, int y) {
- //窗体仿照
- wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
- //设置窗体参数
- lParams = new WindowManager.LayoutParams();
- //设置宽高
- lParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
- lParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
- //属性
- lParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
- //设置半透明
- lParams.alpha = 0.8f;
- //设置居中
- lParams.gravity = Gravity.TOP;
- //设置xy
- lParams.x = 0;
- lParams.y = y-dragPoint + dragOffset;
- //属性
- lParams.format = PixelFormat.TRANSLUCENT;
- //设置动画
- lParams.windowAnimations = 0;
- //图片
- dragImageView = new ImageView(getContext());
- //设置截图
- dragImageView.setImageBitmap(bitMap);
- //添加显示窗体
- wm.addView(dragImageView, lParams);
- }
- /**
- * 触摸事件
- *
- * @param ev
- * @return
- */
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- //错误的位置
- if (dragImageView != null && mEndPosition != INVALID_POSITION) {
- //在滑动事件中控制上下滑动
- switch (ev.getAction()) {
- case MotionEvent.ACTION_MOVE:
- //直接获取到Y坐标进行移动
- int moveY = (int) ev.getY();
- doDrag(moveY);
- break;
- //停止拖动成像
- case MotionEvent.ACTION_UP:
- int upY = (int) ev.getY();
- stopDrag();
- onDrag(upY);
- break;
- }
- }
- //拦截到事件
- return true;
- }
- /**
- * 最终开始成像
- *
- * @param upY
- */
- private void onDrag(int upY) {
- //分割线的处理
- //判断移动到分割线 返回-1
- int tempLine = pointToPosition(0, upY);
- //我们处理他
- if (tempLine != INVALID_POSITION) {
- //只要你不移动到分割线 我才处理
- mEndPosition = tempLine;
- }
- /**
- * 你在最上方就直接落在第一个最下方就直接最下面一个
- */
- //上边界处理
- if (upY < getChildAt(1).getTop()) {
- mEndPosition = 1;
- //下边界处理
- } else if (upY > getChildAt(getChildCount() - 1).getTop()) {
- mEndPosition = getAdapter().getCount() - 1;
- }
- //开始更新item顺序
- if (mEndPosition > 0 && mEndPosition < getAdapter().getCount()) {
- DragAdapter adapter = (DragAdapter) getAdapter();
- //删除原来的条目
- adapter.remove(adapter.getItem(mStartPosition));
- //更新
- adapter.insert(adapter.getItem(mStartPosition), mEndPosition);
- }
- }
- /**
- * 停止的位置
- */
- private void stopDrag() {
- //直接移除窗体
- if (dragImageView != null) {
- wm.removeView(dragImageView);
- dragImageView = null;
- }
- }
- /**
- * 控制窗体移动
- *
- * @param moveY
- */
- private void doDrag(int moveY) {
- if (dragImageView != null) {
- lParams.y = moveY - dragPoint + dragOffset;
- wm.updateViewLayout(dragImageView, lParams);
- }
- //判断移动到分割线 返回-1
- int tempLine = pointToPosition(0, moveY);
- //我们处理他
- if (tempLine != INVALID_POSITION) {
- //只要你不移动到分割线 我才处理
- mEndPosition = tempLine;
- }
- //拖拽时滚动、滚动速度
- int scrollSpeed = 0;
- //上滚
- if (moveY < upScroll) {
- scrollSpeed = 10;
- //下滚
- } else if (moveY > downScroll) {
- scrollSpeed = -10;
- }
- //开始滚动
- if (scrollSpeed != 0) {
- //计算条目的Y坐标
- int dragItemY = getChildAt(mEndPosition - getFirstVisiblePosition()).getTop();
- //当前速度
- int dy = dragItemY + scrollSpeed;
- //设置
- setSelectionFromTop(mEndPosition, dy);
- }
- }
- }
然后我们引用
layout_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:layout_height="match_parent">
- <com.liuguilin.draglistviewsample.view.DragListView
- android:id="@+id/mListView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- </LinearLayout>
对了,别忘记了添加窗体的权限哦
- <!--窗口权限-->
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
- />
做完这一系列事情之后,就觉得这个设想其实是对的,然后我们可以尝试性的运行
以上所述是小编给大家介绍的 Android 自定义 ListView 实现仿 QQ 可拖拽列表功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 PHPERZ 网站的支持!
来源: http://www.phperz.com/article/17/0315/292089.html