編輯:關於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事件都在這裡被處理了,不需要再將事件傳遞下去。
下面是實現後的效果圖:

結束。
Android平台搖搖切歌功能
前些天印尼客戶要求在高通平台7251上加一個搖搖切歌功能。查了些資料,基本實現了此功能。 直接上源碼,用svn查看修改點。 前面兩個ic_mp_sha
Android技術精髓-Timing Activity
Android技術精髓-Timing Activity Timing Activity 顧名思義,在activity中實時通過線程通信顯示當前時間,也是主要用到線
android ListView 實現3級節點 (可拓展N級)
ListView實現二級節點想必大家都知道可以用ExpandableListView 就可以輕松實現,但是要實現3級甚至多級菜單怎麼實現呢? 再利用ExpandableL
【Android藍牙開發】手機藍牙與下位機HC-05藍牙模塊通信系統
本文根據自己的實踐總結而來,參考前人博客之余,也自己總結和開發了一些功能,在這裡給自己備份也分享給大家。不同之處在於:自動打開並搜索藍牙、修改藍牙名字、完整接收藍牙傳輸