亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

自定義ListView實現拖拽ListItem項交換位置(附源碼)

2020-04-11 12:14:30
字體:
來源:轉載
供稿:網友
寫在前面的話
上一篇實現了通過布局泵拿到不同布局為listitem布局,然后實現聯系人的ListView,這一章要做的是拖拽ListView的Item項,本章原理是在上一篇博客基礎之上的,上一篇博客:自定義Adapter并通過布局泵LayoutInflater抓取layout模板編輯每一個item

實現效果圖
 

說明
首先我們看到的上面這張圖就是實現的效果圖了。拖動之后數據項完成交換位置。

功能剖析
我們看到做的這個效果是一個拖拽ListView的Item項位置的功能,在布局方面還是用基于布局泵LayoutInflater來從不同的Layout模板拿到不同的布局然后將view返回。關于布局這一點的知識在上一篇有詳細說明,文章開頭已經說明,OK,下面我們來剖析一下這個拖動效果的實現吧,下面的文章將會以方法執行的順序來給出各個方法的代碼。然后依次剖析每個方法的作用。

方法執行順序
[DragView] -> [初始化ListViewContext,和觸發move事件的最小距離變量]
[onInterceptTouchEvent] -> [初始化拖動的變量]
[startDrag] -> [準備拖動的影像、window等變量]
[stopDrag] -> [判斷重置拖動的影像]
[startDrag] -> [準備拖動的影像、window等變量]
[onTouchEvent] -> [判斷點擊事件、根據動作做不同操作,或重新繪制Move影響,或者Stop停止拖動。交換數據,也就是下面的這兩個方法]
[onDrag] -> [實現滾動的動作]
[onDrop] -> [實現數據item位置切換]
注意
上面的方法執行順序只是大概邏輯,這其中還有判斷和方法中調用其他方法,所以方法的調用是多次的,大家湊合看一下方法的大體功能吧,需要注意的是我們重寫的onTouchEvent是要一直不斷的監聽我們的按鍵的,如果為Move的話也就是會一直不斷的去調用onDrag方法去實現滾動的動作,在此我做了測試,給大家看一下打印的日志如下:
 
我們看到move日志被執行了多次。說明我們在拖動的時候一直在執行這個方法。下面我會給大家自定義ListView的代碼,代碼中注釋量很大,可讀性很高,推薦大家參考上面我給出的方法運行順序去理解,最后我將給出運行源碼。
復制代碼 代碼如下:

package com.example.draglistview;
import com.example.draglistview.MainActivity.DragListAdapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;
public class DragView extends ListView{
private ImageView imageView; //被拖動的圖片
private int scaledTouchSlop; //判斷拖動的距離

private int dragSrcPosition; //手指在touch事件觸摸時候的原始位置
private int dragPosition; //手指拖動列表項時候的位置

private int dragPoint; //手指點擊位置在當前數據項item中的位置,只有Y坐標
private int dragOffset; //當前視圖listview在屏幕中的位置,只有Y坐標

private int upScrollBounce; //向上滑動的邊界
private int downScrollBounce; //拖動的時候向下滑動的邊界

private WindowManager windowManager = null; //窗口管理類
//窗口參數類
private WindowManager.LayoutParams layoutParams = null;



//注意該View如果在Layout xml 注冊使用的話必須使用下面的這個構造進行初始化
public DragView(Context context, AttributeSet attrs) {
super(context, attrs);
//觸發移動事件的最小距離
scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
//重寫于absListView
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

if(ev.getAction() == MotionEvent.ACTION_DOWN){
//獲取的該touch事件的x坐標和y坐標,該坐標是相對于組件的左上角的位置
int x = (int) ev.getX();
int y = (int) ev.getY();
//賦值手指點擊時候的開始坐標
dragSrcPosition = dragPosition = this.pointToPosition(x, y);
//如果點擊在列表之外,也就是不允許的位置
if(dragPosition == AdapterView.INVALID_POSITION){
//直接執行父類,不做任何操作
return super.onInterceptTouchEvent(ev);
}

/***
* 鎖定手指touch的列表item,
* 參數為屏幕的touch坐標減去listview左上角的坐標
* 這里的getChildAt方法參數為相對于組件左上角坐標為00的情況
* 故有下面的這種參數算法
*/
ViewGroup itemView = (ViewGroup) this.getChildAt(dragPosition-this.getFirstVisiblePosition());
/****
* 說明:getX Y為touch點相對于組件左上角的距離
* getRawX 、Y 為touch點相對于屏幕左上角的距離
* 參考http://blog.csdn.net/love_world_/article/details/8164293
*/
//touch點的view相對于該childitem的top坐標的距離
dragPoint = y-itemView.getTop();
//為距離屏幕左上角的Y減去距離組件左上角的Y,其實就是
//組件上方的view+標題欄+狀態欄的Y
dragOffset = (int) (ev.getRawY()-y);

//拿到拖動的imageview對象
View drager = itemView.findViewById(R.id.imageView1);

//判斷條件為拖動touch圖片是否為null和touch的位置,是否符合
if(drager != null && x>drager.getLeft()-20){

//判斷得出向上滑動和向下滑動的值
upScrollBounce = Math.min(y-scaledTouchSlop, getHeight()/3);
downScrollBounce = Math.max(y+scaledTouchSlop, getHeight()*2/3);
//啟用繪圖緩存
itemView.setDrawingCacheEnabled(true);
//根據圖像緩存拿到對應位圖
Bitmap bm = Bitmap.createBitmap(itemView.getDrawingCache());
startDrag(bm, y);
}
return false;
}
return super.onInterceptTouchEvent(ev);
}


//重寫OnTouchEvent,觸摸事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(imageView != null && dragPosition != INVALID_POSITION){
int currentAction = ev.getAction();

switch (currentAction) {
case MotionEvent.ACTION_UP:
int upY = (int) ev.getY();
//還有一些操作
stopDrag();
onDrop(upY);
break;
case MotionEvent.ACTION_MOVE:
Log.v("move", "move---------");
int moveY = (int) ev.getY();
onDrag(moveY);
break;
default:
break;
}
return true;
}
//決定了選中的效果
return super.onTouchEvent(ev);
}



/****
* 準備拖動,初始化拖動時的影像,和一些window參數
* @param bm 拖動緩存位圖
* @param y 拖動之前touch的位置
*/
public void startDrag(Bitmap bm,int y){
stopDrag();
layoutParams = new WindowManager.LayoutParams();
//設置重力
layoutParams.gravity = Gravity.TOP;
//橫軸坐標不變
layoutParams.x = 0;
/**
*
* y軸坐標為 視圖相對于自身左上角的Y-touch點在列表項中的y
* +視圖相對于屏幕左上角的Y,=
* 該view相對于屏幕左上角的位置
*/
layoutParams.y = y-dragPoint+dragOffset;
/****
* 寬度和高度都為wrapContent
*/
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

/****
* 設置該layout參數的一些flags參數
*/
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
//設置該window項是半透明的格式
layoutParams.format = PixelFormat.TRANSLUCENT;
//設置沒有動畫
layoutParams.windowAnimations = 0;

//配置一個影像ImageView
ImageView imageViewForDragAni = new ImageView(getContext());
imageViewForDragAni.setImageBitmap(bm);
//配置該windowManager
windowManager = (WindowManager) this.getContext().getSystemService("window");
windowManager.addView(imageViewForDragAni, layoutParams);
imageView = imageViewForDragAni;
}

/***
* 停止拖動,去掉拖動時候的影像
*/
public void stopDrag(){
if(imageView != null){
windowManager.removeView(imageView);
imageView = null;
}
}


/****
* 拖動方法
* @param y
*/
public void onDrag(int y){

if(imageView != null){
//透明度
layoutParams.alpha = 0.8f;
layoutParams.y = y-this.dragPoint+this.dragOffset;
windowManager.updateViewLayout(imageView, layoutParams);
}


//避免拖動到分割線返回-1
int tempPosition = this.pointToPosition(0, y);
if(tempPosition != this.INVALID_POSITION){
this.dragPosition = tempPosition;
}


int scrollHeight = 0;
if(y<upScrollBounce){
scrollHeight = 8;//定義向上滾動8個像素,如果可以向上滾動的話
}else if(y>downScrollBounce){
scrollHeight = -8;//定義向下滾動8個像素,,如果可以向上滾動的話
}

if(scrollHeight!=0){
//真正滾動的方法setSelectionFromTop()
setSelectionFromTop(dragPosition, getChildAt(dragPosition-getFirstVisiblePosition()).getTop()+scrollHeight);
}
}


/***
* 拖動放下的時候
* param : y
*/
public void onDrop(int y){
int tempPosition = this.pointToPosition(0, y);
if(tempPosition != this.INVALID_POSITION){
this.dragPosition = tempPosition;
}

//超出邊界處理
if(y<getChildAt(1).getTop()){
//超出上邊界
dragPosition = 1;
}else if(y>getChildAt(getChildCount()-1).getBottom()){
//超出下邊界
dragPosition = getAdapter().getCount()-1;
//
}
//數據交換
if(dragPosition>0&&dragPosition<getAdapter().getCount()){
@SuppressWarnings("unchecked")
DragListAdapter adapter = (DragListAdapter)getAdapter();
//原始位置的item
String dragItem = adapter.getItem(dragSrcPosition);
adapter.remove(dragItem);
adapter.insert(dragItem, dragPosition);
Toast.makeText(getContext(), adapter.getList().toString(), Toast.LENGTH_SHORT).show();
}
}
}

寫在后面的話
以上就為自定義ListView的源碼了。當然還有部分未給出的代碼。包括MainActivity、3個Layout xml 文件。[比較簡單] 如果你看懂了[或者有一些疑問]上面的這部分代碼,你可以下載我的源碼查相關的API和案例去學習,去進步,祝成功!

源碼下載
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
88国产精品欧美一区二区三区| 亚洲成人精品在线| 久久久国产精品免费| 亚洲视频欧洲视频| 久久精品色欧美aⅴ一区二区| 午夜精品一区二区三区av| 97av视频在线| 国产精品爱久久久久久久| 国产精品永久免费观看| 日韩av123| 97视频在线免费观看| 蜜臀久久99精品久久久无需会员| 亚洲最大的网站| 日韩免费在线免费观看| 亚洲黄色av女优在线观看| 久久免费高清视频| 久久久av亚洲男天堂| 4438全国亚洲精品在线观看视频| 欧美日本高清视频| 亚洲性av在线| 中文字幕亚洲一区| 菠萝蜜影院一区二区免费| 欧美性猛交xxxx乱大交极品| 亚洲二区在线播放视频| 亚洲电影成人av99爱色| 久久精品国产久精国产思思| 国产成人精品在线播放| 韩日欧美一区二区| 日韩欧美一区二区三区| 国产小视频91| x99av成人免费| 日韩欧美中文字幕在线观看| 欧美激情精品久久久久久| 久久精品人人爽| 国内精品伊人久久| 欧美色欧美亚洲高清在线视频| 欧美成人精品在线播放| 九九热精品视频在线播放| 正在播放欧美一区| 亚洲老板91色精品久久| 亚洲欧美在线一区| 91精品久久久久久综合乱菊| 综合网日日天干夜夜久久| 亚洲在线视频福利| 国产精品久久久久久婷婷天堂| 欧美孕妇孕交黑巨大网站| 欧美大全免费观看电视剧大泉洋| 高清欧美一区二区三区| 日韩精品久久久久久久玫瑰园| 亚洲欧美日韩网| 精品中文字幕在线| 日韩在线观看你懂的| 国产婷婷97碰碰久久人人蜜臀| 久久天堂av综合合色| 亚洲精品电影网站| 狠狠色狠狠色综合日日五| 国产精品久久久久久久电影| 久久久久久国产精品三级玉女聊斋| 欧美日韩精品在线| 在线观看精品自拍私拍| 国产精品成人品| 国产精品jvid在线观看蜜臀| 久久久久久久影视| 一区二区三区 在线观看视| 亚洲网址你懂得| 欧美在线视频导航| 亚洲少妇激情视频| 欧美另类高清videos| www亚洲欧美| 日韩视频―中文字幕| 国产成人jvid在线播放| 国产日产久久高清欧美一区| 精品福利樱桃av导航| 久久精品国产成人精品| 欧美亚洲激情在线| 欧美精品在线播放| 亚洲精品白浆高清久久久久久| 亚洲一级黄色av| 国产成人极品视频| 国产中文日韩欧美| 欧美极品欧美精品欧美视频| 国产精品91久久久久久| 成人日韩av在线| 亚洲成人激情在线| 日韩电影视频免费| 成人av资源在线播放| 国模叶桐国产精品一区| 日韩男女性生活视频| 一色桃子一区二区| 久久久久国产精品免费网站| 成人一区二区电影| 日韩亚洲欧美中文高清在线| 欧美孕妇孕交黑巨大网站| 国产亚洲欧美aaaa| 亚洲片av在线| 欧美日韩中文字幕在线视频| 色一情一乱一区二区| 欧美老少配视频| 91精品国产一区| 国产精品欧美日韩一区二区| 亚洲老司机av| 国产免费一区二区三区香蕉精| 欧美日韩国产精品一区二区三区四区| 国产亚洲视频在线观看| 97国产精品久久| 成人在线视频网站| 欧美亚洲国产精品| 综合136福利视频在线| 国产一区二区三区视频在线观看| 精品视频在线播放免| 日韩中文字幕亚洲| 亚洲一区制服诱惑| 日韩av成人在线观看| 亚洲香蕉成视频在线观看| 亚洲综合社区网| 色一情一乱一区二区| 日本午夜在线亚洲.国产| 成人a级免费视频| 欧美性视频精品| 97久久精品国产| 亚洲www在线| 亚洲美女视频网| 国产精品美女呻吟| 亚洲成人免费在线视频| 岛国视频午夜一区免费在线观看| 日韩成人性视频| 欧美风情在线观看| 国产精品精品国产| 久久久亚洲网站| 久久伊人精品一区二区三区| 欧美色另类天堂2015| 日本精品久久电影| 欧美极品欧美精品欧美视频| 国色天香2019中文字幕在线观看| 91久久精品国产| 久久久久成人网| 亚洲人成在线观| 热99在线视频| 国产精品一区二区av影院萌芽| 亚洲欧洲日韩国产| 欧美亚洲第一区| 欧美一级免费视频| 日韩精品久久久久久久玫瑰园| 在线免费观看羞羞视频一区二区| 国产精品日韩专区| 精品国产乱码久久久久久天美| 亚洲色图偷窥自拍| 亚洲高清免费观看高清完整版| 日产精品99久久久久久| 日韩欧美亚洲范冰冰与中字| 国产精品三级网站| 97成人精品视频在线观看| 成人高清视频观看www| 精品欧美激情精品一区| 亚洲成人网av| 亚洲电影免费观看高清完整版| 国产成人啪精品视频免费网| 成人a免费视频| 国语自产偷拍精品视频偷| 亚洲美女在线看| 免费99精品国产自在在线| 国产亚洲人成网站在线观看| 日韩精品视频在线免费观看| 日韩免费黄色av|