編輯:關於Android編程
首先聲明本文是基於GitHub上"baoyongzhang"的SwipeMenuListView修改而來,
該項目地址:https://github.com/baoyongzhang/SwipeMenuListView
可以說這個側滑刪除效果是我見過效果最好且比較靈活的項目,沒有之一!!!
但是在使用它之前需要給大家提兩點注意事項:
1,該項目支持Gradle dependence,但是目前作者提供的依賴地址對應的項目不是最新的項目,依賴過後的代碼與demo中使用的不一致,會提示沒有BaseSwipeListAdapter這個類,因為這個類是其他的開發者後來提交上去的,所以如果想使用最新的代碼,目前還是得把代碼下載下來,然後把library文件拷貝到自己項目中使用.
下圖是目前作者提供的依賴地址,不是最新的,所以想用最新代碼的朋友還是直接下載代碼到本地吧.
2,第二點注意事項應該算是一個bug吧,如果你測試過作者給的demo,你會發現如果某一項item已經被拉出來了,這個時候你再把ListView向上或向下滑動,讓這個被拉出來的item移出屏幕,然後再移回來,這個已經被拉出來的item會直接恢復到未拉出的狀態.這會讓用戶感覺很困惑,我明明已經拉出了這個菜單,怎麼又不見了,然後可能就會產生這個軟件做的真垃圾的想法,進而可能把你的軟件卸載掉.如下圖:
對於上面兩個注意事項,第一個倒是沒什麼好說的,第二個問題怎麼辦呢?別急,這正是我們今天要說的內容.
首先我們可以先研究一下QQ的側滑刪除效果,說到這你可以打開你的qq看看它的具體效果.
你會發現,如果一個item被拉出來了,當你的手指放到其他的item上時,它會直接先把被拉出的那個item關閉掉,並且當前動作的後續的事件也都不再響應,除非你再次把手指放到屏幕上,他才會響應相關事件,而如果你的手指放到當前被拉出的item上,他不會隱藏這個item,並且可以正常響應左右滑動事件.
ok,QQ的效果我們分析完畢,我們探討一下它的實現原理:
1,如果一個item已經被拉了出來,當你的手指放到其他的item上時,它會直接先把被拉出的那個item關閉掉,怎麼實現呢?
首先我們需要判斷我們當前所按下的這個item是不是被拉出來的那個item,不是的話,我們才需要關閉,是的話,則不用管.代碼如下:
if (view instanceof SwipeMenuLayout) { SwipeMenuLayout touchView = (SwipeMenuLayout) view; if (!touchView.isOpen()) { mTouchView.smoothCloseMenu(); } }
2,並且當前動作的後續的事件也都不再響應,怎麼實現呢?
這就很簡單了,根據view的事件分發原理,如果在某一個觸摸事件中返回了false,那麼該事件後續的事件都不會再交給他處理,也就是說,如果我們在ACTION_DOWN的時候返回了false,那麼後續的ACTION_MOVE,ACTION_UP等事件都不會響應,所以要實現這個效果,我們只需要在關閉菜單的後面,返回false就行了,完整的代碼如下:
/********新添加的內容,當按下的item不是當前已經打開的item,則關閉已經打開的item,並返回false.不再響應down以後的事件,仿qq效果********/ if (view instanceof SwipeMenuLayout) { SwipeMenuLayout touchView = (SwipeMenuLayout) view; if (!touchView.isOpen()) { mTouchView.smoothCloseMenu(); return false; } }
上面的那幾行代碼是基於SwipeMenuListView類修改而來,完整的修改之後的SwipeMenuListView代碼如下所示:
package com.lanma.swipemenulistviewdemo.swipemenulistview; import android.content.Context; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; import android.widget.ListAdapter; import android.widget.ListView; /** * @author baoyz * @date 2014-8-18 * qiang_xi修改於2016-09-07(新增qq的效果) */ public class SwipeMenuListView extends ListView { private static final int TOUCH_STATE_NONE = 0; private static final int TOUCH_STATE_X = 1; private static final int TOUCH_STATE_Y = 2; public static final int DIRECTION_LEFT = 1; public static final int DIRECTION_RIGHT = -1; private int mDirection = 1;//swipe from right to left by default private int MAX_Y = 5; private int MAX_X = 3; private float mDownX; private float mDownY; private int mTouchState; private int mTouchPosition; private SwipeMenuLayout mTouchView; private OnSwipeListener mOnSwipeListener; private SwipeMenuCreator mMenuCreator; private OnMenuItemClickListener mOnMenuItemClickListener; private OnMenuStateChangeListener mOnMenuStateChangeListener; private Interpolator mCloseInterpolator; private Interpolator mOpenInterpolator; public SwipeMenuListView(Context context) { super(context); init(); } public SwipeMenuListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public SwipeMenuListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { MAX_X = dp2px(MAX_X); MAX_Y = dp2px(MAX_Y); mTouchState = TOUCH_STATE_NONE; } @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(new SwipeMenuAdapter(getContext(), adapter) { @Override public void createMenu(SwipeMenu menu) { if (mMenuCreator != null) { mMenuCreator.create(menu); } } @Override public void onItemClick(SwipeMenuView view, SwipeMenu menu, int index) { boolean flag = false; if (mOnMenuItemClickListener != null) { flag = mOnMenuItemClickListener.onMenuItemClick( view.getPosition(), menu, index); } if (mTouchView != null && !flag) { mTouchView.smoothCloseMenu(); } } }); } public void setCloseInterpolator(Interpolator interpolator) { mCloseInterpolator = interpolator; } public void setOpenInterpolator(Interpolator interpolator) { mOpenInterpolator = interpolator; } public Interpolator getOpenInterpolator() { return mOpenInterpolator; } public Interpolator getCloseInterpolator() { return mCloseInterpolator; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //在攔截處處理,在滑動設置了點擊事件的地方也能swip,點擊時又不能影響原來的點擊事件 int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mDownX = ev.getX(); mDownY = ev.getY(); boolean handled = super.onInterceptTouchEvent(ev); mTouchState = TOUCH_STATE_NONE; mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()); View view = getChildAt(mTouchPosition - getFirstVisiblePosition()); //只在空的時候賦值 以免每次觸摸都賦值,會有多個open狀態 if (view instanceof SwipeMenuLayout) { //如果有打開了 就攔截. if (mTouchView != null && mTouchView.isOpen() && !inRangeOfView(mTouchView.getMenuView(), ev)) { return true; } mTouchView = (SwipeMenuLayout) view; mTouchView.setSwipeDirection(mDirection); } //如果摸在另外個view if (mTouchView != null && mTouchView.isOpen() && view != mTouchView) { handled = true; } if (mTouchView != null) { mTouchView.onSwipe(ev); } return handled; case MotionEvent.ACTION_MOVE: float dy = Math.abs((ev.getY() - mDownY)); float dx = Math.abs((ev.getX() - mDownX)); if (Math.abs(dy) > MAX_Y || Math.abs(dx) > MAX_X) { //每次攔截的down都把觸摸狀態設置成了TOUCH_STATE_NONE 只有返回true才會走onTouchEvent 所以寫在這裡就夠了 if (mTouchState == TOUCH_STATE_NONE) { if (Math.abs(dy) > MAX_Y) { mTouchState = TOUCH_STATE_Y; } else if (dx > MAX_X) { mTouchState = TOUCH_STATE_X; if (mOnSwipeListener != null) { mOnSwipeListener.onSwipeStart(mTouchPosition); } } } return true; } } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null) return super.onTouchEvent(ev); int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: int oldPos = mTouchPosition; mDownX = ev.getX(); mDownY = ev.getY(); mTouchState = TOUCH_STATE_NONE; mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()); /*******把這句代碼提前*********/ View view = getChildAt(mTouchPosition - getFirstVisiblePosition()); if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) { /********新添加的內容,當按下的item不是當前已經打開的item,則關閉已經打開的item,並返回false.不再響應down以後的事件,仿qq效果********/ if (view instanceof SwipeMenuLayout) { SwipeMenuLayout touchView = (SwipeMenuLayout) view; if (!touchView.isOpen()) { mTouchView.smoothCloseMenu(); return false; } } /***************************/ mTouchState = TOUCH_STATE_X; mTouchView.onSwipe(ev); return true; } if (mTouchView != null && mTouchView.isOpen()) { mTouchView.smoothCloseMenu(); mTouchView = null; // return super.onTouchEvent(ev); // try to cancel the touch event MotionEvent cancelEvent = MotionEvent.obtain(ev); cancelEvent.setAction(MotionEvent.ACTION_CANCEL); onTouchEvent(cancelEvent); if (mOnMenuStateChangeListener != null) { mOnMenuStateChangeListener.onMenuClose(oldPos); } return true; } if (view instanceof SwipeMenuLayout) { mTouchView = (SwipeMenuLayout) view; mTouchView.setSwipeDirection(mDirection); } if (mTouchView != null) { mTouchView.onSwipe(ev); } break; case MotionEvent.ACTION_MOVE: //有些可能有header,要減去header再判斷 mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()) - getHeaderViewsCount(); //如果滑動了一下沒完全展現,就收回去,這時候mTouchView已經賦值,再滑動另外一個不可以swip的view //會導致mTouchView swip 。 所以要用位置判斷是否滑動的是一個view if (!mTouchView.getSwipEnable() || mTouchPosition != mTouchView.getPosition()) { break; } float dy = Math.abs((ev.getY() - mDownY)); float dx = Math.abs((ev.getX() - mDownX)); if (mTouchState == TOUCH_STATE_X) { if (mTouchView != null) { mTouchView.onSwipe(ev); } getSelector().setState(new int[]{0}); ev.setAction(MotionEvent.ACTION_CANCEL); super.onTouchEvent(ev); return true; } else if (mTouchState == TOUCH_STATE_NONE) { if (Math.abs(dy) > MAX_Y) { mTouchState = TOUCH_STATE_Y; } else if (dx > MAX_X) { mTouchState = TOUCH_STATE_X; if (mOnSwipeListener != null) { mOnSwipeListener.onSwipeStart(mTouchPosition); } } } break; case MotionEvent.ACTION_UP: if (mTouchState == TOUCH_STATE_X) { if (mTouchView != null) { boolean isBeforeOpen = mTouchView.isOpen(); mTouchView.onSwipe(ev); boolean isAfterOpen = mTouchView.isOpen(); if (isBeforeOpen != isAfterOpen && mOnMenuStateChangeListener != null) { if (isAfterOpen) { mOnMenuStateChangeListener.onMenuOpen(mTouchPosition); } else { mOnMenuStateChangeListener.onMenuClose(mTouchPosition); } } if (!isAfterOpen) { mTouchPosition = -1; mTouchView = null; } } if (mOnSwipeListener != null) { mOnSwipeListener.onSwipeEnd(mTouchPosition); } ev.setAction(MotionEvent.ACTION_CANCEL); super.onTouchEvent(ev); return true; } break; } return super.onTouchEvent(ev); } public void smoothOpenMenu(int position) { if (position >= getFirstVisiblePosition() && position <= getLastVisiblePosition()) { View view = getChildAt(position - getFirstVisiblePosition()); if (view instanceof SwipeMenuLayout) { mTouchPosition = position; if (mTouchView != null && mTouchView.isOpen()) { mTouchView.smoothCloseMenu(); } mTouchView = (SwipeMenuLayout) view; mTouchView.setSwipeDirection(mDirection); mTouchView.smoothOpenMenu(); } } } public void smoothCloseMenu() { if (mTouchView != null && mTouchView.isOpen()) { mTouchView.smoothCloseMenu(); } } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } public void setMenuCreator(SwipeMenuCreator menuCreator) { this.mMenuCreator = menuCreator; } public void setOnMenuItemClickListener( OnMenuItemClickListener onMenuItemClickListener) { this.mOnMenuItemClickListener = onMenuItemClickListener; } public void setOnSwipeListener(OnSwipeListener onSwipeListener) { this.mOnSwipeListener = onSwipeListener; } public void setOnMenuStateChangeListener(OnMenuStateChangeListener onMenuStateChangeListener) { mOnMenuStateChangeListener = onMenuStateChangeListener; } public static interface OnMenuItemClickListener { boolean onMenuItemClick(int position, SwipeMenu menu, int index); } public static interface OnSwipeListener { void onSwipeStart(int position); void onSwipeEnd(int position); } public static interface OnMenuStateChangeListener { void onMenuOpen(int position); void onMenuClose(int position); } public void setSwipeDirection(int direction) { mDirection = direction; } /** * 判斷點擊事件是否在某個view內 * * @param view * @param ev * @return */ public static boolean inRangeOfView(View view, MotionEvent ev) { int[] location = new int[2]; view.getLocationOnScreen(location); int x = location[0]; int y = location[1]; if (ev.getRawX() < x || ev.getRawX() > (x + view.getWidth()) || ev.getRawY() < y || ev.getRawY() > (y + view.getHeight())) { return false; } return true; } }
話說回來,當按下的item不是已經被拉出來的那個item時,相應的效果我們已經實現,如果按下的item正好是那個已經被拉出來的item,效果怎麼實現呢?
其實對於這個效果原作者其實已經實現了,只不過實現的不夠好,存在一些問題,我這裡把這個問題修復了.
存在的問題就是:如果在當前被拉出的item上左右滑動時,當你在抬起手指的那一刻並且滑動方向是向著拉開的方向滑動,有很大幾率,這個被拉開的item會被關閉,舉個栗子,比如你設定的是向左滑動是拉開的方向,當你在已經被拉開的item上左右滑動,並且在抬起手指的前一刻你是向左滑動的,講道理的話這個item應該是處於被拉開的狀態,但是實際上有很大幾率當你抬起手指時,這個item卻被關閉了,所以這是一個相當影響用戶體驗問題,
一個item的其實就是一個SwipeMenuLayout,在SwipeMenuLayout類中,有一個onSwipe方法,該方法的參數MotionEvent就是從SwipeMenuListView的onTouchEvent方法中傳遞過來的,之前說過,當手指抬起時才會出問題,那麼我們直接就看ACTION_UP當中的實現代碼,
以下代碼是有問題的:
if ((isFling || Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 2)) && Math.signum(mDownX - event.getX()) == mSwipeDirection) { // open smoothOpenMenu(); } else { // close smoothCloseMenu(); return false; }
所以修改之後的代碼邏輯如下所示:
if ((isFling || Math.signum(mDownX - event.getX()) == mSwipeDirection)) { // open /**************新添加內容****防止在已被拉開的item上向拉開的方向滑動然後抬起手指時,item有很大幾率關閉的問題******************/ if (Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 2)) { smoothOpenMenu(); } else { //沒有item打開時,且滑動距離不滿足打開的條件才進行關閉 if (!isOpen()) { smoothCloseMenu(); } } /*******************************************/ } else { // close smoothCloseMenu(); return false; }
完整的修改之後的SwipeMenuLayout類的代碼如下所示:
package com.lanma.swipemenulistviewdemo.swipemenulistview; import android.content.Context; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.widget.ScrollerCompat; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.GestureDetector.OnGestureListener; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.FrameLayout; /** * @author baoyz * @date 2014-8-23 * qiang_xi修改於2016-09-07 */ public class SwipeMenuLayout extends FrameLayout { private static final int CONTENT_VIEW_ID = 1; private static final int MENU_VIEW_ID = 2; private static final int STATE_CLOSE = 0; private static final int STATE_OPEN = 1; private int mSwipeDirection; private View mContentView; private SwipeMenuView mMenuView; private int mDownX; private int state = STATE_CLOSE; private GestureDetectorCompat mGestureDetector; private OnGestureListener mGestureListener; private boolean isFling; private int MIN_FLING = dp2px(15); private int MAX_VELOCITYX = -dp2px(500); private ScrollerCompat mOpenScroller; private ScrollerCompat mCloseScroller; private int mBaseX; private int position; private Interpolator mCloseInterpolator; private Interpolator mOpenInterpolator; private boolean mSwipEnable = true; public SwipeMenuLayout(View contentView, SwipeMenuView menuView) { this(contentView, menuView, null, null); } public SwipeMenuLayout(View contentView, SwipeMenuView menuView, Interpolator closeInterpolator, Interpolator openInterpolator) { super(contentView.getContext()); mCloseInterpolator = closeInterpolator; mOpenInterpolator = openInterpolator; mContentView = contentView; mMenuView = menuView; mMenuView.setLayout(this); init(); } // private SwipeMenuLayout(Context context, AttributeSet attrs, int // defStyle) { // super(context, attrs, defStyle); // } private SwipeMenuLayout(Context context, AttributeSet attrs) { super(context, attrs); } private SwipeMenuLayout(Context context) { super(context); } public int getPosition() { return position; } public void setPosition(int position) { this.position = position; mMenuView.setPosition(position); } public void setSwipeDirection(int swipeDirection) { mSwipeDirection = swipeDirection; } private void init() { setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); mGestureListener = new SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { isFling = false; return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO if (Math.abs(e1.getX() - e2.getX()) > MIN_FLING && velocityX < MAX_VELOCITYX) { isFling = true; } // Log.i("byz", MAX_VELOCITYX + ", velocityX = " + velocityX); return super.onFling(e1, e2, velocityX, velocityY); } }; mGestureDetector = new GestureDetectorCompat(getContext(), mGestureListener); // mScroller = ScrollerCompat.create(getContext(), new // BounceInterpolator()); if (mCloseInterpolator != null) { mCloseScroller = ScrollerCompat.create(getContext(), mCloseInterpolator); } else { mCloseScroller = ScrollerCompat.create(getContext()); } if (mOpenInterpolator != null) { mOpenScroller = ScrollerCompat.create(getContext(), mOpenInterpolator); } else { mOpenScroller = ScrollerCompat.create(getContext()); } LayoutParams contentParams = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); mContentView.setLayoutParams(contentParams); if (mContentView.getId() < 1) { mContentView.setId(CONTENT_VIEW_ID); } mMenuView.setId(MENU_VIEW_ID); mMenuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); addView(mContentView); addView(mMenuView); // if (mContentView.getBackground() == null) { // mContentView.setBackgroundColor(Color.WHITE); // } // in android 2.x, MenuView height is MATCH_PARENT is not work. // getViewTreeObserver().addOnGlobalLayoutListener( // new OnGlobalLayoutListener() { // @Override // public void onGlobalLayout() { // setMenuHeight(mContentView.getHeight()); // // getViewTreeObserver() // // .removeGlobalOnLayoutListener(this); // } // }); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); } public boolean onSwipe(MotionEvent event) { mGestureDetector.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = (int) event.getX(); isFling = false; break; case MotionEvent.ACTION_MOVE: // Log.i("byz", "downX = " + mDownX + ", moveX = " + event.getX()); int dis = (int) (mDownX - event.getX()); if (state == STATE_OPEN) { dis += mMenuView.getWidth() * mSwipeDirection; } swipe(dis); break; case MotionEvent.ACTION_UP: if ((isFling || Math.signum(mDownX - event.getX()) == mSwipeDirection)) { // open /**************新添加內容****防止在已被拉開的item上向拉開的方向滑動然後抬起手指時,item有很大幾率關閉的問題******************/ if (Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 2)) { smoothOpenMenu(); } else { //沒有item打開時,且滑動距離不滿足打開的條件才進行關閉 if (!isOpen()) { smoothCloseMenu(); } } /*******************************************/ } else { // close smoothCloseMenu(); return false; } break; } return true; } public boolean isOpen() { return state == STATE_OPEN; } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } private void swipe(int dis) { if (!mSwipEnable) { return; } if (Math.signum(dis) != mSwipeDirection) { dis = 0; } else if (Math.abs(dis) > mMenuView.getWidth()) { dis = mMenuView.getWidth() * mSwipeDirection; } mContentView.layout(-dis, mContentView.getTop(), mContentView.getWidth() - dis, getMeasuredHeight()); if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { mMenuView.layout(mContentView.getWidth() - dis, mMenuView.getTop(), mContentView.getWidth() + mMenuView.getWidth() - dis, mMenuView.getBottom()); } else { mMenuView.layout(-mMenuView.getWidth() - dis, mMenuView.getTop(), -dis, mMenuView.getBottom()); } } @Override public void computeScroll() { if (state == STATE_OPEN) { if (mOpenScroller.computeScrollOffset()) { swipe(mOpenScroller.getCurrX() * mSwipeDirection); postInvalidate(); } } else { if (mCloseScroller.computeScrollOffset()) { swipe((mBaseX - mCloseScroller.getCurrX()) * mSwipeDirection); postInvalidate(); } } } public void smoothCloseMenu() { state = STATE_CLOSE; if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { mBaseX = -mContentView.getLeft(); mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, 350); } else { mBaseX = mMenuView.getRight(); mCloseScroller.startScroll(0, 0, mMenuView.getWidth(), 0, 350); } postInvalidate(); } public void smoothOpenMenu() { if (!mSwipEnable) { return; } state = STATE_OPEN; if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { mOpenScroller.startScroll(-mContentView.getLeft(), 0, mMenuView.getWidth(), 0, 350); } else { mOpenScroller.startScroll(mContentView.getLeft(), 0, mMenuView.getWidth(), 0, 350); } postInvalidate(); } public void closeMenu() { if (mCloseScroller.computeScrollOffset()) { mCloseScroller.abortAnimation(); } if (state == STATE_OPEN) { state = STATE_CLOSE; swipe(0); } } public void openMenu() { if (!mSwipEnable) { return; } if (state == STATE_CLOSE) { state = STATE_OPEN; swipe(mMenuView.getWidth() * mSwipeDirection); } } public View getContentView() { return mContentView; } public SwipeMenuView getMenuView() { return mMenuView; } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mMenuView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec( getMeasuredHeight(), MeasureSpec.EXACTLY)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mContentView.layout(0, 0, getMeasuredWidth(), mContentView.getMeasuredHeight()); if (mSwipeDirection == SwipeMenuListView.DIRECTION_LEFT) { mMenuView.layout(getMeasuredWidth(), 0, getMeasuredWidth() + mMenuView.getMeasuredWidth(), mContentView.getMeasuredHeight()); } else { mMenuView.layout(-mMenuView.getMeasuredWidth(), 0, 0, mContentView.getMeasuredHeight()); } } public void setMenuHeight(int measuredHeight) { Log.i("byz", "pos = " + position + ", height = " + measuredHeight); LayoutParams params = (LayoutParams) mMenuView.getLayoutParams(); if (params.height != measuredHeight) { params.height = measuredHeight; mMenuView.setLayoutParams(mMenuView.getLayoutParams()); } } public void setSwipEnable(boolean swipEnable) { mSwipEnable = swipEnable; } public boolean getSwipEnable() { return mSwipEnable; } }
QQ效果圖: 修改後的效果圖:
上面的兩張效果圖可能看不出什麼,特別是我們上面說的那幾個效果,主要是視屏轉成gif之後又卡播放速度又快,所以想比較qq的效果和我們自己的效果,還是下載demo自己比較吧.我只能說改過之後的效果和QQ的效果基本就是一樣的.嘎嘎~~
話不多說開弄:打開eclipse,左上角File=》New=》Android Application Projicet,如圖: Next,Next,Next,N
今天給大家帶來一個顯示gif動態圖片的類庫,使用方法很簡單,只需三步。 1.把GifView.jar加入你的項目。 2.在布局xml中這樣寫: 3.在Activity中
一、概述距離上一篇博客有段時間沒更新了,主要是最近有些私事導致的,那麼就先來一篇簡單一點的博客脈動回來。對於加載圖片,大家都不陌生,一般為了盡可能避免OOM都
TCP和UDP在網絡傳輸中非常重要,在Android開發中同樣重要。首先我們來看一下什麼是TCP和UDP。什麼是TCP?TCP:Transmission Control