麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 系統 > 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和案例去學習,去進步,祝成功!

源碼下載
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 国产成人自拍视频在线 | 欧美a视频在线观看 | 毛片视频大全 | 日韩美香港a一级毛片 | 曰批全过程40分钟免费视频多人 | 免费国产人成网站 | 国产成人自拍av | 国产精品视频中文字幕 | 国产www免费| 久久看视频 | 欧美a∨一区二区三区久久黄 | 精品中文字幕久久久久四十五十骆 | 中文字幕在线永久视频 | 国产精品久久久久久久久粉嫩 | 在线视频观看成人 | 日韩在线激情 | 久久视频精品 | 久久久久久艹 | 青热久思思 | 91精品视频网址 | 久久美女色视频 | 狠狠一区二区 | 欧美成人免费 | 成人国产精品色哟哟 | 在线观看免费精品 | 舌头伸进添的我好爽高潮网站 | 黄色影院网站 | 三级xxxx| 91精品久久久久久久久久久 | 国产一级一区 | 在线中文字幕亚洲 | 激情小说激情电影 | 久久综合久久精品 | 久久国产精品影视 | 欧美a视频| 精品久久久久久国产三级 | 国产美女的小嫩bbb图片 | 成人免费网站在线观看视频 | asian gaysex| 在线成人免费视频 | 成人免费观看av |