編輯:關於Android編程
趁著周一休息,更新一下博客。最近項目中使用到了分組管理,需要實現Listview的Item拖動處理。查略一下資料和借鑒了別人的代碼將功能實現了。現在整理一下代碼,方便自己以後學習具體思路如下
重寫ListView的onInterceptTouchEvent方法進行控件的touch事件攔截
這個方法的作用很簡單:當我們摁下的如果是可拖拽的圖標,那麼進行初始化該Item的映像試視圖。
同時在拖動中判斷拖動的距離,具體查看下面代碼。不是則不進行處理。代碼如下(代碼注釋非常清楚,如果還有不懂的地方可以在下面留言,我們一起討論,或者下載源碼,自己好好研究一下)
package com.pengguichu.testdraglistview.listview; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PixelFormat; import android.os.Handler; 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.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListView; import com.pengguichu.testdraglistview.R; import com.pengguichu.testdraglistview.adapter.DragListAdapter; import com.pengguichu.testdraglistview.entiy.DragItemInfo; /** * 自定義可拖動Item排序ListView * * @author guichupeng * */ @SuppressLint({ "NewApi", "HandlerLeak" }) public class DragListView extends ListView { private ImageView mDragImageView;// 被拖拽的項(item),其實就是一個ImageView private int mStartPosition;// 手指拖動項原始在列表中的位置 private int mDragPosition;// 手指點擊准備拖動的時候,當前拖動項在列表中的位置 private int mLastPosition;// 手指點擊准備拖動的時候,當前拖動項在列表中的位置 private int mDragPoint;// 在當前數據項中的位置 private int mDragOffset;// 當前視圖和屏幕的距離(這裡只使用了y方向上) private int mUpScrollBounce;// 拖動的時候,開始向上滾動的邊界 private int mDownScrollBounce;// 拖動的時候,開始向下滾動的邊界 private final static int mStep = 1;// ListView 滑動步伐 private int mCurrentStep;// 當前步伐 private DragItemInfo mDragItemInfo;// 用於存放Item信息的對象 private int mItemVerticalSpacing = 0;// Item垂直區域空間 private int mHoldPosition;// 標記最後停靠的Position /** windows窗口控制類 */ private WindowManager mWindowManager; /** 用於控制拖拽項的顯示的參數 */ private WindowManager.LayoutParams mWindowParams; /** 停止狀態 */ public static final int MSG_DRAG_STOP = 0x1001; /** 移動狀態 */ public static final int MSG_DRAG_MOVE = 0x1002; /** 動畫時長(一個動畫的耗時) */ private static final int ANIMATION_DURATION = 200; /** 標識是否上鎖 */ private boolean isLock; /** 標識是否處於移動狀態 */ private boolean isMoving = false; /** 是否拖動Item */ private boolean isDragItemMoving = false; /** 標識是否獲取到間距 */ private boolean bHasGetSapcing = false; public DragListView(Context context, AttributeSet attrs) { super(context, attrs); setLayerType(View.LAYER_TYPE_HARDWARE, null); mDragItemInfo = new DragItemInfo(); init(); } /** * 初始化 */ private void init() { mWindowManager = (WindowManager) getContext() .getSystemService("window"); } /** * 接收消息並完成對應動作 */ Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case MSG_DRAG_STOP:// 停止 stopDrag(); onDrop(msg.arg1); break; case MSG_DRAG_MOVE:// 移動 onDrag(msg.arg1); break; } }; }; /** * 獲取間距--獲取上下滾動間距 */ private void getSpacing() { bHasGetSapcing = true; mUpScrollBounce = getHeight() / 3;// 取得向上滾動的邊際,大概為該控件的1/3 mDownScrollBounce = getHeight() * 2 / 3;// 取得向下滾動的邊際,大概為該控件的2/3 int[] firstTempLocation = new int[2]; int[] secondTempLocation = new int[2]; ViewGroup firstItemView = (ViewGroup) getChildAt(0);// 第一行 ViewGroup secondItemView = (ViewGroup) getChildAt(1);// 第二行 if (firstItemView != null) { firstItemView.getLocationOnScreen(firstTempLocation); } else { return; } if (secondItemView != null) { secondItemView.getLocationOnScreen(secondTempLocation); mItemVerticalSpacing = Math.abs(secondTempLocation[1] - firstTempLocation[1]); } else { return; } } /*** * touch事件攔截 在這裡我進行相應攔截, */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 按下 if (ev.getAction() == MotionEvent.ACTION_DOWN && !isLock && !isMoving && !isDragItemMoving) { int x = (int) ev.getX();// 獲取相對與ListView的x坐標 int y = (int) ev.getY();// 獲取相應與ListView的y坐標 mLastPosition = mStartPosition = mDragPosition = pointToPosition(x, y); // 無效不進行處理 if (mDragPosition == AdapterView.INVALID_POSITION) { return super.onInterceptTouchEvent(ev); } if (false == bHasGetSapcing) { getSpacing(); } // 獲取當前位置的視圖(可見狀態) ViewGroup dragger = (ViewGroup) getChildAt(mDragPosition - getFirstVisiblePosition()); DragListAdapter adapter = (DragListAdapter) getAdapter(); mDragItemInfo.obj = adapter.getItem(mDragPosition - getFirstVisiblePosition()); // 獲取到的dragPoint其實就是在你點擊指定item項中的高度. mDragPoint = y - dragger.getTop(); // 這個值是固定的:其實就是ListView這個控件與屏幕最頂部的距離(一般為標題欄+狀態欄). mDragOffset = (int) (ev.getRawY() - y); // 獲取可拖拽的圖標 View draggerIcon = dragger.findViewById(R.id.drag_item_image); if (draggerIcon.getVisibility() == View.VISIBLE) {// 只有在按鈕為可見的情況下才允許移動 // x > dragger.getLeft() - 20這句話為了更好的觸摸(-20可以省略) if (draggerIcon != null && x > draggerIcon.getLeft() - 20) { dragger.destroyDrawingCache(); dragger.setDrawingCacheEnabled(true);// 開啟cache. dragger.setBackgroundColor(0xffefefef); Bitmap bm = Bitmap.createBitmap(dragger .getDrawingCache(true));// 根據cache創建一個新的bitmap對象. hideDropItem(); adapter.setInvisiblePosition(mStartPosition); adapter.notifyDataSetChanged(); startDrag(bm, y);// 初始化影像 isMoving = false; adapter.copyList(); } } } return super.onInterceptTouchEvent(ev); } /** * 獲取依個縮放動畫 * * @return */ public Animation getScaleAnimation() { Animation scaleAnimation = new ScaleAnimation(0.0f, 0.0f, 0.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAnimation.setFillAfter(true); return scaleAnimation; } /** * 隱藏下降的Item */ private void hideDropItem() { final DragListAdapter adapter = (DragListAdapter) this.getAdapter(); adapter.showDropItem(false); } /** * 觸摸事件處理 */ @Override public boolean onTouchEvent(MotionEvent ev) { // item的view不為空,且獲取的dragPosition有效 if (mDragImageView != null && mDragPosition != INVALID_POSITION && !isLock) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_UP: int upY = (int) ev.getY(); stopDrag(); onDrop(upY); break; case MotionEvent.ACTION_MOVE: int moveY = (int) ev.getY(); onDrag(moveY); itemMoveAnimation(moveY); break; case MotionEvent.ACTION_DOWN: break; } return true;// 取消ListView滑動. } return super.onTouchEvent(ev); } /** * 是否為相同方向拖動的標記 */ private boolean isSameDragDirection = true; /** * 移動方向的標記,-1為默認值,0表示向下移動,1表示向上移動 */ private int lastFlag = -1; private int mFirstVisiblePosition, mLastVisiblePosition;// 第一個、最後一個的位置 private int turnUpPosition, turnDownPosition;// 向上、下的位置 /** * 動態改變Item內容 * * @param last * // 最後一項的位置 * @param current * // 當前位置 */ private void onChangeCopy(int last, int current) { DragListAdapter adapter = (DragListAdapter) getAdapter(); if (last != current) {// 判斷是否移動到最後一項 adapter.exchangeCopy(last, current); } } /** * Item移動動畫 * * @param y */ private void itemMoveAnimation(int y) { final DragListAdapter adapter = (DragListAdapter) getAdapter(); int tempPosition = pointToPosition(0, y); if (tempPosition == INVALID_POSITION || tempPosition == mLastPosition) { return; } mFirstVisiblePosition = getFirstVisiblePosition(); mDragPosition = tempPosition; onChangeCopy(mLastPosition, mDragPosition); int MoveNum = tempPosition - mLastPosition;// 計算移動項--移動距離 int count = Math.abs(MoveNum); for (int i = 1; i <= count; i++) { int xAbsOffset, yAbsOffset; // 向下拖動 if (MoveNum > 0) { if (lastFlag == -1) { lastFlag = 0; isSameDragDirection = true; } if (lastFlag == 1) { turnUpPosition = tempPosition; lastFlag = 0; isSameDragDirection = !isSameDragDirection; } if (isSameDragDirection) { mHoldPosition = mLastPosition + 1; } else { if (mStartPosition < tempPosition) { mHoldPosition = mLastPosition + 1; isSameDragDirection = !isSameDragDirection; } else { mHoldPosition = mLastPosition; } } xAbsOffset = 0; yAbsOffset = -mItemVerticalSpacing; mLastPosition++; } else {// 向上拖動 if (lastFlag == -1) { lastFlag = 1; isSameDragDirection = true; } if (lastFlag == 0) { turnDownPosition = tempPosition; lastFlag = 1; isSameDragDirection = !isSameDragDirection; } if (isSameDragDirection) { mHoldPosition = mLastPosition - 1; } else { if (mStartPosition > tempPosition) { mHoldPosition = mLastPosition - 1; isSameDragDirection = !isSameDragDirection; } else { mHoldPosition = mLastPosition; } } xAbsOffset = 0; yAbsOffset = mItemVerticalSpacing; mLastPosition--; } adapter.setHeight(mItemVerticalSpacing); adapter.setIsSameDragDirection(isSameDragDirection); adapter.setLastFlag(lastFlag); ViewGroup moveView = (ViewGroup) getChildAt(mHoldPosition - getFirstVisiblePosition()); Animation animation; if (isSameDragDirection) {// 相同方向拖動 animation = getFromSelfAnimation(xAbsOffset, yAbsOffset); } else {// 不相同方向拖動 animation = getToSelfAnimation(xAbsOffset, -yAbsOffset); } // 啟用對應的動畫 moveView.startAnimation(animation); } } private void onDrop(int x, int y) { final DragListAdapter adapter = (DragListAdapter) getAdapter(); adapter.setInvisiblePosition(-1); adapter.showDropItem(true); adapter.notifyDataSetChanged(); } /** * 准備拖動,初始化拖動項的圖像 * * @param bm * @param y */ private void startDrag(Bitmap bm, int y) { /*** * 初始化window. */ mWindowParams = new WindowManager.LayoutParams(); mWindowParams.gravity = Gravity.TOP; mWindowParams.x = 0; mWindowParams.y = y - mDragPoint + mDragOffset; mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mWindowParams.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;// 默認為不透明,這裡設成透明效果. mWindowParams.windowAnimations = 0;// 窗口所使用的動畫設置 mWindowParams.alpha = 0.8f; mWindowParams.format = PixelFormat.TRANSLUCENT; ImageView imageView = new ImageView(getContext()); imageView.setImageBitmap(bm); mWindowManager.addView(imageView, mWindowParams); mDragImageView = imageView; } /** * 拖動執行,在Move方法中執行 * * @param y */ public void onDrag(int y) { int drag_top = y - mDragPoint;// 拖拽view的top值不能<0,否則則出界. if (mDragImageView != null && drag_top >= 0) { mWindowParams.alpha = 1.0f; mWindowParams.y = y - mDragPoint + mDragOffset; mWindowManager.updateViewLayout(mDragImageView, mWindowParams);// 時時移動. } doScroller(y);// listview移動. } /*** * ListView的移動. * 要明白移動原理:當我移動到下端的時候,ListView向上滑動,當我移動到上端的時候,ListView要向下滑動。正好和實際的相反. * */ public void doScroller(int y) { // ListView需要下滑 if (y < mUpScrollBounce) { mCurrentStep = mStep + (mUpScrollBounce - y) / 10;// 時時步伐 }// ListView需要上滑 else if (y > mDownScrollBounce) { mCurrentStep = -(mStep + (y - mDownScrollBounce)) / 10;// 時時步伐 } else { mCurrentStep = 0; } // 獲取你拖拽滑動到位置及顯示item相應的view上(注:可顯示部分)(position) View view = getChildAt(mDragPosition - getFirstVisiblePosition()); // 真正滾動的方法setSelectionFromTop() setSelectionFromTop(mDragPosition, view.getTop() + mCurrentStep); } /** * 停止拖動,刪除影像 */ public void stopDrag() { isMoving = false; if (mDragImageView != null) { mWindowManager.removeView(mDragImageView); mDragImageView = null; } isSameDragDirection = true; lastFlag = -1; DragListAdapter adapter = (DragListAdapter) getAdapter(); adapter.setLastFlag(lastFlag); adapter.pastList(); } /** * 拖動放下的時候 * * @param y */ public void onDrop(int y) { onDrop(0, y); } /** * 獲取自身出現的動畫 * * @param x * @param y * @return */ private Animation getFromSelfAnimation(int x, int y) { TranslateAnimation translateAnimation = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, x, Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, y); translateAnimation .setInterpolator(new AccelerateDecelerateInterpolator()); translateAnimation.setFillAfter(true); translateAnimation.setDuration(ANIMATION_DURATION); translateAnimation.setInterpolator(new AccelerateInterpolator()); return translateAnimation; } /** * 獲取自身離開的動畫 * * @param x * @param y * @return */ private Animation getToSelfAnimation(int x, int y) { TranslateAnimation translateAnimation = new TranslateAnimation( Animation.ABSOLUTE, x, Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, y, Animation.RELATIVE_TO_SELF, 0); translateAnimation .setInterpolator(new AccelerateDecelerateInterpolator()); translateAnimation.setFillAfter(true); translateAnimation.setDuration(ANIMATION_DURATION); translateAnimation.setInterpolator(new AccelerateInterpolator()); return translateAnimation; } }
package com.pengguichu.testdraglistview.adapter; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.widget.BaseAdapter; import android.widget.TextView; import com.pengguichu.testdraglistview.R; /** * 自定義可拖拽ListView適配器 * @author guichupeng 下午1:15:31 */ public class DragListAdapter extends BaseAdapter { private List方法注釋很詳細,大家應該能弄明白,展示圖如下mDataList;// 標題數組 private Context mContext; /** * DragListAdapter構造方法 * * @param context * // 上下文對象 * @param dataList * // 數據集合 */ public DragListAdapter(Context context, ArrayList dataList) { this.mContext = context; this.mDataList = dataList; } /** * 設置是否顯示下降的Item * * @param showItem */ public void showDropItem(boolean showItem) { this.mShowItem = showItem; } /** * 設置不可見項的位置標記 * * @param position */ public void setInvisiblePosition(int position) { mInvisilePosition = position; } @Override public View getView(int position, View convertView, ViewGroup parent) { /*** * 在這裡盡可能每次都進行實例化新的,這樣在拖拽ListView的時候不會出現錯亂. * 具體原因不明,不過這樣經過測試,目前沒有發現錯亂。雖說效率不高,但是做拖拽LisView足夠了。 */ convertView = LayoutInflater.from(mContext).inflate( R.layout.drag_list_item, null); initItemView(position, convertView); TextView titleTv = (TextView) convertView .findViewById(R.id.drag_item_title_tv); titleTv.setText(mDataList.get(position)); if (isChanged) {// 判斷是否發生了改變 if (position == mInvisilePosition) { if (!mShowItem) {// 在拖拽過程不允許顯示的狀態下,設置Item內容隱藏 // 因為item背景為白色,故而在這裡要設置為全透明色防止有白色遮擋問題(向上拖拽) convertView.findViewById(R.id.drag_item_layout) .setBackgroundColor(0x0000000000); // 隱藏Item上面的內容 int vis = View.INVISIBLE; convertView.findViewById(R.id.drag_item_image) .setVisibility(vis); convertView.findViewById(R.id.drag_item_close_layout) .setVisibility(vis); titleTv.setVisibility(vis); } } if (mLastFlag != -1) { if (mLastFlag == 1) { if (position > mInvisilePosition) { Animation animation; animation = getFromSelfAnimation(0, -mHeight); convertView.startAnimation(animation); } } else if (mLastFlag == 0) { if (position < mInvisilePosition) { Animation animation; animation = getFromSelfAnimation(0, mHeight); convertView.startAnimation(animation); } } } } return convertView; } /** * * 初始化Item視圖 * * @param convertView */ private void initItemView(final int position, final View convertView) { if (convertView != null) { // 設置對應的監聽 convertView.findViewById(R.id.drag_item_close_layout) .setOnClickListener(new OnClickListener() {// 刪除 @Override public void onClick(View v) { // TODO Auto-generated method stub removeItem(position); } }); } } private int mInvisilePosition = -1;// 用來標記不可見Item的位置 private boolean isChanged = true;// 標識是否發生改變 private boolean mShowItem = false;// 標識是否顯示拖拽Item的內容 /*** * 動態修改ListView的方位. * * @param startPosition * 點擊移動的position * @param endPosition * 松開時候的position */ public void exchange(int startPosition, int endPosition) { Object startObject = getItem(startPosition); if (startPosition < endPosition) { mDataList.add(endPosition + 1, (String) startObject); mDataList.remove(startPosition); } else { mDataList.add(endPosition, (String) startObject); mDataList.remove(startPosition + 1); } isChanged = true; } /** * 動態修改Item內容 * * @param startPosition * // 開始的位置 * @param endPosition * // 當前停留的位置 */ public void exchangeCopy(int startPosition, int endPosition) { Object startObject = getCopyItem(startPosition); if (startPosition < endPosition) {// 向下移動 mCopyList.add(endPosition + 1, (String) startObject); mCopyList.remove(startPosition); } else {// 向上拖動或者不動 mCopyList.add(endPosition, (String) startObject); mCopyList.remove(startPosition + 1); } isChanged = true; } /** * 刪除指定的Item * * @param pos * // 要刪除的下標 */ private void removeItem(int pos) { if (mDataList != null && mDataList.size() > pos) { mDataList.remove(pos); this.notifyDataSetChanged(); } } /** * 獲取鏡像(拖拽)Item項 * * @param position * @return */ public Object getCopyItem(int position) { return mCopyList.get(position); } /** * 獲取Item總數 */ @Override public int getCount() { return mDataList.size(); } /** * 獲取ListView中Item項 */ @Override public Object getItem(int position) { return mDataList.get(position); } @Override public long getItemId(int position) { return position; } /** * 添加拖動項 * * @param start * // 要進行添加的位置 * @param obj */ public void addDragItem(int start, Object obj) { mDataList.remove(start);// 刪除該項 mDataList.add(start, (String) obj);// 添加刪除項 } private ArrayList mCopyList = new ArrayList (); public void copyList() { mCopyList.clear(); for (String str : mDataList) { mCopyList.add(str); } } public void pastList() { mDataList.clear(); for (String str : mCopyList) { mDataList.add(str); } } private boolean isSameDragDirection = true;// 是否為相同方向拖動的標記 private int mLastFlag = -1; private int mHeight; private int mDragPosition = -1; /** * 設置是否為相同方向拖動的標記 * * @param value */ public void setIsSameDragDirection(boolean value) { isSameDragDirection = value; } /** * 設置拖動方向標記 * * @param flag */ public void setLastFlag(int flag) { mLastFlag = flag; } /** * 設置高度 * * @param value */ public void setHeight(int value) { mHeight = value; } /** * 設置當前拖動位置 * * @param position */ public void setCurrentDragPosition(int position) { mDragPosition = position; } /** * 從自身出現的動畫 * * @param x * @param y * @return */ private Animation getFromSelfAnimation(int x, int y) { TranslateAnimation translateAnimation = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, x, Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, y); translateAnimation .setInterpolator(new AccelerateDecelerateInterpolator()); translateAnimation.setFillAfter(true); translateAnimation.setDuration(100); translateAnimation.setInterpolator(new AccelerateInterpolator()); return translateAnimation; } }
最近用到了AsyncTask,這玩意每個寫android程序的都會用,可是不見得每個人都能用的好。如果想要用好,那麼首先勢必對基本原理有個大概了解。其實網上對這類問題的說
本文實例講述了Android編程使用自定義View實現水波進度效果。分享給大家供大家參考,具體如下:首先上效果圖:簡介:1.自動適應屏幕大小;2.水波自動橫向滾動;3.各
ListView是每個app中都要使用的,所以今天我來總結下ListView的使用和一些簡單的優化。先看下運行效果:一、創建數據庫為了模擬數據,這裡將數據保存數據庫中,順
我們在開發App的時候有時候碰到多個界面有一個共同點的時候,比如,都有相同的TitleBar,並且TitleBar可以設置顯示的文字。TitleBar上的點擊事件,如果給