Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现


我们做一些好友列表或者商品列表的时候,居多的需求可能就是需要列表拖拽了,而我们选择了ListView,也是因为使用ListView太久远了,导致对他已经有浓厚的感情了,我们之前也是写过几篇关于ListView的博文

  • Android实训案例(三)——实现时间轴效果的ListView,加入本地存储,实现恋爱日记的效果!

  • Android高级控件(一)——ListView绑定CheckBox实现全选,增加和删除等功能

  • Android高级控件(五)——如何打造一个企业级应用对话列表,以QQ,微信为例

而今天我们就来实现以下课拖拽的方案,大家可以借鉴以下

我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的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*/@Overridepublic 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;@Overrideprotected 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*/@Overridepublic 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;}//还会传递事件到子Viewreturn 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;//设置xylParams.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*/@Overridepublic 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);}//判断移动到分割线 返回-1int 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) {//分割线的处理//判断移动到分割线 返回-1int 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 {//按下选中的positionprivate int mStartPosition;//需要达到的positionprivate 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*/@Overridepublic 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;}//还会传递事件到子Viewreturn 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;//设置xylParams.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*/@Overridepublic 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) {//分割线的处理//判断移动到分割线 返回-1int 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);}//判断移动到分割线 返回-1int 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"/>

做完这一系列事情之后,就觉得这个设想其实是对的,然后我们可以尝试性的运行一下

运行时成功的,说明我们的设想是没有问题的,但是随着而来的,也是诸多的bug,所以呢,这也只是思想的参考,不过这些bug我有时间野就修复一下,应该是可以的,好的,本篇博文就先到这里!

有兴趣的加群:555974449

DragListViewSample:http://download.csdn.net/detail/qq_26787115/9616355

Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现相关推荐

  1. Android跨屏拖动item,android中如何实现两个listview中的item可以互相拖拽移位?

    这两天自己baidu+google,简单解决了一些问题,暂且先贴上来. 因为是我的一个作业,所以还有一些按钮的点击事件,大家可以忽略. 这些源码仅仅实现了简单的"将制定控件拖拽到某个区域&q ...

  2. Android高级控件(一)——ListView绑定CheckBox实现全选,增加和删除等功能

    Android高级控件(一)--ListView绑定CheckBox实现全选,增加和删除等功能 这个控件还是挺复杂的,也是项目中应该算是比较常用的了,所以写了一个小Demo来讲讲,主要是自定义adap ...

  3. Android高级控件----AdapterView与Adapter详解

    在J2EE中提供过一种非常好的框架--MVC框架,实现原理:数据模型M(Model)存放数据,利用控制器C(Controller)将数据显示在视图V(View)上.在Android中有这样一种高级控件 ...

  4. 2022 最新 Android 基础教程,从开发入门到项目实战【b站动脑学院】学习笔记——第八章:高级控件

    本章介绍了App开发常用的一些高级控件用法,主要包括:如何使用下拉框及其适配器.如何使用列表 类视图及其适配器.如何使用翻页类视图及其适配器.如何使用碎片及其适配器等.然后结合本章所学 的知识,演示了 ...

  5. Android 高级控件(七)——RecyclerView的方方面面

    Android 高级控件(七)--RecyclerView的方方面面 RecyclerView出来很长时间了,相信大家都已经比较了解了,这里我把知识梳理一下,其实你把他看成一个升级版的ListView ...

  6. Android的高级控件(自动提示文本框与下拉列表)

    一.高级控件与常用控件的区别:是否使用适配器 二.适配器 1.种类 ①.数组适配器 ArrayAdapter       new ArrayAdapter<String>(this,R.l ...

  7. Android高级控件(二)——SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现...

    Android高级控件(二)--SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现 写这个的原因呢,也是因为项目中用到了gif动画,虽然网上有很多的架包可以实现,不过我们还 ...

  8. Android从入门到进阶之高级控件

    高级控件 1.自动完成文本控件 在搜索引擎(百度.搜狗)输入几个文字,会出来一些提示.可以减少用户的输入,提高程序的效率.刚才看到的自动提示,ajax技术. 1.1AotoCompleteTextVi ...

  9. 移动开发技术(Android)——实验5 Android高级控件的应用

    移动开发技术--实验5 Android高级控件的应用 一.实验目的 二.实验内容 1.Spinner.ListView控件与Adapter适配器(一) 2.Spinner.ListView控件与Ada ...

  10. RecyclerView高级控件-附实例【android专题】

    RecyclerView高级控件 RecyclerView是Android 及其重要的一个高级UI控件,使用频率及其的高,APP的90%以上的页面都会使用的到.由于其出色的性能和插拔式的架构设计,被全 ...

最新文章

  1. MathType在手,公式不求人!
  2. 未来教育python视频百度云-青橙课程 | 人工智能走进课堂,为未来教育高质量发展赋能!...
  3. python读取json文件转化为list_Python从所有子目录读取JSON文件
  4. 积极的活下去本身就是挺好的一件事情了
  5. Tachyon更名为 Alluxio,并发布1.0版本
  6. leetcode 3 --- 无重复字符的最长子串
  7. IntelAI开发平台OpenVINO
  8. compose部署redis和mysql_浅析docker-compose部署mysql无法访问的问题
  9. Bartender条码打印软件,提示30天以后该软件将会过期
  10. kali破解wifi密码
  11. 全国计算机自动化办公专业人才证书,办公自动化证书有什么用
  12. 基于Windows2003实现网关-网关虚拟专用网络
  13. oracle 生成随机姓名_Oracle生成随机码
  14. Mac苹果电脑怎样安装Python
  15. 如何提升计算机的网络性能,技术丨怎样提升电脑性能?
  16. AcWing 365. 圆桌骑士
  17. 分治问题CodeForces768B大数
  18. ERROR:Xst:899--FPGA ERROR
  19. 云原生之史上最全K8S环境搭建(强烈建议收藏)
  20. 滚动字幕Marquee代码大全

热门文章

  1. 【算法学习】【图像增强】基于拉普拉斯算子的图像锐化
  2. 用VC++封装自己的DLL动态链接库
  3. 基于C#和DGAL包实现栅格影像的读取和显示
  4. 【LeetCode】【数组】题号:*645,重复数字和缺失数字
  5. python划分train val test
  6. r语言TeachingDemos包绘制黑白脸谱图
  7. CASS9.1计算土方量实例
  8. SSM使用拦截器功能
  9. ImageLoader的简单分析(终结篇)
  10. Apache Flink 进阶(二):时间属性深度解析