android 自定义listview不能滑动,Android自定义ListView实现仿QQ可拖拽列表功能

本文介绍了如何在Android中自定义一个ListView,实现仿QQ的可拖拽列表功能。通过创建DragBean实体类,使用ArrayAdapter填充数据,然后在ListView中拦截触摸事件,实现拖拽过程中截图并显示在窗口上,同时监听ACTION_MOVE和ACTION_UP事件来处理拖动和释放操作,最终完成列表项的拖拽排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的ListView的数据,但是他的Adapter,我们可以用系统封装好的,然后传递进去一个实体类,最后自定义一个listview去操作,所以我们先把准备的工作做好,比如?

list_item.xml

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent">

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"/>

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"/>

这就只有一个头像和一句话了,然后我们把实体类也给写完了

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 {

/**

* 构造方法

*

* @param context

* @param mList

*/

public DragAdapter(Context context, List 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 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);

}

}

现在可以看看实际的效果了

a6ba0e23d4948672dbf33421b196da9e.png

现在我们可以重写我们的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

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:orientation="vertical"

android:layout_height="match_parent">

android:id="@+id/mListView"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

对了,别忘记了添加窗体的权限哦

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

45791a542d2d6479cdf60b061ebb123b.gif

以上所述是小编给大家介绍的Android自定义ListView实现仿QQ可拖拽列表功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值