手把手实现腾讯qq拖拽删去效果(二)
這節,就一個任務如何把上節自定義的翻頁動畫控件整進下拉列表中去。
由于是自定義的下拉列表控件,我們需要自定義能夠上啦下滑的listview,這勢必會造成這個問題,上拉刷新要響應相應touch事件,拖拽也似乎也要相應觸摸事件,這勢必會造成了一種事件的沖突了,怎么解決了。我這里用一個變量來區分一下,偽代碼如下:
前面說了,這么多了,我們直接點評代碼把:
package com.routemap_infomation.utils;import java.util.Date;import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug.IntToString; import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView;import com.routemap_infomation.myfocus.R; import com.routemap_infomation.myfocus.LineFocusActivity.RouteItem; import com.routemap_infomation.utils.GestureJudgeUtils.OnGestureResult;/*** 該類主要是完成 頭部部分的功能封裝* * 一個可以監聽ListView是否滾動到最頂部或最底部的自定義控件* 只能監聽由觸摸產生的,如果是ListView本身Flying導致的,則不能監聽</br> 如果加以改進,可以實現監聽scroll滾動的具體位置等* * @author 進*/public class ScrollOverListView extends ListView implements OnScrollListener {public static boolean canRefleash = true;private static final String TAG = "listview";/**松開更新**/private static final int RELEASE_TO_REFRESH = 0;/**下拉更新**/private static final int PULL_TO_REFRESH = 1;/**更新中**/private static final int REFRESHING = 2;/**無**/private static final int DONE = 3;/**加載中**/private static final int LOADING = 4;/**實際的padding的距離與界面上偏移距離的比例**/private static final int RATIO = 3;/**是否要使用下拉刷新功能**/public boolean showRefresh = true;public boolean isDelete;public void setIsDelete(boolean isDelete){this.isDelete = isDelete;}private int mLastY;private int mBottomPosition;private SlideView mFocusedItemView;private LayoutInflater inflater;/**頭部刷新的布局**/private LinearLayout headView;/**頭部顯示下拉刷新等的控件**/private TextView tipsTextview;/**刷新控件**/private TextView lastUpdatedTextView;/**箭頭圖標**/private ImageView arrowImageView;/**頭部滾動條**/private ProgressBar progressBar;/**顯示動畫**/private RotateAnimation animation;/**頭部回退顯示動畫**/private RotateAnimation reverseAnimation;/** 用于保證startY的值在一個完整的touch事件中只被記錄一次**/private boolean isRecored;/**頭部高度**/private int headContentHeight;/**開始的Y坐標**/private int startY;/**第一個item**/private int firstItemIndex;/**狀態**/private int state;private boolean isBack;private GestureDetector gestureDetector;/** 空的 */private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener() {@Overridepublic boolean onListViewTopAndPullDown(int delta) {return false;}@Overridepublic boolean onListViewBottomAndPullUp(int delta) {return false;}@Overridepublic boolean onMotionDown(MotionEvent ev) {return false;}@Overridepublic boolean onMotionMove(MotionEvent ev, int delta) {return false;}@Overridepublic boolean onMotionUp(MotionEvent ev) {return false;}};public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}public ScrollOverListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public ScrollOverListView(Context context, PullDownView pullDownView) {super(context);isDelete = pullDownView.getIsDelete();init(context);}/**出事化控件**/private void init(Context context) {mBottomPosition = 0;setCacheColorHint(0);inflater = LayoutInflater.from(context);headView = (LinearLayout) inflater.inflate(R.layout.pull_down_head,null);arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);arrowImageView.setMinimumWidth(70);arrowImageView.setMinimumHeight(50);progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView);measureView(headView);headContentHeight = headView.getMeasuredHeight();headView.setPadding(0, -1 * headContentHeight, 0, 0);headView.invalidate();/**列表添加頭部**/addHeaderView(headView, null, false);setOnScrollListener(this);animation = new RotateAnimation(0, -180,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);animation.setInterpolator(new LinearInterpolator());animation.setDuration(250);animation.setFillAfter(true);reverseAnimation = new RotateAnimation(-180, 0,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);reverseAnimation.setInterpolator(new LinearInterpolator());reverseAnimation.setDuration(200);reverseAnimation.setFillAfter(true);state = DONE;gestureDetector=new GestureJudgeUtils(PublicDefine.context,new GestureJudgeUtils.OnGestureResult() {@Overridepublic void onGestureResult(int direction) {// TODO Auto-generated method stubswitch (direction) {case GestureJudgeUtils.GESTURE_LEFT:case GestureJudgeUtils.GESTURE_RIGHT:PublicDefine.isDeleteTag=true;break;case GestureJudgeUtils.GESTURE_DOWN:case GestureJudgeUtils.GESTURE_UP:PublicDefine.isDeleteTag=false;default:break;}}}).Buile();}int currentx=0;int currenty=0;/**觸摸事件的處理**/@Overridepublic boolean onTouchEvent(MotionEvent ev) {final int action = ev.getAction();final int y = (int) ev.getRawY();final int deltax=(int)ev.getRawX();if (action==MotionEvent.ACTION_DOWN&&PublicDefine.isDeleteTag) {mFocusedItemView.move();PublicDefine.isDeleteTag=false;return super.onTouchEvent(ev);}if (action==MotionEvent.ACTION_DOWN) {currentx=(int) ev.getRawX();currenty=(int) ev.getRawY();}PublicDefine.isDeleteTag=Horizontal(currentx,currenty,deltax,y);cancelLongPress();if(PublicDefine.isDeleteTag){switch (action) {case MotionEvent.ACTION_DOWN: {int x = (int) ev.getX();int z = (int) ev.getY();int position = pointToPosition(x, z);Log.e(TAG, "postion=" + position);if (position != INVALID_POSITION) {RouteItem data = (RouteItem) getItemAtPosition(position);mFocusedItemView = data.slideView;Log.e(TAG, "FocusedItemView=" + mFocusedItemView);}}default:break;}if (mFocusedItemView != null) {return mFocusedItemView.onRequireTouchEvent(ev);}return super.onTouchEvent(ev);}else{switch (action) {case MotionEvent.ACTION_DOWN: //按下的時候if (firstItemIndex == 0 && !isRecored) {isRecored = true;startY = (int) ev.getY();Log.v(TAG, "在down時候記錄當前位置‘");}// ===========================mLastY = y;final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);if (isHandled) {mLastY = y;return isHandled;}break;case MotionEvent.ACTION_MOVE: //手指正在移動的時候int tempY = (int) ev.getY();if (showRefresh) {if (!isRecored && firstItemIndex == 0) {Log.v(TAG, "在move時候記錄下位置");isRecored = true;startY = tempY;}if (state != REFRESHING && isRecored && state != LOADING) {// 保證在設置padding的過程中,當前的位置一直是在head,否則如果當列表超出屏幕的話,當在上推的時候,列表會同時進行滾動// 可以松手去刷新了if (state == RELEASE_TO_REFRESH) {setSelection(0);// 往上推了,推到了屏幕足夠掩蓋head的程度,但是還沒有推到全部掩蓋的地步if (((tempY - startY) / RATIO < headContentHeight)&& (tempY - startY) > 0) {state = PULL_TO_REFRESH;changeHeaderViewByState();}else if (tempY - startY <= 0) {// 一下子推到頂了state = DONE;changeHeaderViewByState();Log.v(TAG, "由松開刷新狀態轉變到done狀態");}// else {// 往下拉了,或者還沒有上推到屏幕頂部掩蓋head的地步// // 不用進行特別的操作,只用更新paddingTop的值就行了// } }// 還沒有到達顯示松開刷新的時候,DONE或者是PULL_To_REFRESH狀態if (state == PULL_TO_REFRESH) {setSelection(0);// 下拉到可以進入RELEASE_TO_REFRESH的狀態if ((tempY - startY) / RATIO >= headContentHeight) {state = RELEASE_TO_REFRESH;isBack = true;changeHeaderViewByState();Log.v(TAG, "由done或者下拉刷新狀態轉變到松開刷新");}else if (tempY - startY <= 0) {// 上推到頂了state = DONE;changeHeaderViewByState();Log.v(TAG, "由DOne或者下拉刷新狀態轉變到done狀態");}}// done狀態下if (state == DONE) {if (tempY - startY > 0) {state = PULL_TO_REFRESH;changeHeaderViewByState();}}// 更新headView的sizeif (state == PULL_TO_REFRESH) {headView.setPadding(0, -1 * headContentHeight+ (tempY - startY) / RATIO, 0, 0);}// 更新headView的paddingTopif (state == RELEASE_TO_REFRESH) {headView.setPadding(0, (tempY - startY) / RATIO- headContentHeight, 0, 0);}}}// ==============================================final int childCount = getChildCount();if (childCount == 0){return super.onTouchEvent(ev);}final int itemCount = getAdapter().getCount() - mBottomPosition;final int deltaY = y - mLastY;final int lastBottom = getChildAt(childCount - 1).getBottom();final int end = getHeight() - getPaddingBottom();final int firstVisiblePosition = getFirstVisiblePosition();final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY);if (isHandleMotionMove) {mLastY = y;return true;}/** 到達底部 * 到達底部的事件在另外一個類執行**/if (firstVisiblePosition + childCount >= itemCount&& lastBottom <= end && deltaY < 0) {final boolean isHandleOnListViewBottomAndPullDown;isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY);if (isHandleOnListViewBottomAndPullDown) {mLastY = y;return true;}}break;case MotionEvent.ACTION_UP: //手指抬起來的時候if (state != REFRESHING && state != LOADING) {// if (state == DONE) {// // 什么都不做// }if (state == PULL_TO_REFRESH) {state = DONE;changeHeaderViewByState();Log.v(TAG, "由下拉刷新狀態,到done狀態");}if (state == RELEASE_TO_REFRESH) {state = REFRESHING;changeHeaderViewByState();canRefleash = true;Log.v(TAG, "由松開刷新狀態,到done狀態");}}isRecored = false;isBack = false;// /======================final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev);if (isHandlerMotionUp) {mLastY = y;return true;}break;default:break;}mLastY = y;return super.onTouchEvent(ev);} // }catch(Exception e){ // Log.i("test", e.getMessage()); // return true; // // } }private boolean Horizontal(int dx, int dy,int dx1,int dy1) {// TODO Auto-generated method stubint deltax=dx-dx1;int deltay=dy-dy1;if (Math.abs(deltax)>=Math.abs(deltay)) {return true;}else {return false; }}// =============================== public method/*** 可以自定義其中一個條目為頭部,頭部觸發的事件將以這個為準,默認為第一個* * @param index 正數第幾個,必須在條目數范圍之內*/public void setTopPosition(int index) {if (getAdapter() == null){throw new NullPointerException("You must set adapter before setTopPosition!");}if (index < 0){throw new IllegalArgumentException("Top position must > 0");}}/*** 可以自定義其中一個條目為尾部,尾部觸發的事件將以這個為準,默認為最后一個* * @param index 倒數第幾個,必須在條目數范圍之內*/public void setBottomPosition(int index) {if (getAdapter() == null){throw new NullPointerException("You must set adapter before setBottonPosition!");}if (index < 0){throw new IllegalArgumentException("Bottom position must > 0");}mBottomPosition = index;}/*** 設置這個Listener可以監聽是否到達頂端,或者是否到達低端等事件</br>* * @see OnScrollOverListener*/public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener) {mOnScrollOverListener = onScrollOverListener;}/*** 滾動監聽接口* * @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)* */public interface OnScrollOverListener {/*** 到達最頂部觸發* * @param delta* 手指點擊移動產生的偏移量* @return*/boolean onListViewTopAndPullDown(int delta);/*** 到達最底部觸發* * @param delta* 手指點擊移動產生的偏移量* @return*/boolean onListViewBottomAndPullUp(int delta);/*** 手指觸摸按下觸發,相當于{@link MotionEvent#ACTION_DOWN}* * @return 返回true表示自己處理* @see View#onTouchEvent(MotionEvent)*/boolean onMotionDown(MotionEvent ev);/*** 手指觸摸移動觸發,相當于{@link MotionEvent#ACTION_MOVE}* * @return 返回true表示自己處理* @see View#onTouchEvent(MotionEvent)*/boolean onMotionMove(MotionEvent ev, int delta);/*** 手指觸摸后提起觸發,相當于{@link MotionEvent#ACTION_UP}* * @return 返回true表示自己處理* @see View#onTouchEvent(MotionEvent)*/boolean onMotionUp(MotionEvent ev);}@Overridepublic void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,int arg3) {firstItemIndex = firstVisiableItem;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}// 此方法直接照搬自網絡上的一個下拉刷新的demo,此處是“估計”headView的width以及heightprivate void measureView(View child) {ViewGroup.LayoutParams p = child.getLayoutParams();if (p == null) {p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);}int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);int lpHeight = p.height;int childHeightSpec;if (lpHeight > 0) {childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,MeasureSpec.EXACTLY);} else {childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);}child.measure(childWidthSpec, childHeightSpec);}public void onRefreshComplete() {state = DONE;lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());changeHeaderViewByState();}// 當狀態改變時候,調用該方法,以更新界面private void changeHeaderViewByState() {switch (state) {case RELEASE_TO_REFRESH:arrowImageView.setVisibility(View.VISIBLE);progressBar.setVisibility(View.GONE);tipsTextview.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.startAnimation(animation);tipsTextview.setText("松開刷新");Log.v(TAG, "當前狀態,松開刷新");break;case PULL_TO_REFRESH:progressBar.setVisibility(View.GONE);tipsTextview.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.setVisibility(View.VISIBLE);// 是由RELEASE_To_REFRESH狀態轉變來的if (isBack) {isBack = false;arrowImageView.clearAnimation();arrowImageView.startAnimation(reverseAnimation);tipsTextview.setText("下拉刷新");} else {tipsTextview.setText("下拉刷新");}Log.v(TAG, "當前狀態,下拉刷新");break;case REFRESHING:headView.setPadding(0, 0, 0, 0);progressBar.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.setVisibility(View.GONE);tipsTextview.setText("正在刷新...");lastUpdatedTextView.setVisibility(View.VISIBLE);Log.v(TAG, "當前狀態,正在刷新...");break;case DONE:headView.setPadding(0, -1 * headContentHeight, 0, 0);progressBar.setVisibility(View.GONE);arrowImageView.clearAnimation();arrowImageView.setImageResource(R.drawable.pull_down_arrow);tipsTextview.setText("下拉刷新");lastUpdatedTextView.setVisibility(View.VISIBLE);Log.v(TAG, "當前狀態,done");break;default:break;}}public void shrinkListItem(int position) {View item = getChildAt(position);if (item != null) {try {((SlideView) item).shrink();} catch (ClassCastException e) {e.printStackTrace();}}} }①由于這個類繼承與listview,因此具有了listview常見屬性與方法。
②由于他要記錄上拉下拉刷新的方式,因此,我們這里需要用定義一些變量來保存,并且要記錄他們的狀態。
③上拉刷新的狀態是需要監聽的了,怎么進行監聽了,這里依照java中插件機制,依照相應的觀察者(observer)模式,需要定義一個接口來監聽了。
④為了實現動態,這里需要定義不同參數的構造函數了。
⑤這里是一個重點,我們定義了一個變量,接上面的論證,我們這里定義了一個變量來確定到底是調用那個觸摸方法。
⑥一般在android中,init初始化,找到相應控件,數據加載,是一個司空見慣的方法,在這里了,我們還對相應的手勢的方法進行了監聽,但是后來發現在此情景下,主要是由于touch事件中,無法對手勢進行監聽。
⑦onTouchEvent事件,觸摸事件,是這一切事件的重中之重,我這里首先要對其手勢方向進行了監聽,上文提到無法直接對手勢的方向進行監聽了,因此我這里粗略進行進行了判斷,判斷觸摸按下與觸摸抬起的時候,兩點x軸之間的距離與兩點y之間的距離孰大孰小。來判斷他是豎向滾動,還是橫向拖拽。倘若是橫向的拖拽,進把觸摸事件下放到slideview控件,由她將觸摸事件進行處理,否則的話,就由此控件監聽上拉下滑的刷新事件。
⑧與上節一樣,對其滑動以后,也要對其控件的距離,控件位置進行調整,進行計算。
⑨這個控件本質是個列表控件,本質是對數據進行處理,因此,我們這里需要一個方法對其數據加以處理的。
以上的內容就把slideview整合到項目中去了,其最終的效果是這樣的:
以上代碼下載地址:
總結
以上是生活随笔為你收集整理的手把手实现腾讯qq拖拽删去效果(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ORACLE的sign函数和DECODE
- 下一篇: ubuntu下git服务器搭建过程