之前寫過一個類似IOS的左劃出現刪除,點擊刪除的博客,但是在後期的開發中,遇到了一些問題,光設計前端效果的話是沒有問題的,但是發現沒有Item的點擊,一個側滑的事件和點擊item的事件監聽衝突了。所以在此做出一定的修改,也找了很多資料來了解。最後實現了,既可以側滑刪除監聽,也可以Item點擊監聽。
這只是針對我的項目來寫出的東西,有些項目也許只需要側滑的刪除,所以沒有誰對誰錯。根據自己的需求來做吧。
實現後的效果圖:
那就開始上代碼了:
首先是封裝好的兩個類庫,一個是pulltorefresh的類庫,這個是下拉刷新的類庫,裏面封裝了下拉刷新的內容。
另一個就是swipemenulistview的類庫,這個就是側滑刪除的listview的類庫。
這倆類庫已經封裝好,大家可以直接複製代碼到制定的目錄。到時候寫的時候直接引用就可以了。
大家可以去資源下載地址下載這兩個類庫,方便大家的使用,下面這個連接進行下載:
關鍵的代碼還是我們自己的引用部分以及設計部分:
首先上我們的代碼,將兩個類庫結合使用,寫我們的PullToRefreshSwipeMenuListView.java:
import android.content.Context; import android.os.AsyncTask; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.TextView; /** * Created by Administrator on 2016/2/27. */ public class PullToRefreshSwipeMenuListView extends ListView implements OnScrollListener { private static final int TOUCH_STATE_NONE = 0; private static final int TOUCH_STATE_X = 1; private static final int TOUCH_STATE_Y = 2; 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 Interpolator mCloseInterpolator; private Interpolator mOpenInterpolator; private float mLastY = -1; // save event y private Scroller mScroller; // used for scroll back private OnScrollListener mScrollListener; // user's scroll listener // the interface to trigger refresh and load more. private IXListViewListener mListViewListener; // -- header view private PullToRefreshListHeader mHeaderView; // header view content, use it to calculate the Header's height. And hide it // when disable pull refresh. private RelativeLayout mHeaderViewContent; private TextView mHeaderTimeView; private int mHeaderViewHeight; // header view's height private boolean mEnablePullRefresh = true; private boolean mPullRefreshing = false; // is refreashing. // -- footer view private PullToRefreshListFooter mFooterView; private boolean mEnablePullLoad; private boolean mPullLoading; private boolean mIsFooterReady = false; private int mTotalItemCount; private int mScrollBack; private final static int SCROLLBACK_HEADER = 0; private final static int SCROLLBACK_FOOTER = 1; private final static int SCROLL_DURATION = 400; private final static int PULL_LOAD_MORE_DELTA = 50; private final static float OFFSET_RADIO = 1.8f; public PullToRefreshSwipeMenuListView(Context context) { super(context); init(context); } public PullToRefreshSwipeMenuListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public PullToRefreshSwipeMenuListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { mScroller = new Scroller(context, new DecelerateInterpolator()); super.setOnScrollListener(this); // init header view mHeaderView = new PullToRefreshListHeader(context); mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content); mHeaderTimeView = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time); addHeaderView(mHeaderView); // init footer view mFooterView = new PullToRefreshListFooter(context); // init header height mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mHeaderViewHeight = mHeaderViewContent.getHeight(); getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); MAX_X = dp2px(MAX_X); MAX_Y = dp2px(MAX_Y); mTouchState = TOUCH_STATE_NONE; } @Override public void setAdapter(ListAdapter adapter) { if (mIsFooterReady == false) { mIsFooterReady = true; addFooterView(mFooterView); } 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) { if (mOnMenuItemClickListener != null) { mOnMenuItemClickListener.onMenuItemClick(view.getPosition(), menu, index); } if (mTouchView != null) { 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) { return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { if (mLastY == -1) { mLastY = ev.getRawY(); } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mLastY = ev.getRawY(); int oldPos = mTouchPosition; mDownX = ev.getX(); mDownY = ev.getY(); mTouchState = TOUCH_STATE_NONE; mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()); if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) { mTouchState = TOUCH_STATE_X; mTouchView.onSwipe(ev); return true; } View view = getChildAt(mTouchPosition - getFirstVisiblePosition()); if (mTouchView != null && mTouchView.isOpen()) { mTouchView.smoothCloseMenu(); mTouchView = null; return super.onTouchEvent(ev); } if (view instanceof SwipeMenuLayout) { mTouchView = (SwipeMenuLayout) view; } if (mTouchView != null) { mTouchView.onSwipe(ev); } break; case MotionEvent.ACTION_MOVE: final float deltaY = ev.getRawY() - mLastY; float dy = Math.abs((ev.getY() - mDownY)); float dx = Math.abs((ev.getX() - mDownX)); mLastY = ev.getRawY(); if ((mTouchView == null || !mTouchView.isActive()) && Math.pow(dx, 2) / Math.pow(dy, 2) <= 3) { if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) { // the first item is showing, header has shown or pull down. updateHeaderHeight(deltaY / OFFSET_RADIO); invokeOnScrolling(); } else if ((mFooterView.getBottomMargin() > 0 || deltaY < 0)) { // last item, already pulled up or want to pull up. updateFooterHeight(-deltaY / OFFSET_RADIO); } } 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: mLastY = -1; // reset if (mEnablePullLoad && mFooterView.getHeight()>0 && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) { startLoadMore(); resetFooterHeight(); new ResetHeaderHeightTask().execute(); } else if (getFirstVisiblePosition() == 0) { // invoke refresh if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mPullRefreshing = true; mHeaderView.setState(PullToRefreshListHeader.STATE_REFRESHING); if (mListViewListener != null) { mListViewListener.onRefresh(); } } resetHeaderHeight(); } if (mTouchState == TOUCH_STATE_X) { if (mTouchView != null) { mTouchView.onSwipe(ev); if (!mTouchView.isOpen()) { 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); } class ResetHeaderHeightTask extends AsyncTask<Void, Void, Void> { protected Void doInBackground(Void... params) { try { Thread.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } return null; } protected void onPostExecute(Void result) { mPullRefreshing = false; mHeaderView.setState(PullToRefreshListHeader.STATE_NORMAL); resetHeaderHeight(); } } 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.smoothOpenMenu(); } } } 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 static interface OnMenuItemClickListener { void onMenuItemClick(int position, SwipeMenu menu, int index); } public static interface OnSwipeListener { void onSwipeStart(int position); void onSwipeEnd(int position); } public void setPullRefreshEnable(boolean enable) { mEnablePullRefresh = enable; if (!mEnablePullRefresh) { // disable, hide the content mHeaderViewContent.setVisibility(View.INVISIBLE); } else { mHeaderViewContent.setVisibility(View.VISIBLE); } } /** * enable or disable pull up load more feature. * * @param enable */ public void setPullLoadEnable(boolean enable) { mEnablePullLoad = enable; if (!mEnablePullLoad) { mFooterView.hide(); mFooterView.setOnClickListener(null); } else { mPullLoading = false; mFooterView.show(); mFooterView.setState(PullToRefreshListFooter.STATE_NORMAL); // both "pull up" and "click" will invoke load more. mFooterView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startLoadMore(); } }); } } /** * stop refresh, reset header view. */ public void stopRefresh() { if (mPullRefreshing == true) { mPullRefreshing = false; resetHeaderHeight(); } } /** * stop load more, reset footer view. */ public void stopLoadMore() { if (mPullLoading == true) { mPullLoading = false; mFooterView.setState(PullToRefreshListFooter.STATE_NORMAL); } } /** * set last refresh time * * @param time */ public void setRefreshTime(String time) { mHeaderTimeView.setText(time); } private void invokeOnScrolling() { if (mScrollListener instanceof OnXScrollListener) { OnXScrollListener l = (OnXScrollListener) mScrollListener; l.onXScrolling(this); } } private void updateHeaderHeight(float delta) { mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight()); if (mEnablePullRefresh && !mPullRefreshing) { if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mHeaderView.setState(PullToRefreshListHeader.STATE_READY); } else { mHeaderView.setState(PullToRefreshListHeader.STATE_NORMAL); } } setSelection(0); // scroll to top each time } /** * reset header view's height. */ private void resetHeaderHeight() { int height = mHeaderView.getVisiableHeight(); if (height == 0) // not visible. return; // refreshing and header isn't shown fully. do nothing. if (mPullRefreshing && height <= mHeaderViewHeight) { return; } int finalHeight = 0; // default: scroll back to dismiss header. // is refreshing, just scroll back to show all the header. if (mPullRefreshing && height > mHeaderViewHeight) { finalHeight = mHeaderViewHeight; } mScrollBack = SCROLLBACK_HEADER; mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION); // trigger computeScroll invalidate(); } private void updateFooterHeight(float delta) { int height = mFooterView.getBottomMargin() + (int) delta; if (mEnablePullLoad && !mPullLoading) { if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load // more. mFooterView.setState(PullToRefreshListFooter.STATE_READY); } else { mFooterView.setState(PullToRefreshListFooter.STATE_NORMAL); } } mFooterView.setBottomMargin(height); // setSelection(mTotalItemCount - 1); // scroll to bottom } private void resetFooterHeight() { int bottomMargin = mFooterView.getBottomMargin(); if (bottomMargin > 0) { mScrollBack = SCROLLBACK_FOOTER; mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION); invalidate(); } } private void startLoadMore() { mPullLoading = true; mFooterView.setState(PullToRefreshListFooter.STATE_LOADING); if (mListViewListener != null) { mListViewListener.onLoadMore(); } } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { if (mScrollBack == SCROLLBACK_HEADER) { mHeaderView.setVisiableHeight(mScroller.getCurrY()); } else { mFooterView.setBottomMargin(mScroller.getCurrY()); } postInvalidate(); invokeOnScrolling(); } super.computeScroll(); } @Override public void setOnScrollListener(OnScrollListener l) { mScrollListener = l; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mScrollListener != null) { mScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // send to user's listener mTotalItemCount = totalItemCount; if (mScrollListener != null) { mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } public void setXListViewListener(IXListViewListener l) { mListViewListener = l; } /** * you can listen ListView.OnScrollListener or this one. it will invoke * onXScrolling when header/footer scroll back. */ public interface OnXScrollListener extends OnScrollListener { public void onXScrolling(View view); } /** * implements this interface to get refresh/load more event. */ public interface IXListViewListener { public void onRefresh(); public void onLoadMore(); } }
這部分封裝好以後,就可以在我們的xml中進行引用了:
listview的佈局部分:
<com.view.PullToRefreshSwipeMenuListView android:id="@+id/list_address" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/toolbar" android:layout_marginTop="10dp" android:divider="@null"> </com.view.PullToRefreshSwipeMenuListView>
引用成功後,需要的就是我們的Adapter進行適配:
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; import java.util.Map; /** * Created by Administrator on 2016/8/5 0005. */ public class MyAddressAdapter extends BaseAdapter { private Context context; private List<Map<String, Object>> address; public MyAddressAdapter(Context context, List<Map<String, Object>> address) { this.context = context; this.address = address; } @Override public int getCount() { if (address == null) { return 0; } else { return address.size(); } } @Override public Object getItem(int position) { if (address == null) { return 0; } else { return address.get(position); } } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View contentView, ViewGroup arg2) { ViewHolder holder = null; if (contentView == null) { holder = new ViewHolder(); contentView = LayoutInflater.from(context).inflate(R.layout.list_address_item, null); holder.img_first = (ImageView) contentView.findViewById(R.id.img_first); holder.text_name = (TextView) contentView.findViewById(R.id.text_name); holder.text_phone = (TextView) contentView.findViewById(R.id.text_phone); holder.text_address = (TextView) contentView.findViewById(R.id.text_address); contentView.setTag(holder); } else { holder = (ViewHolder) contentView.getTag(); } holder.text_name.setText((String) address.get(position).get("title")); return contentView; } class ViewHolder { public TextView text_phone; public TextView text_address; public TextView text_name; ImageView img_first; } }
Adapter中的涉及到的佈局是我們的list_item的佈局,然後對要進行修改的控件進行聲明並且獲取資源等。關於list_item中的佈局就不再給出了。每個人的item是不一樣的。adapter根據自己的需求來弄。完事都已經具備了,就等我們在自己需要的Activity中進行使用了。如下:
private List<Map<String, Object>> address; private PullToRefreshSwipeMenuListView mListView; private MyAddressAdapter addressAdapter;聲明我們需要的參數,然後在我們的onCreate中:
/** * 對listview的處理適配 * */ mListView = (PullToRefreshSwipeMenuListView) findViewById(R.id.list_address); afterPost();
然後就是我們的afterPost()了:
private void afterPost() { address = getMessage(); addressAdapter = new MyAddressAdapter(this, address); mListView.setAdapter(addressAdapter); mListView.setPullRefreshEnable(true); mListView.setPullLoadEnable(true); mListView.setXListViewListener(this); // step 1. 創建刪除按鈕 SwipeMenuCreator creator = new SwipeMenuCreator() { @Override public void create(SwipeMenu menu) { // create "delete" item SwipeMenuItem deleteItem = new SwipeMenuItem(getApplicationContext()); // set item background deleteItem.setBackground(new ColorDrawable(Color.rgb(222, 102, 84))); // set item width deleteItem.setWidth(dp2px(60)); deleteItem.setTitle("刪除"); deleteItem.setTitleColor(Color.rgb(255, 255, 255)); deleteItem.setTitleSize(16); // add to menu menu.addMenuItem(deleteItem); } }; // set creator mListView.setMenuCreator(creator); // step 2. 刪除 mListView.setOnMenuItemClickListener(new PullToRefreshSwipeMenuListView.OnMenuItemClickListener() { @Override public void onMenuItemClick(int position, SwipeMenu menu, int index) { switch (index) { case 0: Toast.makeText(getApplicationContext(), "點擊刪除", Toast.LENGTH_SHORT).show(); break; } } }); // set SwipeListener mListView.setOnSwipeListener(new PullToRefreshSwipeMenuListView.OnSwipeListener() { @Override public void onSwipeStart(int position) { // swipe start } @Override public void onSwipeEnd(int position) { // swipe end } }); // test item click mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //position 如果有headview position =0 的第一個爲headview //id:如果有headview或者footview 則這兩個view的id爲-1 if (id != -1) { Toast.makeText(getApplicationContext(), "點擊的第" + position + "個Item", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "有headView", Toast.LENGTH_SHORT).show(); } } }); } /** * 初始化item信息 */ private List<Map<String, Object>> getMessage() { List<Map<String, Object>> listItems = new ArrayList<Map<String, Object>>(); for (int i = 0; i < 5; i++) { Map<String, Object> map = new HashMap<String, Object>(); map.put("", ""); map.put("image", imgeIDs[i]); //圖片資源 map.put("title", "恆聖" + i); //title map.put("info", goodsNames[i]); //物品名稱 map.put("detail", goodsDetails[i]); //物品詳情 listItems.add(map); } return listItems; } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); }
這樣就實現了我們的側滑刪除並且點擊Item的效果了。