我們大致的思路,其實是這樣子的,也是我的設想,我們可以先去實現一個簡單的ListView的資料,但是他的Adapter,我們可以用系統封裝好的,然後傳遞進去一個實體類,最後自訂一個listview去操作,所以我們先把準備的工作做好,比如?
list_item.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/iv_logo"android:layout_width="50dp"android:layout_height="50dp"android:layout_alignParentLeft="true"android:layout_centerInParent="true"android:layout_marginLeft="10dp"/><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:layout_marginLeft="10dp"android:layout_toRightOf="@id/iv_logo"/></RelativeLayout>
這就只有一個頭像和一句話了,然後我們把實體類也給寫完了
DragBean
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這樣能讓我們插入和刪除很輕鬆
DragAdapter
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<DragBean> {/*** 構造方法** @param context* @param mList*/public DragAdapter(Context context, List<DragBean> mList) {super(context, 0, mList);}/*** 實現View** @param position* @param convertView* @param parent* @return*/@Overridepublic 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了,我們給他填充點資料
MainActivity
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<DragBean> mList = new ArrayList<>();//資料來源private DragAdapter adapter;@Overrideprotected 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*/@Overridepublic 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;}//還會傳遞事件到子Viewreturn 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;//設定xylParams.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*/@Overridepublic 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);}//判斷移動到分割線 返回-1int 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) {//分割線的處理//判斷移動到分割線 返回-1int 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);}}
全部代碼貼上
DragListView
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 {//按下選中的positionprivate int mStartPosition;//需要達到的positionprivate 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*/@Overridepublic 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;}//還會傳遞事件到子Viewreturn 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;//設定xylParams.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*/@Overridepublic 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) {//分割線的處理//判斷移動到分割線 返回-1int 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);}//判斷移動到分割線 返回-1int 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);}}}
然後我們引用
layout_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:orientation="vertical"android:layout_height="match_parent"><com.liuguilin.draglistviewsample.view.DragListViewandroid:id="@+id/mListView"android:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout>
對了,別忘記了添加表單的許可權哦
<!--視窗許可權--><uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
做完這一系列事情之後,就覺得這個設想其實是對的,然後我們可以嘗試性的運行
以上所述是小編給大家介紹的Android自訂ListView實現仿QQ可拖拽列表功能,希望對大家有所協助,如果大家有任何疑問請給我留言,小編會及時回複大家的。在此也非常感謝大家對雲棲社區網站的支援!