編輯:關於Android編程
我們做一些好友列表或者商品列表的時候,居多的需求可能就是需要列表拖拽了,而我們選擇了ListView,也是因為使用ListView太久遠了,導致對他已經有濃厚的感情了
而今天我們就來實現以下課拖拽的方案,大家可以借鑒以下
我們大致的思路,其實是這樣子的,也是我的設想,我們可以先去實現一個簡單的ListView的數據,但是他的Adapter,我們可以用系統封裝好的,然後傳遞進去一個實體類,最後自定義一個listview去操作,所以我們先把准備的工作做好,比如?
這就只有一個頭像和一句話了,然後我們把實體類也給寫完了
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這樣能讓我們插入和刪除很輕松
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了,我們給他填充點數據
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);
}
}
現在可以看看實際的效果了

現在我們可以重寫我們的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);
}
}
全部代碼貼上
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);
}
}
}
然後我們引用
對了,別忘記了添加窗體的權限哦
做完這一系列事情之後,就覺得這個設想其實是對的,然後我們可以嘗試性的運行一下

運行時成功的,說明我們的設想是沒有問題的,但是隨著而來的,也是諸多的bug,所以呢,這也只是思想的參考,不過這些bug我有時間野就修復一下,應該是可以的,好的,本篇博文就先到這裡!
怎麼使用微信 微信使用教程
怎麼使用微信?這是一個很廣泛的問題,涉及到微信的各個功能,借助微信我們能夠與好友分享音樂圖片和有趣的鏈接,能夠創建群聊,能夠發發紅包,創建微信公眾號後還能推
Android checkbox的listView具體操作方法
本文主要實現在自定義的ListView布局中加入CheckBox控件,通過判斷用戶是否選中CheckBox來對ListView的選中項進行相應的操作。通過一個Demo來展
淺談談Android 圖片選擇器
ImageSelector 簡介Android自定義相冊,實現了拍照、圖片選擇(單選/多選)、ImageLoader無綁定 任由開發者選擇https://github.c
Android之旅第二站——界面(布局)…
Android中有五大布局: LinerLayout(線性布局)、RelativeLayout(相對布局)、Tablelayout(表格布局)、用來控制控件的擺放。Fra