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. 再结合“动画”和“速率” 提升用户体验即可
- Android 实现item可左右滑动移除的GridView
- 另一种可左右滑动的横向GridView的实现
- android 实现Listview左右滑动删除Item
- android实现界面左右滑动(GridView动态设置item,支持每个item按某个属性排序来显示在不同的界面)
- android实现界面左右滑动(GridView动态设置item,支持每个item按某个属性排序来显示在不同的界面)
- android实现界面左右滑动(GridView动态设置item,支持每个item按某个属性排序来显示在不同的界面)
- android 自定义view实现可左右滑动的Tabbar
- Android实现可左右滑动的选择控件
- Android使用highcharts实现可左右滑动的折线图
- GridView左右滑动的实现(一)
- 【Android实战】HorizontalScrollView实现可滑动GridView
- Android 使用Scroller实现ListView左右滑动删除Item效果
- Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果
- Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果
- Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果
- Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果
- Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果
- Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果
- linux c/c++ 后台开发之—连接池
- 对数据库中的参照完整性的一点想法:
- 1068. Find More Coins
- HBase、Redis、MongoDB、Couchbase、LevelDB主流 NoSQL 数据库的对比
- hdu 4734 F(x) (数位dp)
- Android 实现item可左右滑动移除的GridView
- 整数数组去重
- ubuntu,ros教程问题之[rospack] Error: no such package beginner_tutorials
- 无卡支付时代 银行信用卡联手京东金融欲打翻身仗
- Python Version 2.7 required which was not found in the registry
- MDT 支持 XP
- sublime text3安装插件中心
- 1069. The Black Hole of Numbers
- java参数传递内存图