首页 > 代码库 > ListView 无所不包

ListView 无所不包

1介绍 Listview节约内存的几种写法
2技巧一:解决ListView的ItemView中带有Button时,OnItemClick无效的问题 
3技巧二Listview滚动条的属性
4技巧三有关美化 android ListView的美化涉及到的一些属性
5精品代码:android开发之横向滚动/竖向滚动的ListView(固定列头)
6代码2   android ListView和GridView拖拽移位实现代码


一.Listview节约内存的几种写法 

这里有一个优化的地方,就是重用view,这样减少内存消耗,同时加快item加载速度。 

在getView中优化的地方,大家想必都非常情况,下面我总结了三种优化的写法,请大家指正。 

第一这是常规写法了 

重用了convertView,很大程度上的减少了内存的消耗。通过判断convertView是否为null,是的话就需要产生一个视图出来,然后给这个视图数据,最后将这个视图返回给底层,呈献给用户。 

特点:如果当前的convertView为null,则通过LayoutInflat产生一个view。 

复制代码 代码如下:


ViewCode 

public View getView(intposition,ViewconvertView,ViewGroupparent) 

if(convertView==null) 

convertView=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null); 

TextViewt v_name=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_name); 

TextViewtv_phone=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_phoneNum); 

ContactInfo  confo=contacts.get(position); 

if(confo!=null){//toseteveryitem‘stext 

tv_name.setText(confo.getContactName()); 

tv_phone.setText(confo.getContact_Phone()); 

returnconvertView; 



: 

反正现在开发都这么写了,那就这么写吧。

特点,使用了内部类classViewHolder、重用了convertView。 

区别第二种写法是,使用了一个临时变量Viewview=convertView,然后修改view,最后返回view 

@Override 

public View getView(int position,View convertView,ViewGroup parent) 

View view=convertView; 

ViewHolder holder; 

if(view==null){ 

view=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null); 

holder=new ViewHolder(); 

holder.tv_name=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_name); 

holder.tv_phone=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_phoneNum); 

view.setTag(holder); 

else 

holder=(ViewHolder)view.getTag(); 

ContactInfo confo=contacts.get(position); 

Log.i("my","confo"+confo.getContactName()); 

if(confo!=null){//toseteveryitem‘stext 


holder.tv_name.setText(confo.getContactName()); 

holder.tv_phone.setText(confo.getContact_Phone()); 

returnview; 

classViewHolder 

TextViewtv_name,tv_phone; 


以上就是集中写法,供新手学习和总结。 

 

 

 

技巧一:解决ListView的ItemView中带有Button时,OnItemClick无效的问题

button增加

[html] view plaincopy

android:focusable="false"  

android:clickable="false"  


contentView增加:

[html] view plaincopy

android:descendantFocusability="blocksDescendants"  

 

 目的:阻止Button获得触摸事件和点击事件

 

 

 

技巧二Listview滚动条的属性

本文主要介绍android viewandroid:scrollbarStyle属性意义android:scrollbarStyle可以定义滚动条的样式和位置,可选值有insideOverlayinsideInsetoutsideOverlayoutsideInset四种。



  其中insideoutside分别表示是否在viewpadding区域内,overlayinset表示覆盖在view上或是插在view后面,所以四种值分别表示:



  insideOverlay:默认值,表示在padding区域内并且覆盖在view

  insideInset:表示在padding区域内并且插入在view后面

  outsideOverlay:表示在padding区域外并且覆盖在view上,推荐这个

  outsideInset:表示在padding区域外并且插入在view后面

 


   个人觉得outsideOverlay最合适,视觉效果最好。

  我们可以在xml中定义android:scrollbarStyle属性,如下:

  android:scrollbarStyle="outsideOverlay"

 

 

 

 

技巧三有关美化 android ListView的美化涉及到的一些属性

 (2011-12-14 12:08:08)

标签: 

杂谈

分类: android

用心的朋友应该会发现,listview中在设置了背景之后。会有些问题。


1.listview在拖动的时候背景图片消失变成黑色背景。等到拖动完毕我们自己的背景图片才显示出来。


listview的上边和下边有黑色的阴影。


3lsitview的每一项之间需要设置一个图片做为间隔。


针对以上问题 在listviewxml文件中设置一下语句。


问题有如下代码结解决 android:scrollingCache="false"


问题用如下代码解决:android:fadingEdge="none"  

问题3  用如下代码解决:  android:divider="@drawable/list_driver"  其中  @drawable/list_driver 是一个图片资源

总体如下

 

1

<ListView

 

2

  android:id="@+id/myListView01"

 

3

  android:layout_width="fill_parent"

 

4

  android:layout_height="287dip"

 

5

  android:fadingEdge="none"  

 

6

  android:divider="@drawable/list_driver"

 

7

  android:scrollingCache="false"

 

8

  android:background="@drawable/list">

 

android:fadeScrollbars="true"

 

9

  </ListView>


4. 自定义listview的时候,当你不使用android:cacheColorHint=“#00000000”会出现下面选中一个空间黑色底色的情况,破坏整体美观度:



5. 当你不使用android:listSelector属性,默认会显示选中的item为橙黄底色,有时候我们需要去掉这种效果:

xml文件中的ListView控件中加入如下属性:

android:listSelector="@drawable/timer_list_selector"

drawable中定义timer_list_selector的属性值

timer_list_selector.xml中定义如下:

<?xml version="1.0" encoding="utf-8"?>

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

    <item android:state_selected="true" android:drawable="@android:color/transparent" />

</selector>

values文件夹下的colors.xml中定义transparent如下:

<color name="transparent">#50000000</color>

 

 

 

精品代码:android开发之横向滚动/竖向滚动的ListView(固定列头)

由于项目需要,我们需要一个可以横向滚动的,又可以竖向滚动的 表格;经过几天的研究终于搞定,感兴趣的朋友可以了解下哦

 

 

http://yunpan.cn/cgSrLvraWHfNY  提取码 cfda

 

 

 

 

 

代码2  android ListViewGridView拖拽移位实现代码

 

 

 

有些朋友对android中ListView和GridView拖拽移位功能的实现不是很了解,接下来将详细介绍,需要了解的朋友可以参考下

关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例:

            


首先说一下:拖拽ListViewitem就不应该可以任意移动,只应该在ListView所在的范围内,而网易的你看看我都可以移动到状态栏了,虽然你做了处理,但是用户体验我个人感觉不好,在看看百度的,不仅控制了移动范围,更不错的百度的移动起来会时时的换位,看起来相当的形象,所以我认为这样相当的棒.

说明一点,我没有那么有才,我也是看别人代码,然后自己整理下.在这里就简单记载一下.

首先对touch事件的处理,从应用中,我们可以得出,在我们点击后面拖拉图标后,就会创建一个item的影像视图.并且可以移动该影像,而此时的ListView不应该有touch事件.

onInterceptTouchEvent方法.

[java]

复制代码 代码如下:


/*** 

* touch事件拦截 

*/ 

@Override 

public boolean onInterceptTouchEvent(MotionEvent ev) { 

// 按下 

if (ev.getAction() == MotionEvent.ACTION_DOWN) { 

int x = (int) ev.getX();// 获取相对与ListViewx坐标 

int y = (int) ev.getY();// 获取相应与ListViewy坐标 

dragSrcPosition = dragPosition = pointToPosition(x, y); 

// 无效不进行处理 

if (dragPosition == AdapterView.INVALID_POSITION) { 

return super.onInterceptTouchEvent(ev); 


// 获取当前位置的视图(可见状态

ViewGroup itemView = (ViewGroup) getChildAt(dragPosition 

- getFirstVisiblePosition()); 


// 获取到的dragPoint其实就是在你点击指定item项中的高度

dragPoint = y - itemView.getTop(); 

// 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏)

dragOffset = (int) (ev.getRawY() - y); 


// 获取可拖拽的图标 

View dragger = itemView.findViewById(R.id.iv_drag_list_item_2); 


// x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略) 

if (dragger != null && x > dragger.getLeft() - 20) { 


upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3 

downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3 


itemView.setDrawingCacheEnabled(true);// 开启cache. 

Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象

startDrag(bm, y);// 初始化影像 

// return false; 


return super.onInterceptTouchEvent(ev); 


这个方法的作用很简单:当我们摁下的如果是可拖拽的图标,那么进行初始化该Item的映像试图.

而在这里如果大家对WindowManagerWindowManager.LayoutParams不熟悉的朋友先去参考下这篇文章,要对WindowManager有一定的了解,简单的会应用.

接下来我们看onTouchEvent事件:

[java]

/** 

触摸事件处理 

*/ 

@Override 

public boolean onTouchEvent(MotionEvent ev) { 

// itemview不为空,且获取的dragPosition有效 

if (dragImageView != null && dragPosition != INVALID_POSITION) { 

int action = ev.getAction(); 

switch (action) { 

case MotionEvent.ACTION_UP: 

int upY = (int) ev.getY(); 

stopDrag(); 

onDrop(upY); 

break; 

case MotionEvent.ACTION_MOVE: 

int moveY = (int) ev.getY(); 

onDrag(moveY); 

break; 

case MotionEvent.ACTION_DOWN: 

break; 

default: 

break; 

return true;// 取消ListView滑动


return super.onTouchEvent(ev); 


简单说明:首先在Touch中,我们要进行判断,是否点击的是拖动图标,如果是的话,那么对ACTION_MOVE and ACTION_UP相应事件进行处理,并且返回true or false.作用:取消ListView自身的Touch事件.如果不是的话,执行ListView 本身的Touch事件.

大致就介绍这么多,具体的实现,还是大家看源码吧,我注释的还算清晰,只要大家仔细看的话,一定可以掌握的,为什么这么说呢,技术只有在掌握了情况下才可以进行拓展.

对了,提醒大家要理解这三句话:

getRawX()和getRawY():获得的是相对屏幕的位置.

getX()和getY():获得的永远是相对view的触摸位置 坐标(这两个值不会超过view的长度和宽度)。

getLeft , getTop, getBottom,getRight, 这个指的是该控件相对于父控件的距离.

源码:

[java] 

复制代码 代码如下:


package com.jj.drag; 


import android.content.Context; 

import android.graphics.Bitmap; 

import android.os.AsyncTask; 

import android.util.AttributeSet; 

import android.util.Log; 

import android.view.Gravity; 

import android.view.MotionEvent; 

import android.view.View; 

import android.view.ViewConfiguration; 

import android.view.ViewGroup; 

import android.view.WindowManager; 

import android.widget.AbsListView; 

import android.widget.AbsListView.OnScrollListener; 

import android.widget.AdapterView; 

import android.widget.ImageView; 

import android.widget.ListView; 


import com.jj.drag.MainActivity.DragListAdapter; 


/*** 

自定义拖拽ListView 

* @author zhangjia 

*/ 

public class DragListView extends ListView { 


private WindowManager windowManager;// windows窗口控制类 

private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数 


private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24) 


private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView 

private int dragSrcPosition;// 手指拖动项原始在列表中的位置 

private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置


private int dragPoint;// 在当前数据项中的位置 

private int dragOffset;// 当前视图和屏幕的距离(这里只使用了y方向上


private int upScrollBounce;// 拖动的时候,开始向上滚动的边界 

private int downScrollBounce;// 拖动的时候,开始向下滚动的边界 


private final static int step = 1;// ListView 滑动步伐


private int current_Step;// 当前步伐


/*** 

构造方法 

* @param context 

* @param attrs 

*/ 

public DragListView(Context context, AttributeSet attrs) { 

super(context, attrs); 


/*** 

* touch事件拦截 

*/ 

@Override 

public boolean onInterceptTouchEvent(MotionEvent ev) { 

// 按下 

if (ev.getAction() == MotionEvent.ACTION_DOWN) { 

int x = (int) ev.getX();// 获取相对与ListViewx坐标 

int y = (int) ev.getY();// 获取相应与ListViewy坐标 

dragSrcPosition = dragPosition = pointToPosition(x, y); 

// 无效不进行处理 

if (dragPosition == AdapterView.INVALID_POSITION) { 

return super.onInterceptTouchEvent(ev); 


// 获取当前位置的视图(可见状态

ViewGroup itemView = (ViewGroup) getChildAt(dragPosition 

- getFirstVisiblePosition()); 


// 获取到的dragPoint其实就是在你点击指定item项中的高度

dragPoint = y - itemView.getTop(); 

// 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏)

dragOffset = (int) (ev.getRawY() - y); 


// 获取可拖拽的图标 

View dragger = itemView.findViewById(R.id.iv_drag_list_item_2); 


// x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略) 

if (dragger != null && x > dragger.getLeft() - 20) { 


upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3 

downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3 


itemView.setDrawingCacheEnabled(true);// 开启cache. 

Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象

startDrag(bm, y);// 初始化影像 


return super.onInterceptTouchEvent(ev); 


/** 

触摸事件处理 

*/ 

@Override 

public boolean onTouchEvent(MotionEvent ev) { 

// itemview不为空,且获取的dragPosition有效 

if (dragImageView != null && dragPosition != INVALID_POSITION) { 

int action = ev.getAction(); 

switch (action) { 

case MotionEvent.ACTION_UP: 

int upY = (int) ev.getY(); 

stopDrag(); 

onDrop(upY); 

break; 

case MotionEvent.ACTION_MOVE: 

int moveY = (int) ev.getY(); 

onDrag(moveY); 

break; 

case MotionEvent.ACTION_DOWN: 

break; 

default: 

break; 

return true;// 取消ListView滑动


return super.onTouchEvent(ev); 


/** 

准备拖动,初始化拖动项的图像 

* @param bm 

* @param y 

*/ 

private void startDrag(Bitmap bm, int y) { 

// stopDrag(); 

/*** 

初始化window. 

*/ 

windowParams = new WindowManager.LayoutParams(); 

windowParams.gravity = Gravity.TOP; 

windowParams.x = 0; 

windowParams.y = y - dragPoint + dragOffset; 

windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT; 

windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT; 


windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE// 不需获取焦点 

| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE// 不需接受触摸事件 

| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON// 保持设备常开,并保持亮度不变。 

| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;// 窗口占满整个屏幕,忽略周围的装饰边框(例如状态栏)。此窗口需考虑到装饰边框的内容。 


// windowParams.format = PixelFormat.TRANSLUCENT;// 默认为不透明,这里设成透明效果

windowParams.windowAnimations = 0;// 窗口所使用的动画设置 


ImageView imageView = new ImageView(getContext()); 

imageView.setImageBitmap(bm); 

windowManager = (WindowManager) getContext().getSystemService("window"); 

windowManager.addView(imageView, windowParams); 

dragImageView = imageView; 



/** 

拖动执行,在Move方法中执行 

* @param y 

*/ 

public void onDrag(int y) { 

int drag_top = y - dragPoint;// 拖拽viewtop值不能<0,否则则出界

if (dragImageView != null && drag_top >= 0) { 

windowParams.alpha = 0.5f;// 透明度 

windowParams.y = y - dragPoint + dragOffset;// 移动y.//记得要加上dragOffsetwindowManager计算的是整个屏幕.(标题栏和状态栏都要算上

windowManager.updateViewLayout(dragImageView, windowParams);// 时时移动

// 为了避免滑动到分割线的时候,返回-1的问题 

int tempPosition = pointToPosition(0, y); 

if (tempPosition != INVALID_POSITION) { 

dragPosition = tempPosition; 


doScroller(y); 


/*** 

* ListView的移动

要明白移动原理:当映像移动到下端的时候,ListView向上滑动,当映像移动到上端的时候,ListView要向下滑动。正好和实际的相反

*/ 


public void doScroller(int y) { 

Log.e("jj", "y=" + y); 

Log.e("jj", "upScrollBounce=" + upScrollBounce); 

// ListView需要下滑 

if (y < upScrollBounce) { 

current_Step = step + (upScrollBounce - y) / 10;// 时时步伐 

}// ListView需要上滑 

else if (y > downScrollBounce) { 

current_Step = -(step + (y - downScrollBounce)) / 10;// 时时步伐 

} else { 

current_Step = 0; 


// 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position) 

View view = getChildAt(dragPosition - getFirstVisiblePosition()); 

// 真正滚动的方法setSelectionFromTop() 

setSelectionFromTop(dragPosition, view.getTop() + current_Step); 



/** 

停止拖动,删除影像 

*/ 

public void stopDrag() { 

if (dragImageView != null) { 

windowManager.removeView(dragImageView); 

dragImageView = null; 


/** 

拖动放下的时候 

* @param y 

*/ 

public void onDrop(int y) { 


// 为了避免滑动到分割线的时候,返回-1的问题 

int tempPosition = pointToPosition(0, y); 

if (tempPosition != INVALID_POSITION) { 

dragPosition = tempPosition; 


// 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置

if (y < getChildAt(0).getTop()) { 

// 超出上边界 

dragPosition = 0; 

// 如果拖动超过最后一项的最下边那么就防止在最下边 

} else if (y > getChildAt(getChildCount() - 1).getBottom()) { 

// 超出下边界 

dragPosition = getAdapter().getCount() - 1; 


// 数据交换 

if (dragPosition < getAdapter().getCount()) { 

DragListAdapter adapter = (DragListAdapter) getAdapter(); 

adapter.update(dragSrcPosition, dragPosition); 



}


下面我说下适配器:

[java] 

复制代码 代码如下:


/*** 

自定义适配器 

* @author zhangjia 

*/ 

class DragListAdapter extends BaseAdapter { 

private ArrayList<String> arrayTitles; 

private ArrayList<Integer> arrayDrawables; 

private Context context; 


public DragListAdapter(Context context, ArrayList<String> arrayTitles, 

ArrayList<Integer> arrayDrawables) { 

this.context = context; 

this.arrayTitles = arrayTitles; 

this.arrayDrawables = arrayDrawables; 


@Override 

public View getView(int position, View convertView, ViewGroup parent) { 

View view = convertView; 

/*** 

在这里尽可能每次都进行实例化新的,这样在拖拽ListView的时候不会出现错乱

具体原因不明,不过这样经过测试,目前没有发现错乱。虽说效率不高,但是做拖拽LisView足够了。 

*/ 

view = LayoutInflater.from(context).inflate( 

R.layout.drag_list_item, null); 


TextView textView = (TextView) view 

.findViewById(R.id.tv_drag_list_item_text); 

ImageView imageView = (ImageView) view 

.findViewById(R.id.iv_drag_list_item_1); 

imageView.setImageResource(arrayDrawables.get(position)); 

textView.setText(arrayTitles.get(position)); 

return view; 


/*** 

动态修改ListVIiw的方位

* @param start 

点击移动的position 

* @param down 

松开时候的position 

*/ 

public void update(int start, int down) { 

// 获取删除的东东

String title = arrayTitles.get(start); 

int drawable_id = arrayDrawables.get(start); 


arrayTitles.remove(start);// 删除该项 

arrayDrawables.remove(start);// 删除该项 


arrayTitles.add(down, title);// 添加删除项 

arrayDrawables.add(down, drawable_id);// 添加删除项 


notifyDataSetChanged();// 刷新ListView 


@Override 

public int getCount() { 


return Title.length; 


@Override 

public Object getItem(int position) { 

return Title[position]; 


@Override 

public long getItemId(int position) { 

return position; 



这里不过多解释了,相信大家都看的明白.如果疑问请留言.

展示下运行效果:

效果看起来还行吧,如果觉得不错的话,记得要赞一个哦.

下面我们接着修改,模拟百度嘛,谁让百度这么牛叉呢.

思路:点中拖拉图标的时候,每次移动只要dragPosition发生改变,也就是我移动到了下一个位置,那么此时我就进行交换执行update.并且除了第一次移动外,在每次交换后要除去映射源的显示,这样用户觉得这里的空位就是就是为我准备的,比较人性化.

实现起来并不复杂,前提是你得掌握上面的操作.

源码如下;

[java]

复制代码 代码如下:


package com.jj.drag; 


import android.content.Context; 

import android.graphics.Bitmap; 

import android.graphics.Color; 

import android.os.AsyncTask; 

import android.util.AttributeSet; 

import android.util.Log; 

import android.view.Gravity; 

import android.view.MotionEvent; 

import android.view.View; 

import android.view.ViewConfiguration; 

import android.view.ViewGroup; 

import android.view.WindowManager; 

import android.widget.AbsListView; 

import android.widget.AbsListView.OnScrollListener; 

import android.widget.AdapterView; 

import android.widget.ImageView; 

import android.widget.ListView; 


import com.jj.drag.MainActivity.DragListAdapter; 


public class DragListView extends ListView { 


private WindowManager windowManager;// windows窗口控制类 

private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数 


private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24) 


private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView 

private int dragSrcPosition;// 手指拖动项原始在列表中的位置 

private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置


private int dragPoint;// 在当前数据项中的位置 

private int dragOffset;// 当前视图和屏幕的距离(这里只使用了y方向上


private int upScrollBounce;// 拖动的时候,开始向上滚动的边界 

private int downScrollBounce;// 拖动的时候,开始向下滚动的边界 


private final static int step = 1;// ListView 滑动步伐


private int current_Step;// 当前步伐


private int temChangId;// 临时交换id 


private boolean isLock;// 是否上锁


public void setLock(boolean isLock) { 

this.isLock = isLock; 


public DragListView(Context context, AttributeSet attrs) { 

super(context, attrs); 

scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 


/*** 

* touch事件拦截 在这里我进行相应拦截, 

*/ 

@Override 

public boolean onInterceptTouchEvent(MotionEvent ev) { 

// 按下 

if (ev.getAction() == MotionEvent.ACTION_DOWN && !isLock) { 

int x = (int) ev.getX();// 获取相对与ListViewx坐标 

int y = (int) ev.getY();// 获取相应与ListViewy坐标 

temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y); 

// 无效不进行处理 

if (dragPosition == AdapterView.INVALID_POSITION) { 

return super.onInterceptTouchEvent(ev); 


// 获取当前位置的视图(可见状态

ViewGroup itemView = (ViewGroup) getChildAt(dragPosition 

- getFirstVisiblePosition()); 


// 获取到的dragPoint其实就是在你点击指定item项中的高度

dragPoint = y - itemView.getTop(); 

// 这个值是固定的:其实就是ListView这个控件与屏幕最顶部的距离(一般为标题栏+状态栏)

dragOffset = (int) (ev.getRawY() - y); 


// 获取可拖拽的图标 

View dragger = itemView.findViewById(R.id.iv_drag_list_item_2); 


// x > dragger.getLeft() - 20这句话为了更好的触摸(-20可以省略) 

if (dragger != null && x > dragger.getLeft() - 20) { 


upScrollBounce = getHeight() / 3;// 取得向上滚动的边际,大概为该控件的1/3 

downScrollBounce = getHeight() * 2 / 3;// 取得向下滚动的边际,大概为该控件的2/3 

itemView.setBackgroundColor(Color.BLUE); 

itemView.setDrawingCacheEnabled(true);// 开启cache. 

Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());// 根据cache创建一个新的bitmap对象

startDrag(bm, y);// 初始化影像 

return false; 


return super.onInterceptTouchEvent(ev); 


/** 

触摸事件处理 

*/ 

@Override 

public boolean onTouchEvent(MotionEvent ev) { 

// itemview不为空,且获取的dragPosition有效 

if (dragImageView != null && dragPosition != INVALID_POSITION 

&& !isLock) { 

int action = ev.getAction(); 

switch (action) { 

case MotionEvent.ACTION_UP: 

int upY = (int) ev.getY(); 

stopDrag(); 

onDrop(upY); 

break; 

case MotionEvent.ACTION_MOVE: 

int moveY = (int) ev.getY(); 

onDrag(moveY); 


break; 

case MotionEvent.ACTION_DOWN: 

break; 

default: 

break; 

return true;// 取消ListView滑动


return super.onTouchEvent(ev); 


/** 

准备拖动,初始化拖动项的图像 

* @param bm 

* @param y 

*/ 

private void startDrag(Bitmap bm, int y) { 

// stopDrag(); 

/*** 

初始化window. 

*/ 

windowParams = new WindowManager.LayoutParams(); 

windowParams.gravity = Gravity.TOP; 

windowParams.x = 0; 

windowParams.y = y - dragPoint + dragOffset; 

windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT; 

windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT; 


windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE// 不需获取焦点 

| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE// 不需接受触摸事件 

| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON// 保持设备常开,并保持亮度不变。 

| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;// 窗口占满整个屏幕,忽略周围的装饰边框(例如状态栏)。此窗口需考虑到装饰边框的内容。 


// windowParams.format = PixelFormat.TRANSLUCENT;// 默认为不透明,这里设成透明效果

windowParams.windowAnimations = 0;// 窗口所使用的动画设置 


ImageView imageView = new ImageView(getContext()); 

imageView.setImageBitmap(bm); 

windowManager = (WindowManager) getContext().getSystemService("window"); 

windowManager.addView(imageView, windowParams); 

dragImageView = imageView; 



/** 

拖动执行,在Move方法中执行 

* @param y 

*/ 

public void onDrag(int y) { 

int drag_top = y - dragPoint;// 拖拽viewtop值不能<0,否则则出界

if (dragImageView != null && drag_top >= 0) { 

windowParams.alpha = 0.5f; 

windowParams.y = y - dragPoint + dragOffset; 

windowManager.updateViewLayout(dragImageView, windowParams);// 时时移动

// 为了避免滑动到分割线的时候,返回-1的问题 

int tempPosition = pointToPosition(0, y); 

if (tempPosition != INVALID_POSITION) { 

dragPosition = tempPosition; 


onChange(y);// 时时交换 


doScroller(y);// listview移动


/*** 

* ListView的移动

要明白移动原理:当我移动到下端的时候,ListView向上滑动,当我移动到上端的时候,ListView要向下滑动。正好和实际的相反

*/ 


public void doScroller(int y) { 

// Log.e("jj", "y=" + y); 

// Log.e("jj", "upScrollBounce=" + upScrollBounce); 

// ListView需要下滑 

if (y < upScrollBounce) { 

current_Step = step + (upScrollBounce - y) / 10;// 时时步伐 

}// ListView需要上滑 

else if (y > downScrollBounce) { 

current_Step = -(step + (y - downScrollBounce)) / 10;// 时时步伐 

} else { 

current_Step = 0; 


// 获取你拖拽滑动到位置及显示item相应的view上(注:可显示部分)(position) 

View view = getChildAt(dragPosition - getFirstVisiblePosition()); 

// 真正滚动的方法setSelectionFromTop() 

setSelectionFromTop(dragPosition, view.getTop() + current_Step); 



/** 

停止拖动,删除影像 

*/ 

public void stopDrag() { 

if (dragImageView != null) { 

windowManager.removeView(dragImageView); 

dragImageView = null; 


/*** 

拖动时时change 

*/ 

private void onChange(int y) { 

// 数据交换 

if (dragPosition < getAdapter().getCount()) { 

DragListAdapter adapter = (DragListAdapter) getAdapter(); 

adapter.isHidden = false; 

if (dragPosition != temChangId) { 

adapter.update(temChangId, dragPosition); 

temChangId = dragPosition;// 将点击最初所在位置position付给临时的,用于判断是否换位


// 为了避免滑动到分割线的时候,返回-1的问题 

int tempPosition = pointToPosition(0, y); 

if (tempPosition != INVALID_POSITION) { 

dragPosition = tempPosition; 


// 超出边界处理(如果向上超过第二项Top的话,那么就放置在第一个位置

if (y < getChildAt(0).getTop()) { 

// 超出上边界 

dragPosition = 0; 

// 如果拖动超过最后一项的最下边那么就防止在最下边 

} else if (y > getChildAt(getChildCount() - 1).getBottom()) { 

// 超出下边界 

dragPosition = getAdapter().getCount() - 1; 



/** 

拖动放下的时候 

* @param y 

*/ 

public void onDrop(int y) { 

// 数据交换 

if (dragPosition < getAdapter().getCount()) { 

DragListAdapter adapter = (DragListAdapter) getAdapter(); 

adapter.isHidden = false; 

adapter.notifyDataSetChanged();// 刷新



因为我们要时时交换位置,所以将原先的拖动方法onDrop方法移动到onChange.具体的还是看源码吧.

另外的就是对适配器的修改,因为你要对特殊的item进行隐藏之类的操作,这些代码我就不写了,我会将案例上传网上,不懂的可以下载源码.

好了还是我们来观看下效果吧.

怎么样,这个效果看起来要比上面那个效果更人性化点吧,我的操作或许有点快,不信的话,你自己手机体验一下吧.

关于ListView拖拽就说到这里,如有不足请大家自己创新.

下面我们接着对GridView的拖拽简单说明.因为这些在项目中我们都会用到,所以既然做到就做全面点吧.好了大家接着往下看吧.

首先说明,原理一样,都是拖动映像,记录拖动位置,然后调用notifyDataSetChanged更新UI.

GridView不同的是你要根据xy值共同获取点击的position和移动至的position,而ListView因为不涉及x坐标.

嗯,最初的原始移动我就不给大家展示了,效果也不是很友好,我直接展示时时更新的那种方法.效果类是与上面那个时时更新ListView一样。

原理也一样.下面我们直接看代码吧.

[java]

复制代码 代码如下:


package com.jj.draggrid; 


import java.util.logging.Handler; 


import com.jj.draggrid.MainActivity.DragGridAdapter; 


import android.content.Context; 

import android.graphics.Bitmap; 

import android.graphics.PixelFormat; 

import android.util.AttributeSet; 

import android.util.Log; 

import android.view.Gravity; 

import android.view.MotionEvent; 

import android.view.View; 

import android.view.ViewGroup; 

import android.view.WindowManager; 

import android.widget.AdapterView; 

import android.widget.BaseAdapter; 

import android.widget.GridView; 

import android.widget.ImageView; 

import android.widget.Toast; 


/*** 

自定义拖拽GridView 

* @author zhangjia 

*/ 


public class DragGridView extends GridView { 


private WindowManager windowManager;// windows窗口控制类 

private WindowManager.LayoutParams windowParams;// 用于控制拖拽项的显示的参数 


private int scaledTouchSlop;// 判断滑动的一个距离,scroll的时候会用到(24) 


private ImageView dragImageView;// 被拖拽的项(item),其实就是一个ImageView 

private int dragSrcPosition;// 手指拖动项原始在列表中的位置 

private int dragPosition;// 手指点击准备拖动的时候,当前拖动项在列表中的位置


private int dragPointX;// 在当前数据项中的位置 

private int dragPointY;// 在当前数据项中的位置 

private int dragOffsetX;// 当前视图和屏幕的距离(这里只使用了x方向上

private int dragOffsetY;// 当前视图和屏幕的距离(这里只使用了y方向上


private int upScrollBounce;// 拖动的时候,开始向上滚动的边界 

private int downScrollBounce;// 拖动的时候,开始向下滚动的边界 


private int temChangId;// 临时交换id 


private boolean isDoTouch = false;// touch是否可用 


private boolean isHide = false;// 是否隐藏 


private Handler handler; 


public void setDoTouch(boolean isDoTouch) { 

this.isDoTouch = isDoTouch; 


public DragGridView(Context context, AttributeSet attrs) { 

super(context, attrs); 


@Override 

public boolean onInterceptTouchEvent(MotionEvent ev) { 


if (ev.getAction() == MotionEvent.ACTION_DOWN) { 

int x = (int) ev.getX(); 

int y = (int) ev.getY(); 


temChangId = dragSrcPosition = dragPosition = pointToPosition(x, y); 


if (dragPosition == AdapterView.INVALID_POSITION) { 

return super.onInterceptTouchEvent(ev); 


ViewGroup itemView = (ViewGroup) getChildAt(dragPosition 

- getFirstVisiblePosition()); 


dragPointX = x - itemView.getLeft(); 

dragPointY = y - itemView.getTop(); 

dragOffsetX = (int) (ev.getRawX() - x); 

dragOffsetY = (int) (ev.getRawY() - y); 


View dragger = itemView.findViewById(R.id.drag_grid_item); 


/*** 

判断是否选中拖动图标 

*/ 

if (dragger != null && dragPointX > dragger.getLeft() 

&& dragPointX < dragger.getRight() 

&& dragPointY > dragger.getTop() 

&& dragPointY < dragger.getBottom() + 20) { 


upScrollBounce = getHeight() / 4; 

downScrollBounce = getHeight() * 3 / 4; 


itemView.setDrawingCacheEnabled(true); 

Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache()); 

startDrag(bm, x, y);// 初始话映像 


dragger.setVisibility(View.INVISIBLE);// 隐藏该项


return super.onInterceptTouchEvent(ev); 


@Override 

public boolean onTouchEvent(MotionEvent ev) { 


if (dragImageView != null && dragPosition != INVALID_POSITION 

&& isDoTouch) { 

int action = ev.getAction(); 

switch (action) { 

/*** 

*/ 

case MotionEvent.ACTION_UP: 

int upX = (int) ev.getX(); 

int upY = (int) ev.getY(); 

stopDrag();// 删除映像 

onDrop(upX, upY);// 松开 

// isDoTouch = false; 

break; 

/*** 

拖拽item 

*/ 

case MotionEvent.ACTION_MOVE: 

int moveX = (int) ev.getX(); 

int moveY = (int) ev.getY(); 

onDrag(moveX, moveY);// 拖拽 

break; 

case MotionEvent.ACTION_DOWN: 

int downX = (int) ev.getX(); 

int downY = (int) ev.getY(); 

onHide(downX, downY);// 隐藏该项 

break; 

default: 

break; 

return true; 

return super.onTouchEvent(ev); 

/** 

准备拖动,初始化拖动项的图像 

* @param bm 

* @param y 

*/ 

public void startDrag(Bitmap bm, int x, int y) { 

windowParams = new WindowManager.LayoutParams(); 

windowParams.gravity = Gravity.TOP | Gravity.LEFT; 

windowParams.x = x - dragPointX + dragOffsetX; 

windowParams.y = y - dragPointY + dragOffsetY; 

windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT; 

windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT; 

windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 

| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 

| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 

| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 

windowParams.windowAnimations = 0; 

ImageView imageView = new ImageView(getContext()); 

imageView.setImageBitmap(bm); 

windowManager = (WindowManager) getContext().getSystemService("window"); 

windowManager.addView(imageView, windowParams); 

dragImageView = imageView; 

/*** 

拖动时时change 

*/ 

private void onChange(int x, int y) { 

// 获取适配器 

DragGridAdapter adapter = (DragGridAdapter) getAdapter(); 

// 数据交换 

if (dragPosition < getAdapter().getCount()) { 

// 不相等的情况下要进行换位,相等的情况下说明正在移动 

if (dragPosition != temChangId) { 

adapter.update(temChangId, dragPosition);// 进行换位 

temChangId = dragPosition;// 将点击最初所在位置position付给临时的,用于判断是否换位

// 为了避免滑动到分割线的时候,返回-1的问题 

int tempPosition = pointToPosition(x, y); 

if (tempPosition != INVALID_POSITION) { 

dragPosition = tempPosition; 

/*** 

拖动执行,在Move方法中执行 

* @param x 

* @param y 

*/ 

public void onDrag(int x, int y) { 

// 移动 

if (dragImageView != null) { 

windowParams.alpha = 0.8f; 

windowParams.x = x - dragPointX + dragOffsetX; 

windowParams.y = y - dragPointY + dragOffsetY; 

windowManager.updateViewLayout(dragImageView, windowParams); 

onChange(x, y);// 时时交换 

// 滚动 

if (y < upScrollBounce || y > downScrollBounce) { 

// 使用setSelection来实现滚动 

setSelection(dragPosition); 

/*** 

隐藏该选项 

*/ 

private void onHide(int x, int y) { 

// 获取适配器 

DragGridAdapter adapter = (DragGridAdapter) getAdapter(); 

// 为了避免滑动到分割线的时候,返回-1的问题 

int tempPosition = pointToPosition(x, y); 

if (tempPosition != INVALID_POSITION) { 

dragPosition = tempPosition; 

adapter.setIsHidePosition(dragPosition); 

/** 

停止拖动,删除影像 

*/ 

public void stopDrag() { 

if (dragImageView != null) { 

windowManager.removeView(dragImageView); 

dragImageView = null; 


/*** 

拖动放下的时候 

* @param x 

* @param y 

*/ 

public void onDrop(int x, int y) { 


DragGridAdapter adapter = (DragGridAdapter) getAdapter(); 

adapter.setIsHidePosition(-1);// 不进行隐藏 

 

ListView 无所不包