展会信息港展会大全

Android超炫特效:ListView item拖拽效果
来源:互联网   发布日期:2015-11-26 11:19:47   浏览:4634次  

导读:一、准备。1 需求问题 初步:实现列表的拖拽效果(可参考Android源码下packages apps Music中的播放列表TouchInterceptor java源码)。下面以初步实现为例子,逐步展开实现步骤。2 搭建主界面DragListActiv ...

一、准备。

1.需求问题

初步:实现列表的拖拽效果(可参考Android源码下packages/apps /Music中的播放列表TouchInterceptor.java源码)。

下面以初步实现为例子,逐步展开实现步骤。

2.搭建主界面DragListActivity.java和主布局drag_list_activity.xml。

public class DragListActivity extends Activity {

//数据列表

private List<String> list = null;

//数据适配器

private DragListAdapter adapter = null;

//存放分组标签

public static List<String> groupKey= new ArrayList<String>();

//分组一

private List<String> navList = new ArrayList<String>();

//分组二

private List<String> moreList = new ArrayList<String>();

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.drag_list_activity);

//初始化样本数据

initData();

//后面会介绍DragListView

DragListView dragListView = (DragListView)findViewById(R.id.drag_list);

adapter = new DragListAdapter(this, list);

dragListView.setAdapter(adapter);

}

}

3.列表项的布局drag_list_item.xml。

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

<!-- 强调一点,使用相对布局 -->

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

android:layout_width="fill_parent"

android:layout_height="wrap_content">

<TextView

android:id="@+id/drag_list_item_text"

android:layout_width="wrap_content"

android:layout_height="@dimen/drag_item_normal_height"

android:paddingLeft="5dip"

android:layout_alignParentLeft="true"

android:layout_centerVertical="true"

android:gravity="center_vertical"/>

<ImageView android:id="@+id/drag_list_item_image"

android:src="@drawable/list_icon"

android:layout_alignParentRight="true"

android:layout_centerVertical="true"

android:layout_width="wrap_content"

android:layout_height="@dimen/drag_item_normal_height"/>

</RelativeLayout>

4.准备样本数据。

我已经准备好了两组数据,在前面提到的initData()方法中执行初始化。

public void initData(){

//数据结果

list = new ArrayList<String>();

//groupKey存放的是分组标签

groupKey.add("A组");

groupKey.add("B组");

for(int i=0; i<5; i++){

navList.add("A选项"+i);

}

list.add("A组");

list.addAll(navList);

for(int i=0; i<8; i++){

moreList.add("B选项"+i);

}

list.add("B组");

list.addAll(moreList);

}

这里定义了分组标签集合groupKey后面分组的时候会用到。

5.自定义适配器类DragListAdapter。

接着我们搭建数据适配器,负责把list的数据填充到ListView中。

public static class DragListAdapter extends ArrayAdapter<String>{

public DragListAdapter(Context context, List<String> objects) {

super(context, 0, objects);

}

@Override

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

View view = convertView;

if(view==null){

//加载列表项模板

view = LayoutInflater.from(getContext()).inflate(R.layout.drag_list_item, null);

}

TextView textView = (TextView)view.findViewById(R.id.drag_list_item_text);

textView.setText(getItem(position));

return view;

}

}

注意getItem(position)会取得数组适配器中position位置的T(这里是字符串),比较好用的一个方法。

至此,我们准备了一个正常的数据列表,效果如下:

二、实现

上面部分是我们的一个 准备工作,接下来我们通过自定义ListView,重写ListView中 onInterceptTouchEvent(),onTouchEvent()方法来响应触控事件做相应的界面调整(选中,拖动,数据更改后刷新界面) 等等。

6.自定义视图类。

//自定义ListView,准备改造成自己想要的ListView

//这样的好处是我们不仅可以直接使用ListView很多现成的稳定的方法,而且可以重写方法改写ListView的行为(利用的是java面向对象的继承特性,本人喜欢在任何代码中分析面向对象的特性、原则和模式)

public class DragListView extends ListView {

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

public DragListView(Context context, AttributeSet attrs) {

super(context, attrs);

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

}

}

7.重写触控拦截事件方法onInterceptTouchEvent()。

为了能在子控件响应触摸事件的情况下此ListView也能监听到触摸事件,我们把重写这个方法,做一些初始化工作。我们在这里捕获down事件,在 down事件中,我们做一些拖动的准备工作:

1)获取点击数据项,初始化一些变量;

2)判断是否是拖动还是仅仅点击;

3)如果是拖动,建立拖动影像;

这些工作是我们后面拖动的一个执行基础,非常重要。

//下面定义要使用的所有变量

private ImageView dragImageView;//被拖拽项的影像,其实就是一个ImageView

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

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

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

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

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

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

private int scaledTouchSlop;//判断滑动的一个距离

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

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

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

//捕获down事件

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

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

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

//选中的数据项位置,使用ListView自带的pointToPosition(x, y)方法

dragSrcPosition = dragPosition = pointToPosition(x, y);

//如果是无效位置(超出边界,分割线等位置),返回

if(dragPosition==AdapterView.INVALID_POSITION){

return super.onInterceptTouchEvent(ev);

}

//获取选中项View

//getChildAt(int position)显示display在界面的position位置的View

//getFirstVisiblePosition()返回第一个display在界面的view在adapter的位置position,可能是0,也可能是4

ViewGroup itemView = (ViewGroup) getChildAt(dragPosition-getFirstVisiblePosition());

//dragPoint点击位置在点击View内的相对位置

//dragOffset屏幕位置和当前ListView位置的偏移量,这里只用到y坐标上的值

//这两个参数用于后面拖动的开始位置和移动位置的计算

dragPoint = y - itemView.getTop();

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

//获取右边的拖动图标,这个对后面分组拖拽有妙用

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

//如果在右边位置(拖拽图片左边的20px的右边区域)

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

//准备拖动

//初始化拖动时滚动变量

//scaledTouchSlop定义了拖动的偏差位(一般+-10)

//upScrollBounce当在屏幕的上部(上面1/3区域)或者更上的区域,执行拖动的边界,downScrollBounce同理定义

upScrollBounce = Math.min(y-scaledTouchSlop, getHeight()/3);

downScrollBounce = Math.max(y+scaledTouchSlop, getHeight()*2/3);

//设置Drawingcache为true,获得选中项的影像bm,就是后面我们拖动的哪个头像

itemView.setDrawingCacheEnabled(true);

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

//准备拖动影像(把影像加入到当前窗口,并没有拖动,拖动操作我们放在onTouchEvent()的move中执行)

startDrag(bm, y);

}

return false;

}

return super.onInterceptTouchEvent(ev);

}

看到上面的一大堆变量和操作,你可能有些眼花缭乱,在后面使用的时候回头再去理解也可。

开始拖动影像startDrag()方法:

/**

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

* @param bm

* @param y

*/

public void startDrag(Bitmap bm ,int y){

//释放影像,在准备影像的时候,防止影像没释放,每次都执行一下

stopDrag();

windowParams = new WindowManager.LayoutParams();

//从上到下计算y方向上的相对位置,

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;

//把影像ImagView添加到当前视图中

ImageView imageView = new ImageView(getContext());

imageView.setImageBitmap(bm);

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

windowManager.addView(imageView, windowParams);

//把影像ImageView引用到变量drawImageView,用于后续操作(拖动,释放等等)

dragImageView = imageView;

}

stopDrag()方法如下:

/**

* 停止拖动,去除拖动项的头像

*/

public void stopDrag(){

if(dragImageView!=null){

windowManager.removeView(dragImageView);

dragImageView = null;

}

}

运行看看,我们点击一项的时候没有什么反应,但是细心观察的话,其实点击项上有一层淡淡的重影,这就是我们定义的点击项的影像,后面要做的就是拖动 这个影像,放下影像以及放下位置的数据项插入和原数据项位置的删除,这部分内容以及后续扩展的内容我们放到下篇中继续分析并实现。

分组的应用场合还是很多的,有数据集合的地方往往要分组显示;

分组的形式也很多,最常见的就是镶嵌在列表中,网上说的很多ExpandListView的也是一种。

Android自带的通讯录中的联系人是按照拼音首字母(A,B,C,D......)分组分类的,效果如下:

我 们今天也是要实现这样类似的一个效果。

1.样本数据:

为了突出重点,直击要点,这里提供一个整理好的数据样本:

//list:数据集合

private List<String> list = new ArrayList<String>();

//listTag:Tag集合,其中Tag是分类的分割标签,每个分组的header

private List<String> listTag = new ArrayList<String>();

public void setData(){

list.add("A");

listTag.add("A");

for(int i=0;i<3;i++){

list.add("阿凡达"+i);

}

list.add("B");

listTag.add("B");

for(int i=0;i<3;i++){

list.add("比特风暴"+i);

}

list.add("C");

listTag.add("C");

for(int i=0;i<30;i++){

list.add("查理风云"+i);

}

}

2.Activity 布局准备:

放置一个listView来呈现数据。

group_list_activity.xml:

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

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

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<!--简单的列表显示-->

<ListView android:id="@+id/group_list"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:cacheColorHint="#00000000"/>

</LinearLayout>

3.自定义Adapter(本文继承ArrayAdapter):

这个是本文的重点和核心。

Adapter接口为数据和界面搭建了一个访问的桥梁,最重要的就是getView()方法,用这个方法我们可以实现一定程度的界面自定义。

ArrayAdapter间接实现了Adapter接口,这里我们简单起见,数据源只是提供单一的String数组。

private static class GroupListAdapter extends ArrayAdapter<String>{

//存放标签的列表,用来判断数据项的类型

//如果数据项在标签列表中,则是标签项,否则是数据项

private List<String> listTag = null;

public GroupListAdapter(Context context, List<String> objects, List<String> tags) {

super(context, 0, objects);

this.listTag = tags;

}

@Override

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

... ....

}

}

我们来看看getView方法:

//该方法根据adapter的顺序一行一行的组织列表

//其中position表示第几行,也就是当前行在adapter的位置,

//convertView表示第几行的View

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

现在我们就是要重写getView方法,来实现列表中嵌入分组标签。

分组标签也是列表数据项之一,也是被一行一行的画上去的,但是它和其他数据项UI是不一致的,所以我们需要准备2套数据项布局模板:

数据项模板group_list_item.xml:

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

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

android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:padding="5dip">

<!-- 图片和文字 -->

<!-- 随便放了一张图片,稍微美化一下 -->

<ImageView

android:src="@drawable/list_icon"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

<TextView

android:id="@+id/group_list_item_text"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:paddingLeft="5dip"

android:gravity="center_vertical"/>

</LinearLayout>

标签项模板group_list_item_tag.xml:

<!-- 只有文字,但是高度小店,背景色设置为555555灰色 -->

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

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

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:background="#555555"

android:paddingLeft="10dip">

<TextView

android:id="@+id/group_list_item_text"

android:layout_width="wrap_content"

android:layout_height="20dip"

android:textColor="#ffffff"

android:gravity="center_vertical"/>

</LinearLayout>

好,我们现在把这两个模板应用到getView方法中去:

@Override

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

View view = convertView;

//根据标签类型加载不通的布局模板

if(listTag.contains(getItem(position))){

//如果是标签项

view = LayoutInflater.from(getContext()).inflate(R.layout.group_list_item_tag, null);

}else{

//否则就是数据项了

view = LayoutInflater.from(getContext()).inflate(R.layout.group_list_item, null);

}

//显示名称

TextView textView = (TextView) view.findViewById(R.id.group_list_item_text);

textView.setText(getItem(position));

//返回重写的view

return view;

}

4.禁止标签项的响应事件:

在ArrayAdapter的父类BaseAdapter中提供了isEnable的()方法,我们看看这个方法:

//默认情况,如果这个方法不是分割符,返回true

//分隔符是无选中和无点击事件的

//说白了,你想不想把改position项当做分隔符,想的话就返回false,否则返回true

public boolean isEnabled (int position)

这个方法刚好用来禁用标签项的响应事件。具体实现如下:

@Override

public boolean isEnabled(int position) {

if(listTag.contains(getItem(position))){

return false;

}

return super.isEnabled(position);

}

现在标签项不会再有任何触控效果了,犹如一块死木板。

5.完整代码:

整个Activity和Adapter代码如下:

public class GroupListActivity extends Activity {

private GroupListAdapter adapter = null;

private ListView listView = null;

private List<String> list = new ArrayList<String>();

private List<String> listTag = new ArrayList<String>();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.group_list_activity);

setData();

adapter = new GroupListAdapter(this, list, listTag);

listView = (ListView)findViewById(R.id.group_list);

listView.setAdapter(adapter);

}

public void setData(){

list.add("A");

listTag.add("A");

for(int i=0;i<3;i++){

list.add("阿凡达"+i);

}

list.add("B");

listTag.add("B");

for(int i=0;i<3;i++){

list.add("比特风暴"+i);

}

list.add("C");

listTag.add("C");

for(int i=0;i<30;i++){

list.add("查理风云"+i);

}

}

private static class GroupListAdapter extends ArrayAdapter<String>{

private List<String> listTag = null;

public GroupListAdapter(Context context, List<String> objects, List<String> tags) {

super(context, 0, objects);

this.listTag = tags;

}

@Override

public boolean isEnabled(int position) {

if(listTag.contains(getItem(position))){

return false;

}

return super.isEnabled(position);

}

@Override

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

View view = convertView;

if(listTag.contains(getItem(position))){

view = LayoutInflater.from(getContext()).inflate(R.layout.group_list_item_tag, null);

}else{

view = LayoutInflater.from(getContext()).inflate(R.layout.group_list_item, null);

}

TextView textView = (TextView) view.findViewById(R.id.group_list_item_text);

textView.setText(getItem(position));

return view;

}

}

}

6.最终效果:

赞助本站

人工智能实验室

相关热词: ListView item 拖拽 效果

相关内容
AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港