Android 实现item可左右滑动移除的GridView

来源:互联网 发布:大数据能力开放平台 编辑:程序博客网 时间:2024/05/22 14:54

前言

  见如下效果图,实现了如下功能:

1. 上下滑动可翻页(GridView自带的功能)

2. 左右滑动Item,可以进行移除,且有动画效果,当移除完成后,onAnimationEnd方法会被回调

3. 点击黑色区域(子View,此处为TextView)时响应点击事件

4. 点击红色区域(父View,此处为FrameLayout)时响应点击事件


  开发期间遇到的问题:

1.事件没有传递到子view,即TextView不能响应点击事件

2.事件已传递到子view(TextView),但是父View(TaskView)不能响应点击事件

3.左右滑动item时,TaskView高概率突然接受到"ACTION_CANCEL"事件,导致item的滑动动作不受控制


  开发期间遇到的问题解决方法:

第1和第2个问题是onInterceptTouchEvent和onTouchEvent的返回值导致的,见如下TaskView类

第3个问题:左右滑动item时突然接受到"ACTION_CANCEL"事件,是由于GridView的事件干扰导致的,解决方法:在“ACTION_MOVE”事件中加入"getParent().requestDisallowInterceptTouchEvent(true)"(不能在“ACTION_DOWN"事件中调用该方法,否则会影响GridView的上下滑动翻页事件),在“ACTION_UP”和"ACTION_CANCEL"时需要requestDisallowInterceptTouchEvent(false)。


原文地址:http://blog.csdn.net/yelangjueqi/article/details/56021609

效果图



源码

一 grid_recents.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <GridView        android:id="@+id/grid_view"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginEnd="20dp"        android:layout_marginStart="20dp"        android:layout_marginTop="8dp"        android:columnWidth="120dp"        android:horizontalSpacing="20dp"        android:numColumns="2"        android:scrollbars="none"        android:stretchMode="columnWidth"        android:verticalSpacing="20dp" /></FrameLayout>

二 simple_list_item.xml

<?xml version="1.0" encoding="utf-8"?><com.example.android.TaskView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_marginBottom="10dp"    android:layout_marginTop="10dp"    android:background="#ff0000" >    <TextView        android:id="@+id/text"        android:layout_width="60dp"        android:layout_height="60dp"        android:layout_gravity="center"        android:layout_marginBottom="10dp"        android:layout_marginTop="10dp"        android:background="#000000"        android:gravity="center"        android:textColor="#00ff00" /></com.example.android.TaskView>

三 TaskView.java

package com.example.android;import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.ValueAnimator;import android.animation.ValueAnimator.AnimatorUpdateListener;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewParent;import android.view.animation.AccelerateInterpolator;import android.widget.FrameLayout;import android.widget.Toast;public class TaskView extends FrameLayout {    private String TAG = "TaskView";    private ValueAnimator mValueAnimator;    private int mPosition = -1;    private int DIRECTION_LEFT_TO_RIGHT = 0;    private int DIRECTION_RIGHT_TO_LEFT = 1;    private int mSlideDirection = -1;    private int mTaskViewDownLeft;    private int mTaskViewLeft;    private int mTaskViewTop;    private int mTaskViewRight;    private int mTaskViewBottom;    private int mDownX;    private float mLastX;    private final ViewConfiguration mConfiguration;    private float mTouchSlop;    private ViewParent mViewParent;    private int offsetDistanceThreshold;    private final float BASEVELOCOTY = 100;    private VelocityTracker mVelocityTracker;    private float mVelocityX;    private boolean eventConsumed = false;    public TaskView(Context context) {        this(context, null);    }    public TaskView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {        this(context, attrs, defStyleAttr, 0);    }    public TaskView(final Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        mConfiguration = ViewConfiguration.get(context);        mTouchSlop = mConfiguration.getScaledTouchSlop();        setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(context, "TaskView clicked", 1000).show();            }        });    }    public void updatePositionInAdapter(int position) {        mPosition = position;    }    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        boolean consumed = handleTouchEvent(event);        Log.d(TAG, "consumed =" + consumed);        return consumed ? true : super.onInterceptTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        boolean consumed = handleTouchEvent(event);        Log.d(TAG, "consumed =" + consumed);        return consumed ? true : super.onTouchEvent(event);    }    private boolean handleTouchEvent(MotionEvent event) {        int action = event.getAction();        switch (action & MotionEvent.ACTION_MASK) {        case MotionEvent.ACTION_DOWN: {            // Save the touch down info            mDownX = (int) event.getX();            mLastX = (int) event.getRawX();            mTaskViewDownLeft = getLeft();            mTaskViewLeft = mTaskViewDownLeft;            mTaskViewTop = getTop();            mTaskViewRight = getRight();            mTaskViewBottom = getBottom();            offsetDistanceThreshold = getWidth() / 3;            if (mVelocityTracker == null) {                mVelocityTracker = VelocityTracker.obtain();            } else {                mVelocityTracker.clear();            }            mVelocityTracker.addMovement(event);            break;        }        case MotionEvent.ACTION_POINTER_DOWN: {            // TODO            break;        }        case MotionEvent.ACTION_MOVE: {            if (mVelocityTracker != null) {                mVelocityTracker.addMovement(event);                mVelocityTracker.computeCurrentVelocity(1000);                mVelocityX = mVelocityTracker.getXVelocity();            }            mViewParent = getParent();            if (mViewParent != null) {                // Avoid this view occurence unexpected "ACTION_CANCEL" event                mViewParent.requestDisallowInterceptTouchEvent(true);            }            int x = (int) event.getX();            int dx = (int) Math.abs(event.getRawX() - mLastX);            int offsetX = x - mDownX;            if (Math.abs(offsetX) > mTouchSlop && (mPosition % 2) == 0 && offsetX < 0) {                Log.d(TAG, "slide to left");                mSlideDirection = DIRECTION_RIGHT_TO_LEFT;                mTaskViewLeft = getLeft() - dx;                mTaskViewRight = getRight() - dx;            } else if (Math.abs(offsetX) > mTouchSlop && (mPosition % 2) != 0 && offsetX > 0) {                Log.d(TAG, "slide to right");                mSlideDirection = DIRECTION_LEFT_TO_RIGHT;                mTaskViewLeft = getLeft() + dx;                mTaskViewRight = getRight() + dx;            }            this.layout(mTaskViewLeft, mTaskViewTop, mTaskViewRight, mTaskViewBottom);            mLastX = (int) event.getRawX();            break;        }        case MotionEvent.ACTION_POINTER_UP: {            // TODO            break;        }        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_CANCEL: {            Log.d(TAG, (action == 1) ? "ACTION_UP" : "ACTION_CANCEL");            eventConsumed = upOrcancel();            reset();            if (mViewParent != null) {                mViewParent.requestDisallowInterceptTouchEvent(false);            }            break;        }        }        return eventConsumed;    }    private boolean upOrcancel() {        boolean consumed = false;        int offsetDistance = Math.abs(getLeft() - mTaskViewDownLeft);        boolean overstep = (offsetDistance > offsetDistanceThreshold);        int viewCurrentLeft = getLeft(); // current left position        int viewEndLeft = viewCurrentLeft; // default value        boolean fling = (Math.abs(mVelocityX) > BASEVELOCOTY);        // left column        if ((mPosition % 2) == 0 && mSlideDirection == DIRECTION_RIGHT_TO_LEFT) {            // exceed "offsetDistanceThreshold" value or fling gesture            boolean shouldDismissChildView = overstep || fling;            viewEndLeft = shouldDismissChildView ? (mTaskViewDownLeft - getWidth()) : mTaskViewDownLeft;            finishSlide(viewCurrentLeft, viewEndLeft, shouldDismissChildView);            consumed = true;            // right column        } else if ((mPosition % 2) != 0 && mSlideDirection == DIRECTION_LEFT_TO_RIGHT) {            // exceed "offsetDistanceThreshold" value or fling gesture            boolean shouldDismissChildView = overstep || fling;            viewEndLeft = shouldDismissChildView ? (mTaskViewDownLeft + getWidth()) : mTaskViewDownLeft;            finishSlide(viewCurrentLeft, viewEndLeft, shouldDismissChildView);            consumed = true;        }        return consumed;    }    private void finishSlide(int startPosition, int endPosition, final boolean shouldDismissChildView) {        mValueAnimator = ValueAnimator.ofFloat(startPosition, endPosition);        mValueAnimator.setDuration(220);        mValueAnimator.setInterpolator(new AccelerateInterpolator());        mValueAnimator.addUpdateListener(new AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                float currentValue = (Float) animation.getAnimatedValue();                int left = (int) currentValue;                int right = (int) (left + getWidth());                layout(left, mTaskViewTop, right, mTaskViewBottom);            }        });        mValueAnimator.addListener(new AnimatorListenerAdapter() {            @Override            public void onAnimationEnd(Animator animation) {                if (shouldDismissChildView) {                    Log.d(TAG, "--->onAnimationEnd");                    // TODO Notify other object to update by callback                }            }        });        mValueAnimator.start();    }    private void reset() {        if (mVelocityTracker != null) {            mVelocityTracker.recycle();            mVelocityTracker = null;        }        mSlideDirection = -1;    }}

四 SwipeGridViewActivity.java

package com.example.android;import java.util.ArrayList;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.GridView;import android.widget.TextView;import android.widget.Toast;public class SwipeGridViewActivity extends Activity {    private String TAG = "SwipeGridViewActivity";    private GridView mGridView;    private ArrayList<String> mAllList = new ArrayList<String>();    private GridTaskAdapter mGridTaskAdapter;    private LayoutInflater mInflater;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.grid_recents);        mInflater = LayoutInflater.from(SwipeGridViewActivity.this);        loadData();        mGridView = (GridView) findViewById(R.id.grid_view);        mGridView.setAdapter(new GridTaskAdapter());    }    private class GridTaskAdapter extends BaseAdapter {        @Override        public int getCount() {            return mAllList.size();        }        @Override        public Object getItem(int position) {            return mAllList.get(position);        }        @Override        public long getItemId(int position) {            return position;        }        @Override        public View getView(int position, View convertView, ViewGroup parent) {            TaskView taskView = (TaskView) mInflater.inflate(R.layout.simple_list_item, null);            convertView = taskView;            TextView textView = (TextView) taskView.findViewById(R.id.text);            taskView.updatePositionInAdapter(position);            textView.setText((String) getItem(position));            textView.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    Log.d(TAG, "zzz setOnClickListener");                    Toast.makeText(SwipeGridViewActivity.this, "child textView clicked", 1000).show();                }            });            return convertView;        }    }    public Object[] loadData() {        mAllList.clear();        mAllList.add("aa");        mAllList.add("ddfa");        mAllList.add("qw");        mAllList.add("sd");        mAllList.add("fd");        mAllList.add("cf");        mAllList.add("re");        mAllList.add("aa");        mAllList.add("ddfa");        mAllList.add("qw");        mAllList.add("sd");        mAllList.add("fd");        mAllList.add("cf");        mAllList.add("re");        mAllList.add("aa");        mAllList.add("ddfa");        mAllList.add("qw");        mAllList.add("sd");        mAllList.add("fd");        mAllList.add("cf");        mAllList.add("re");        mAllList.add("aa");        mAllList.add("ddfa");        mAllList.add("qw");        mAllList.add("sd");        mAllList.add("fd");        mAllList.add("cf");        mAllList.add("re");        return mAllList.toArray();    }}

源码和录制的视频地址



View滑动实现:

1.原始“相对位置”(getLeft()等方法)

2.计算偏移量(通过滑动事件“坐标差值”计算)

3.“原始相对位置” 结合 “偏移量” 得出 “新位置”,然后根据“新位置”刷新界面即可实现滑动

4. 再结合“动画”和“速率” 提升用户体验即可


0 0
原创粉丝点击