編輯:關於Android編程
一般情況下,如果想要在ListView上面實現Listitem的滑動刪除效果,或者仿QQ的滑動顯示刪除效果的時候,只需要繼承ListView,自定義一個ListView就可以,不過由於之前用了開源庫StickyListHeaders來實現ListView的分組,那麼這一次在實現這個仿QQ的左右滑動顯示隱藏刪除按鈕的時候,就要在StickyListHeaders的基礎上實現。而StickyListHeaders要開放了一個setOnTouchListener的接口,允許調用者傳進去一個OnTouchListener,來實現自定義的OnTouch效果,如下:
@Override public void setOnTouchListener(final OnTouchListener l) { if (l != null) { mList.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return l.onTouch(StickyListHeadersListView.this, event); } }); } else { mList.setOnTouchListener(null); } }
那麼解決問題的方法就在於實現一個OnTouchListener,然後將其傳給StickyListHeaderListView。
lvTasks.setOnTouchListener(new ListitemSlopListener(this));
public class ListitemSlopListener implements OnTouchListener { ... private void initConfiguration() { ViewConfiguration vc = ViewConfiguration.get(mContext); mSlop = vc.getScaledTouchSlop(); mMinFling = vc.getScaledMaximumFlingVelocity(); mMaxFling = mMinFling * 8; } ... @Override public boolean onTouch(View v, MotionEvent event) { mListView = ((StickyListHeadersListView) v).getWrappedList(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = event.getX(); mDownY = event.getY(); mPosition = mListView.pointToPosition((int)mDownX, (int)mDownY); mSlopView = mListView.getChildAt(mPosition - mListView.getFirstVisiblePosition()); Log.v(Helper.TAG,"Action Down : onTouch Triggered"); if(mSlopView != null){ btnDelete = (Button) mSlopView.findViewById(R.id.btnDelete); if(btnDelete.getVisibility() == View.INVISIBLE || btnDelete.getVisibility() == View.GONE){ btnDelete.setVisibility(View.VISIBLE); ViewHelper.setAlpha(btnDelete, 0); } mBtnWidth = btnDelete.getWidth(); initVelocityTrackerIfNotExists(event); return true; } break; case MotionEvent.ACTION_MOVE: if(mVelocityTracker != null && mSlopView != null && btnDelete != null){ float dx = event.getX() - mDownX; float dy = event.getY() - mDownY; if(Math.abs(dx) > mSlop && Math.abs(dy) < mSlop){ mMoving = true; } if(mMoving){ if (dx > 0) { //Direction : right to hide the button if the button shows if(ViewHelper.getAlpha(btnDelete) > 0){ ViewHelper.setAlpha( btnDelete,Math.min(1f,Math.max(0f, 1f - Math.abs(dx) /mBtnWidth))); } } else { //Direction : Left to show the button if the button exists ViewHelper.setAlpha( btnDelete, Math.max(0f,Math.min(1f, Math.abs(dx) / mBtnWidth))); } return true; }else{ return false; } } case MotionEvent.ACTION_UP: if (mVelocityTracker != null && mSlopView != null && mMoving) { float dx = event.getX() - mDownX; mVelocityTracker.computeCurrentVelocity(1000); float vx = Math.abs(mVelocityTracker.getXVelocity()); float vy = Math.abs(mVelocityTracker.getYVelocity()); boolean isShow = false; if(Math.abs(dx) > mBtnWidth / 2){ isShow = dx < 0; }else if(vx > mMinFling && vx < mMaxFling && vx > vy){ isShow = vx < 0; } if(isShow){ ViewPropertyAnimator.animate(btnDelete).alpha(1f).setDuration(DURATION); }else{ ViewPropertyAnimator.animate(btnDelete).alpha(0f).setDuration(DURATION); } recycleVelocityTracker(); mMoving = false; }else{ //Only When the Delete Button is not visible, the the onItemClick action was fired. if(btnDelete != null && ViewHelper.getAlpha(btnDelete) > 0){ ViewPropertyAnimator.animate(btnDelete).alpha(0f).setDuration(DURATION); }else{ TodoTask todoTask = (TodoTask) mListView.getItemAtPosition(mPosition); if(todoTask != null){ mListView.performItemClick(mSlopView, mPosition, todoTask.getId()); } } } return true; } return false; } }
我們知道,在屏幕上的任何事件,包括點擊,長按,滑動,還是其它手勢,其實都是由一系列的Touch事件組成的,而這些Touch事件其實是下面三步組成的:
0)一個ACTION_DOWN事件
1)N個ACTION_MOVE事件
2)一個ACTION_UP事件組成的。
而根據Android的事件分發機制,在觸發View本身的OnTouchEvent方法的時候,如果存在一個OnTouchListener,那麼View會首先調用OnTouchListener來響應事件,只有當OnTouchListener返回false,表明其不處理該事件的時候,才會繼續將事件傳遞給View的OnTouchEvent,那麼在這裡,我們就要在OnTouchListener的onTouch方法中,將這個事件給完全消費掉,來實現我們自己想要的效果。
OnTouch方法中,主要實現了下面的功能:1)當我們手指接觸到屏幕上的時候,一個Down事件就會被觸發,於是會來到Listener的Down分支下面。
在這裡,我們會首先利用ListView的pointToPosition方法,根據觸摸的點坐標來獲取ListView中item的位置,並利用getChildAt方法找出手指觸摸到的那個Listitem,並將其放置到變量mSlopView中。
mSlopView其實就是TaskItem,其結構如下:
而最後,要在這裡返回一個true,表明在這裡,Down事件已經被這個OnTouchListener給消費了,這樣,後續的Move事件, Up事件才會繼續被這個OnTouchListener來處理。
2)當Donw事件被這個OnTouchListener消費後,後續的Move事件也會來到這裡進行處理。
在Move分支中, 我們要根據移動的距離來判斷這是不是一次移動,這是通過跟mSlop變量比較實現的。mSlop值是Android定義一個滑動事件的最短距離,當移動超過這個距離,表明這是一個滑動事件,那麼這個時候,我們就要根據移動的距離去動態地改變刪除按鈕的alpha值,來顯示或者隱藏刪除按鈕。在這裡,定義
2.1)當手指向左移動的時候,則表明這是要顯示按鈕,那麼會將按鈕的alpha值由 0 向 1 變化,最終完全顯示按鈕。
2.2)當手指向右移動的時候,則表明是要刪除按鈕,那麼會將按鈕的alpha值由 1 向 0 變化,最終完全隱藏按鈕。
最後,這個方法也要返回 true ,表明Move事件也在這裡被消費了。
3)最後就是來到Up事件了。每一個Listitem的OnTouch事件其實分兩種情況:
3.1)當OnTouch事件被定義為滑動事件時,即滑動距離超過mSlop的時候,我們要根據最終滑動的距離來決定是否要顯示按鈕。只有當滑動距離超過按鈕的一半寬度的時候,才顯示按鈕,如果按鈕還沒有完全顯示,則添加一個動畫效果,來顯示按鈕。而如果沒有超過按鈕的一半寬度的時候,也要為該按鈕添加一個動畫效果,但是這個效果則是慢慢慢將按鈕的alpha值變為0,最終隱藏按鈕。
3.1)當滑動的距離小於mSlop的時候,該事件只被定義為一個OnItemClick事件,那麼在這裡要顯示地調用performClick方法來觸發ListView的OnItemClick事件,這是因為不想去改變開源庫的源碼,所以我們必須將所有的OnTouch事件(當然,只是我們想處理的)都在OnTouchListener中處理。
最後,在Up分支中,我們處理了事件之後,同樣返回一個true,表明所有的OnTouch事件都在這裡被處理了,不需要再將事件傳遞下去。
下面是實現後的效果圖:
結束。
前些天印尼客戶要求在高通平台7251上加一個搖搖切歌功能。查了些資料,基本實現了此功能。 直接上源碼,用svn查看修改點。 前面兩個ic_mp_sha
Android技術精髓-Timing Activity Timing Activity 顧名思義,在activity中實時通過線程通信顯示當前時間,也是主要用到線
ListView實現二級節點想必大家都知道可以用ExpandableListView 就可以輕松實現,但是要實現3級甚至多級菜單怎麼實現呢? 再利用ExpandableL
本文根據自己的實踐總結而來,參考前人博客之余,也自己總結和開發了一些功能,在這裡給自己備份也分享給大家。不同之處在於:自動打開並搜索藍牙、修改藍牙名字、完整接收藍牙傳輸