Androidカスタムviewシリーズの99.99%はQQ横滑り削除効果の実例コードを実現します。
まず、本論文はGitHub上の「baoyongzhang」のSwipeMenuListViewに基づいて修正されたものである。
https://github.com/baoyongzhang/SwipeMenuListView
この横滑り削除効果は見たことがありますが、最も効果的で柔軟なアイテムです。一つもありません。
しかし、それを使う前に、みんなに注意事項を2つ提出しなければなりません。
1、このプロジェクトはGrade dependenceをサポートしていますが、現在著者が提供している依存住所対応のプロジェクトは最新のプロジェクトではなく、後のコードとdemoで使用されているものと一致していないので、BaseSwipeListAdapterという種類はないと提示されています。このクラスは他の開発者が提出したものですので、最新のコードを使いたいなら、今はやはりコードをダウンロードしなければなりません。その後、自分のプロジェクトにリブラアルファイルをコピーして使います。
下の図は現在の著者が提供している住所です。最新のものではないので、最新のコードを使って友達に直接コードをダウンロードしてください。
2番目の注意事項はバグです。もし作者からのデモをテストしたら、もう一つのアイテムが引っ張られたら、この時にListViewを上に向けたり、下にスライドさせたりして、引っ張られたアイテムを画面から出して、また元に戻してください。これはもう引っ張られたアイテムをそのまま未解決の状態に戻します。これはユーザーを困惑させます。私は明らかにすでにこのメニューを引っ張り出しましたが、どうしてまたなくなったのですか?そしてこのソフトで作った本当のゴミの考えが生まれて、あなたのソフトを取り外してしまうかもしれません。
上の二つの注意事項に対して、一つ目は何も言いません。二つ目の問題はどうすればいいですか?焦らないでください。これは私達が今日話したい内容です。
まず私達は先にQQの横滑りを研究して効果を削除することができて、これに話及んであなたのqqを開けてその具体的な効果を見てみることができます。
あなたは、他のアイテムに指を置くと、それが直接に最初に引っ張られたアイテムをオフにします。そして、現在のアクションの後のイベントも応答しません。もしあなたが再び画面に指を置いたら、彼は関連するイベントになります。もしあなたの指が現在引き出したアイテムに置いたら、彼はこのアイテムを隠しません。そして正常に左右のスライドイベントに応答できます。
ok、QQの効果は分析済みです。その実現原理を検討します。
1,もし一つのアイテムがもう引っ張られてしまったら、他のアイテムに指を置いたら、そのアイテムを直接に外してしまいます。どうやって実現しますか?
まず、私達は今押しているこのアイテムが引っ張られているものかどうかを判断しなければなりません。そうでなければ、管理しなくてもいいです。コードは以下の通りです。
これは簡単です。viewのイベント配布原理により、あるタッチイベントでfalseに戻ったら、その後のイベントは彼に任せられません。つまり、ACTION_。DOWNの時にfalseに戻り、その後のACT ION_MOVE、ACTION_UPなどのイベントは応答しないので、この効果を実現するには、メニューを閉じた後にfalseに戻るだけでいいです。完全なコードは以下の通りです。
上記のコードはSwipeMenuListView類の修正に基づいています。完全な修正後のSwipeMenuListViewコードは以下の通りです。
ところで、ボタンを押したitemがすでに引かれたものではない時、相応の効果はすでに実現されました。もし押したアイテムがちょうどそれが既に引っ張られたアイテムだったら、効果はどうなりますか?
実はこの効果のもとの作者に対して実はすでに実現して、ただ実現するのがあまり良くなくて、いくつか問題が存在して、私はここでこの問題を修復しました。
問題があるのは、現在引き出しているアイテムを左右にスライドさせると、指を持ち上げた瞬間にスライド方向が開いた方向にスライドするという大きな確率で、この引かれたアイテムは閉じられます。例えば、左にスライドする方向を設定すると、開いているアイテムを左右にスライドさせます。そして指を持ち上げる前に左にスライドします。道理を言うとこのアイテムは開いた状態にあるはずですが、実際には大きな確率で指を上げると、このアイテムは閉じられてしまいます。だから、これはかなりユーザーの体験に影響を与えます。この方法のパラメータMotionEventは、SwipeMenuListViewのonTouchEventメソッドから渡されたものです。前に言ったように、指を上げると問題が発生すると、ACT IONを直接見ます。UP中の実現コード、
以下のコードに問題があります。
したがって、修正後のコードロジックは以下の通りです。
完全な修正後のSwipeMenuLayout類のコードは以下の通りです。
QQ効果図:
修正後の効果図:
上の2枚の効果図は何も見えないかもしれません。特に上で述べたいくつかの効果は、主に画面をgifに変えた後、カードの再生速度が速いので、qqの効果と自分達の効果を比較したいです。demoをダウンロードしたほうがいいです。変更後の効果とQQの効果は基本的に同じです。ガガ~!
https://github.com/baoyongzhang/SwipeMenuListView
この横滑り削除効果は見たことがありますが、最も効果的で柔軟なアイテムです。一つもありません。
しかし、それを使う前に、みんなに注意事項を2つ提出しなければなりません。
1、このプロジェクトはGrade dependenceをサポートしていますが、現在著者が提供している依存住所対応のプロジェクトは最新のプロジェクトではなく、後のコードとdemoで使用されているものと一致していないので、BaseSwipeListAdapterという種類はないと提示されています。このクラスは他の開発者が提出したものですので、最新のコードを使いたいなら、今はやはりコードをダウンロードしなければなりません。その後、自分のプロジェクトにリブラアルファイルをコピーして使います。
下の図は現在の著者が提供している住所です。最新のものではないので、最新のコードを使って友達に直接コードをダウンロードしてください。
2番目の注意事項はバグです。もし作者からのデモをテストしたら、もう一つのアイテムが引っ張られたら、この時にListViewを上に向けたり、下にスライドさせたりして、引っ張られたアイテムを画面から出して、また元に戻してください。これはもう引っ張られたアイテムをそのまま未解決の状態に戻します。これはユーザーを困惑させます。私は明らかにすでにこのメニューを引っ張り出しましたが、どうしてまたなくなったのですか?そしてこのソフトで作った本当のゴミの考えが生まれて、あなたのソフトを取り外してしまうかもしれません。
上の二つの注意事項に対して、一つ目は何も言いません。二つ目の問題はどうすればいいですか?焦らないでください。これは私達が今日話したい内容です。
まず私達は先にQQの横滑りを研究して効果を削除することができて、これに話及んであなたのqqを開けてその具体的な効果を見てみることができます。
あなたは、他のアイテムに指を置くと、それが直接に最初に引っ張られたアイテムをオフにします。そして、現在のアクションの後のイベントも応答しません。もしあなたが再び画面に指を置いたら、彼は関連するイベントになります。もしあなたの指が現在引き出したアイテムに置いたら、彼はこのアイテムを隠しません。そして正常に左右のスライドイベントに応答できます。
ok、QQの効果は分析済みです。その実現原理を検討します。
1,もし一つのアイテムがもう引っ張られてしまったら、他のアイテムに指を置いたら、そのアイテムを直接に外してしまいます。どうやって実現しますか?
まず、私達は今押しているこのアイテムが引っ張られているものかどうかを判断しなければなりません。そうでなければ、管理しなくてもいいです。コードは以下の通りです。
if (view instanceof SwipeMenuLayout) {
SwipeMenuLayout touchView = (SwipeMenuLayout) view;
if (!touchView.isOpen()) {
mTouchView.smoothCloseMenu();
}
}
2、そして現在の動作の後続のイベントも応答しなくなりました。どうやって実現しますか?これは簡単です。viewのイベント配布原理により、あるタッチイベントでfalseに戻ったら、その後のイベントは彼に任せられません。つまり、ACTION_。DOWNの時にfalseに戻り、その後のACT ION_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;
}
}
このような数行のコードは先ほど分析したqq効果の中の前半部分の効果を実現しました。つまり、一つのアイテムが引っ張られたら、他のアイテムに指を置いた時、直接に引き出したアイテムを閉じます。そして、現在の動作の後のイベントも応答しなくなります。もう一回スクリーンに指を置いたら、彼は事件に応じられます。上記のコードは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;
}
}
このようにqqのその効果を実現するのは簡単ですか?でも、原作者がよく書いてくれたおかげで、添削しやすくなりました。ところで、ボタンを押したitemがすでに引かれたものではない時、相応の効果はすでに実現されました。もし押したアイテムがちょうどそれが既に引っ張られたアイテムだったら、効果はどうなりますか?
実はこの効果のもとの作者に対して実はすでに実現して、ただ実現するのがあまり良くなくて、いくつか問題が存在して、私はここでこの問題を修復しました。
問題があるのは、現在引き出しているアイテムを左右にスライドさせると、指を持ち上げた瞬間にスライド方向が開いた方向にスライドするという大きな確率で、この引かれたアイテムは閉じられます。例えば、左にスライドする方向を設定すると、開いているアイテムを左右にスライドさせます。そして指を持ち上げる前に左にスライドします。道理を言うとこのアイテムは開いた状態にあるはずですが、実際には大きな確率で指を上げると、このアイテムは閉じられてしまいます。だから、これはかなりユーザーの体験に影響を与えます。この方法のパラメータMotionEventは、SwipeMenuListViewのonTouchEventメソッドから渡されたものです。前に言ったように、指を上げると問題が発生すると、ACT IONを直接見ます。UP中の実現コード、
以下のコードに問題があります。
if ((isFling || Math.abs(mDownX - event.getX()) > (mMenuView.getWidth() / 2)) &&
Math.signum(mDownX - event.getX()) == mSwipeDirection) {
// open
smoothOpenMenu();
} else {
// close
smoothCloseMenu();
return false;
}
私達は分析してみます。isFlingは見なくても大丈夫です。問題はここにあるのではなく、後の判断ロジックを見ます。X軸方向のスライド距離の絶対値はメニュー幅の半分より大きく、スライド方向は開いた方向にスライドする時に、一つのアイテムが開かれます。他のすべての状況が直接else文に進んでitemを閉じます。問題はここに現れます。上記の問題もこれによって引き起こされたものです。現在のitemはすでに引き出しられていますので、私達のスライド距離は絶対値がメニュー幅の半分を超えていません。しかし、私達がスライドする方向はitemに引っ張られる方向です。道理を言えば、私達のitemは引き出された状態で表示し続けるべきです。コードの中の判定ロジックによって、この場合は直接else文を入れます。つまりこのitemを閉じます。だからここのロジックは大きな問題があると判断します。私達が必要な効果は、もう引っ張られました。あなたが引き出された方向にスライドし続けるなら、elseに進みません。直接にifでスライドの距離を判断します。このような論理が正しいです。最初のスライドの時に問題が起こらないように、初めてのスライドに対応した操作をしなければなりません。したがって、修正後のコードロジックは以下の通りです。
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;
}
このようにして、私達はqqの第二の効果を完璧に実現しました。つまり、もしあなたの指が現在引き出したアイテムに置いたら、彼はこのアイテムを隠すことはできません。正常に左右のスライドイベントに応答できます。完全な修正後の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;
}
}
では、私たちのタイトルの名前は99.99%で横滑りの削除効果を実現していますので、効果図を見に来てみます。99.99%であるかどうかは同じです。QQ効果図:
修正後の効果図:
上の2枚の効果図は何も見えないかもしれません。特に上で述べたいくつかの効果は、主に画面をgifに変えた後、カードの再生速度が速いので、qqの効果と自分達の効果を比較したいです。demoをダウンロードしたほうがいいです。変更後の効果とQQの効果は基本的に同じです。ガガ~!