>在我的应用程序中,我添加了“滑动”以在Listview中关闭项目以及“撤消”选项.
Requried:
>我想在Listview中添加来自服务器的Section Headers,如Group1,Group2,..等.
使用Undo解除滑动代码.请帮助我实现我的要求.
Link i referred
ListviewActivity.class
package com.swipe.delete; import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.hudomju.swipe.SwipeToDismissTouchListener; import com.hudomju.swipe.adapter.ListViewAdapter; import java.util.ArrayList; import java.util.List; import static android.widget.Toast.LENGTH_SHORT; public class ListViewActivity extends Activity { private static final int TIME_TO_AUTOMATICALLY_DISMISS_ITEM = 3000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.list_view_activity); init((ListView) findViewById(R.id.list_view)); } private void init(ListView listView) { final MyBaseAdapter adapter = new MyBaseAdapter(); listView.setAdapter(adapter); final SwipeToDismissTouchListener<ListViewAdapter> touchListener = new SwipeToDismissTouchListener<>( new ListViewAdapter(listView),new SwipeToDismissTouchListener.DismissCallbacks<ListViewAdapter>() { @Override public boolean canDismiss(int position) { return true; } @Override public void onPendingDismiss(ListViewAdapter recyclerView,int position) { } @Override public void onDismiss(ListViewAdapter view,int position) { adapter.remove(position); } }); touchListener.setDismissDelay(TIME_TO_AUTOMATICALLY_DISMISS_ITEM); listView.setOnTouchListener(touchListener); // Setting this scroll listener is required to ensure that during ListView scrolling,// we don't look for swipes. listView.setOnScrollListener((AbsListView.OnScrollListener) touchListener.makeScrollListener()); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent,View view,int position,long id) { if (touchListener.existPendingDismisses()) { touchListener.undoPendingDismiss(); } else { Toast.makeText(ListViewActivity.this,"Position " + position,LENGTH_SHORT).show(); } } }); } static class MyBaseAdapter extends BaseAdapter { private static final int SIZE = 100; private final List<String> mDataSet = new ArrayList<>(); MyBaseAdapter() { for (int i = 0; i < SIZE; i++) mDataSet.add(i,"This is row number " + i); } @Override public int getCount() { return mDataSet.size(); } @Override public String getItem(int position) { return mDataSet.get(position); } @Override public long getItemId(int position) { return position; } public void remove(int position) { mDataSet.remove(position); notifyDataSetChanged(); } static class ViewHolder { TextView dataTextView; ViewHolder(View view) { dataTextView = (TextView) view.findViewById(R.id.txt_data); view.setTag(this); } } @Override public View getView(int position,View convertView,ViewGroup parent) { ViewHolder viewHolder = convertView == null ? new ViewHolder(convertView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_item,parent,false)) : (ViewHolder) convertView.getTag(); viewHolder.dataTextView.setText(mDataSet.get(position)); return convertView; } } }
SwipeToDismissTouchListener.class
package com.swipe.Delete; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.graphics.Rect; import android.os.Handler; import android.support.annotation.NonNull; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.ListView; public class SwipeToDismissTouchListener<SomeCollectionView extends ViewAdapter> implements View.OnTouchListener { // Cached ViewConfiguration and system-wide constant values private final int mSlop; private final int mMinFlingVelocity; private final int mMaxFlingVelocity; private final long mAnimationTime; // Fixed properties private final SomeCollectionView mRecyclerView; private final DismissCallbacks<SomeCollectionView> mCallbacks; private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero // Transient properties private PendingDismissData mPendingDismiss; private float mDownX; private float mDownY; private boolean mSwiping; private int mSwipingSlop; private VelocityTracker mVelocityTracker; private int mDownPosition; private RowContainer mRowContainer; private boolean mPaused; // Handler to dismiss pending items after a delay private final Handler mHandler; private final Runnable mDismissRunnable = new Runnable() { @Override public void run() { processPendingDismisses(); } }; private long mDismissDelayMillis = -1; // negative to disable automatic dismissing public class RowContainer { final View container; final View dataContainer; final View undoContainer; boolean dataContainerHasBeenDismissed; public RowContainer(ViewGroup container) { this.container = container; dataContainer = container.getChildAt(0); undoContainer = container.getChildAt(1); dataContainerHasBeenDismissed = false; } View getCurrentSwipingView() { return dataContainerHasBeenDismissed ? undoContainer: dataContainer; } } /** * The callback interface used by {@link SwipeToDismissTouchListener} to inform its client * about a successful dismissal of one or more list item positions. */ public interface DismissCallbacks<SomeCollectionView extends ViewAdapter> { /** * Called to determine whether the given position can be dismissed. */ boolean canDismiss(int position); /** * Called when an item is swiped away by the user and the undo layout is completely visible. * Do NOT remove the list item yet,that should be done in { #onDismiss(com.hudomju.swipe.adapter.ViewAdapter,int)} * This may also be called immediately before and item is completely dismissed. * * @param recyclerView The originating {android.support.v7.widget.RecyclerView}. * @param position The position of the dismissed item. */ void onPendingDismiss(SomeCollectionView recyclerView,int position); /** * Called when the item is completely dismissed and removed from the list,after the undo layout is hidden. * * @param recyclerView The originating { android.support.v7.widget.RecyclerView}. * @param position The position of the dismissed item. */ void onDismiss(SomeCollectionView recyclerView,int position); } /** * Constructs a new swipe-to-dismiss touch listener for the given list view. * * @param recyclerView The list view whose items should be dismissable. * @param callbacks The callback to trigger when the user has indicated that she would like to * dismiss one or more list items. */ public SwipeToDismissTouchListener(SomeCollectionView recyclerView,DismissCallbacks<SomeCollectionView> callbacks) { ViewConfiguration vc = ViewConfiguration.get(recyclerView.getContext()); mSlop = vc.getScaledTouchSlop(); mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); mAnimationTime = recyclerView.getContext().getResources().getInteger( android.R.integer.config_shortAnimTime); mRecyclerView = recyclerView; mCallbacks = callbacks; mHandler = new Handler(); } /** * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures. * * @param enabled Whether or not to watch for gestures. */ public void setEnabled(boolean enabled) { mPaused = !enabled; } /** * Set the delay after which the pending items will be dismissed when there was no user action. * Set to a negative value to disable automatic dismissing items. * @param dismissDelayMillis The delay between onPendingDismiss and onDismiss calls,in milliseconds. */ public void setDismissDelay(long dismissDelayMillis) { this.mDismissDelayMillis = dismissDelayMillis; } /** * Returns an {@link AbsListView.OnScrollListener} to be added to the {@link * ListView} using {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}. * If a scroll listener is already assigned,the caller should still pass scroll changes through * to this listener. This will ensure that this {@link SwipeToDismissTouchListener} is * paused during list view scrolling.</p> * * @see SwipeToDismissTouchListener */ public Object makeScrollListener() { return mRecyclerView.makeScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView absListView,int scrollState) { processPendingDismisses(); setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); } @Override public void onScroll(AbsListView absListView,int i,int i1,int i2) { } }); } @Override public boolean onTouch(View view,MotionEvent motionEvent) { if (mViewWidth < 2) { mViewWidth = mRecyclerView.getWidth(); } switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: { if (mPaused) { return false; } // TODO: ensure this is a finger,and set a flag // Find the child view that was touched (perform a hit test) Rect rect = new Rect(); int childCount = mRecyclerView.getChildCount(); int[] listViewCoords = new int[2]; mRecyclerView.getLocationOnScreen(listViewCoords); int x = (int) motionEvent.getRawX() - listViewCoords[0]; int y = (int) motionEvent.getRawY() - listViewCoords[1]; View child; for (int i = 0; i < childCount; i++) { child = mRecyclerView.getChildAt(i); child.getHitRect(rect); if (rect.contains(x,y)) { assert child instanceof ViewGroup && ((ViewGroup) child).getChildCount() == 2 : "Each child needs to extend from ViewGroup and have two children"; boolean dataContainerHasBeenDismissed = mPendingDismiss != null && mPendingDismiss.position == mRecyclerView.getChildPosition(child) && mPendingDismiss.rowContainer.dataContainerHasBeenDismissed; mRowContainer = new RowContainer((ViewGroup) child); mRowContainer.dataContainerHasBeenDismissed = dataContainerHasBeenDismissed; break; } } if (mRowContainer != null) { mDownX = motionEvent.getRawX(); mDownY = motionEvent.getRawY(); mDownPosition = mRecyclerView.getChildPosition(mRowContainer.container); if (mCallbacks.canDismiss(mDownPosition)) { mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(motionEvent); } else { mRowContainer = null; } } return false; } case MotionEvent.ACTION_CANCEL: { if (mVelocityTracker == null) { break; } if (mRowContainer != null && mSwiping) { // cancel mRowContainer.getCurrentSwipingView() .animate() .translationX(0) .alpha(1) .setDuration(mAnimationTime) .setListener(null); } mVelocityTracker.recycle(); mVelocityTracker = null; mDownX = 0; mDownY = 0; mRowContainer = null; mDownPosition = ListView.INVALID_POSITION; mSwiping = false; break; } case MotionEvent.ACTION_UP: { if (mVelocityTracker == null) { break; } float deltaX = motionEvent.getRawX() - mDownX; mVelocityTracker.addMovement(motionEvent); mVelocityTracker.computeCurrentVelocity(1000); float velocityX = mVelocityTracker.getXVelocity(); float absVelocityX = Math.abs(velocityX); float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); boolean dismiss = false; boolean dismissRight = false; if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) { dismiss = true; dismissRight = deltaX > 0; } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity && absVelocityY < absVelocityX && mSwiping) { // dismiss only if flinging in the same direction as dragging dismiss = (velocityX < 0) == (deltaX < 0); dismissRight = mVelocityTracker.getXVelocity() > 0; } if (dismiss && mDownPosition != ListView.INVALID_POSITION) { // dismiss final RowContainer downView = mRowContainer; // mDownView gets null'd before animation ends final int downPosition = mDownPosition; mRowContainer.getCurrentSwipingView() .animate() .translationX(dismissRight ? mViewWidth : -mViewWidth) .alpha(0) .setDuration(mAnimationTime) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { performDismiss(downView,downPosition); } }); } else { // cancel mRowContainer.getCurrentSwipingView() .animate() .translationX(0) .alpha(1) .setDuration(mAnimationTime) .setListener(null); } mVelocityTracker.recycle(); mVelocityTracker = null; mDownX = 0; mDownY = 0; mRowContainer = null; mDownPosition = ListView.INVALID_POSITION; mSwiping = false; break; } case MotionEvent.ACTION_MOVE: { if (mVelocityTracker == null || mPaused) { break; } mVelocityTracker.addMovement(motionEvent); float deltaX = motionEvent.getRawX() - mDownX; float deltaY = motionEvent.getRawY() - mDownY; if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { mSwiping = true; mSwipingSlop = deltaX > 0 ? mSlop : -mSlop; mRecyclerView.requestDisallowInterceptTouchEvent(true); // Cancel ListView's touch (un-highlighting the item) MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); mRecyclerView.onTouchEvent(cancelEvent); cancelEvent.recycle(); } if (mSwiping) { mRowContainer.getCurrentSwipingView().setTranslationX(deltaX - mSwipingSlop); mRowContainer.getCurrentSwipingView().setAlpha(Math.max(0f,Math.min(1f,1f - 2f * Math.abs(deltaX) / mViewWidth))); return true; } break; } } return false; } class PendingDismissData implements Comparable<PendingDismissData> { public int position; public RowContainer rowContainer; public PendingDismissData(int position,RowContainer rowContainer) { this.position = position; this.rowContainer= rowContainer; } @Override public int compareTo(@NonNull PendingDismissData other) { // Sort by descending position return other.position - position; } } private void performDismiss(RowContainer dismissView,int dismissPosition) { // Animate the dismissed list item to zero-height and fire the dismiss callback when // all dismissed list item animations have completed. This triggers layout on each animation // frame; in the future we may want to do something smarter and more performant. if (mPendingDismiss != null) { boolean dismissingDifferentRow = mPendingDismiss.position != dismissPosition; int newPosition = mPendingDismiss.position < dismissPosition ? dismissPosition-1 : dismissPosition; processPendingDismisses(); if (dismissingDifferentRow) { addPendingDismiss(dismissView,newPosition); } } else { addPendingDismiss(dismissView,dismissPosition); } } private void addPendingDismiss(RowContainer dismissView,int dismissPosition) { dismissView.dataContainerHasBeenDismissed = true; dismissView.undoContainer.setVisibility(View.VISIBLE); mPendingDismiss = new PendingDismissData(dismissPosition,dismissView); // Notify the callbacks mCallbacks.onPendingDismiss(mRecyclerView,dismissPosition); // Automatically dismiss the item after a certain delay if(mDismissDelayMillis >= 0) mHandler.removeCallbacks(mDismissRunnable); mHandler.postDelayed(mDismissRunnable,mDismissDelayMillis); } /** * If a view was dismissed and the undo container is showing it will proceed with the final * dismiss of the item. * @return whether there were any pending rows to be dismissed. */ public boolean processPendingDismisses() { boolean existPendingDismisses = existPendingDismisses(); if (existPendingDismisses) processPendingDismisses(mPendingDismiss); return existPendingDismisses; } /** * Whether a row has been dismissed and is waiting for confirmation * @return whether there are any pending rows to be dismissed. */ public boolean existPendingDismisses() { return mPendingDismiss != null && mPendingDismiss.rowContainer.dataContainerHasBeenDismissed; } /** * If a view was dismissed and the undo container is showing it will undo and make the data * container reappear. * @return whether there were any pending rows to be dismissed. */ public boolean undoPendingDismiss() { boolean existPendingDismisses = existPendingDismisses(); if (existPendingDismisses) { mPendingDismiss.rowContainer.undoContainer.setVisibility(View.GONE); mPendingDismiss.rowContainer.dataContainer .animate() .translationX(0) .alpha(1) .setDuration(mAnimationTime) .setListener(null); mPendingDismiss = null; } return existPendingDismisses; } private void processPendingDismisses(final PendingDismissData pendingDismissData) { mPendingDismiss = null; final ViewGroup.LayoutParams lp = pendingDismissData.rowContainer.container.getLayoutParams(); final int originalHeight = pendingDismissData.rowContainer.container.getHeight(); ValueAnimator animator = ValueAnimator.ofInt(originalHeight,1).setDuration(mAnimationTime); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mCallbacks.canDismiss(pendingDismissData.position)) mCallbacks.onDismiss(mRecyclerView,pendingDismissData.position); pendingDismissData.rowContainer.dataContainer.post(new Runnable() { @Override public void run() { pendingDismissData.rowContainer.dataContainer.setTranslationX(0); pendingDismissData.rowContainer.dataContainer.setAlpha(1); pendingDismissData.rowContainer.undoContainer.setVisibility(View.GONE); pendingDismissData.rowContainer.undoContainer.setTranslationX(0); pendingDismissData.rowContainer.undoContainer.setAlpha(1); lp.height = originalHeight; pendingDismissData.rowContainer.container.setLayoutParams(lp); } }); } }); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { lp.height = (Integer) valueAnimator.getAnimatedValue(); pendingDismissData.rowContainer.container.setLayoutParams(lp); } }); animator.start(); } }
解决方法
我已更新并评论您的代码以实现您想要的.首先检查MyBaseAdapter()构造函数,然后检查getView()方法.
Package com.swipe.delete; import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.hudomju.swipe.SwipeToDismissTouchListener; import com.hudomju.swipe.adapter.ListViewAdapter; import java.util.ArrayList; import java.util.List; import static android.widget.Toast.LENGTH_SHORT; public class ListViewActivity extends Activity { private static final int TIME_TO_AUTOMATICALLY_DISMISS_ITEM = 3000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.list_view_activity); init((ListView) findViewById(R.id.list_view)); } private void init(ListView listView) { final MyBaseAdapter adapter = new MyBaseAdapter(); listView.setAdapter(adapter); final SwipeToDismissTouchListener<ListViewAdapter> touchListener = new SwipeToDismissTouchListener<>( new ListViewAdapter(listView),LENGTH_SHORT).show(); } } }); } static class MyBaseAdapter extends BaseAdapter { private static final int SIZE = 100; private final List<String> mDataSet = new ArrayList<>(); MyBaseAdapter() { for (int i = 0; i < 5; i++){ // You should add an object containing type of view like section or sectionHeader along with your string. // Here i am just using string search to detect whether it is sectionHeader or section. have a look at getView() Method mDataSet.add("This is Section Header " + i); for (int e = 0; e < SIZE; i++){ mDataSet.add(i,"This is section " + e); } } } @Override public int getCount() { return mDataSet.size(); } @Override public String getItem(int position) { return mDataSet.get(position); } @Override public long getItemId(int position) { return position; } public void remove(int position) { mDataSet.remove(position); notifyDataSetChanged(); } static class ViewHolder { TextView dataTextView; ViewHolder(View view) { dataTextView = (TextView) view.findViewById(R.id.txt_data); view.setTag(this); } } @Override public View getView(int position,false)) : (ViewHolder) convertView.getTag(); viewHolder.dataTextView.setText(mDataSet.get(position)); if(mDataSet.get(position).indexof("Section Header") != -1){ // This is your sectionHeader // Customize you sectionHeader UI here // Ideally you should use object here so (object.type == 1) can determine whether this is a sectionHeader or header }else{ // This is your section // Customize you section UI here } return convertView; } } }
我希望它有所帮助.