一个简单地list侧滑菜单,自己实现不是梦
来源:互联网 发布:用友软件怎么建账 编辑:程序博客网 时间:2024/06/05 04:47
1,SwipeItemLayout这个是滑动的条目。
package com.aitsuki.swipe;import android.annotation.SuppressLint;import android.content.Context;import android.graphics.Rect;import android.support.v4.view.GravityCompat;import android.support.v4.view.ViewCompat;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.util.Log;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.FrameLayout;import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;/** * 1. 最多同时设置两个菜单 * 2. 菜单必须设置layoutGravity属性. start left end right */public class SwipeItemLayout extends FrameLayout { public static final String TAG = "SwipeItemLayout"; private ViewDragHelper mDragHelper; private int mTouchSlop; private int mVelocity; private float mDownX; private float mDownY; private boolean mIsDragged; private boolean mSwipeEnable = true; /** * 通过判断手势进行赋值 {@link #checkCanDragged(MotionEvent)} */ private View mCurrentMenu; /** * 某些情况下,不能通过mIsOpen判断当前菜单是否开启或是关闭。 * 因为在调用 {@link #open()} 或者 {@link #close()} 的时候,mIsOpen的值已经被改变,但是 * 此时ContentView还没有到达应该的位置。亦或者ContentView已经到拖拽达指定位置,但是此时并没有 * 松开手指,mIsOpen并不会重新赋值。 */ private boolean mIsOpen; /** * Menu的集合,以{@link android.view.Gravity#LEFT}和{@link android.view.Gravity#LEFT}作为key, * 菜单View作为value保存。 */ private LinkedHashMap<Integer, View> mMenus = new LinkedHashMap<>(); private List<SwipeListener> mListeners; public SwipeItemLayout(Context context) { this(context, null); } public SwipeItemLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwipeItemLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity(); mDragHelper = ViewDragHelper.create(this, new DragCallBack()); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); updateMenu(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { // 关闭菜单过程中禁止接收down事件 if (isCloseAnimating()) { return false; } // 菜单打开的时候,按下Content关闭菜单 if (mIsOpen && isTouchContent(((int) ev.getX()), ((int) ev.getY()))) { close(); return false; } } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (!mSwipeEnable) { return false; } int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mIsDragged = false; mDownX = ev.getX(); mDownY = ev.getY(); break; case MotionEvent.ACTION_MOVE: checkCanDragged(ev); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mIsDragged) { mDragHelper.processTouchEvent(ev); mIsDragged = false; } break; default: if (mIsDragged) { mDragHelper.processTouchEvent(ev); } break; } return mIsDragged || super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { if (!mSwipeEnable) { return super.onTouchEvent(ev); } int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mIsDragged = false; mDownX = ev.getX(); mDownY = ev.getY(); break; case MotionEvent.ACTION_MOVE: boolean beforeCheckDrag = mIsDragged; checkCanDragged(ev); if (mIsDragged) { mDragHelper.processTouchEvent(ev); } // 开始拖动后,发送一个cancel事件用来取消点击效果 if (!beforeCheckDrag && mIsDragged) { MotionEvent obtain = MotionEvent.obtain(ev); obtain.setAction(MotionEvent.ACTION_CANCEL); super.onTouchEvent(obtain); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mIsDragged || mIsOpen) { mDragHelper.processTouchEvent(ev); // 拖拽后手指抬起,或者已经开启菜单,不应该响应到点击事件 ev.setAction(MotionEvent.ACTION_CANCEL); mIsDragged = false; } break; default: if (mIsDragged) { mDragHelper.processTouchEvent(ev); } break; } return mIsDragged || super.onTouchEvent(ev); } /** * 判断是否可以拖拽View */ @SuppressLint("RtlHardcoded") private void checkCanDragged(MotionEvent ev) { if (mIsDragged) { return; } float dx = ev.getX() - mDownX; float dy = ev.getY() - mDownY; boolean isRightDrag = dx > mTouchSlop && dx > Math.abs(dy); boolean isLeftDrag = dx < -mTouchSlop && Math.abs(dx) > Math.abs(dy); if (mIsOpen) { // 开启状态下,点击在content上就捕获事件,点击在菜单上则判断touchSlop int downX = (int) mDownX; int downY = (int) mDownY; if (isTouchContent(downX, downY)) { mIsDragged = true; } else if (isTouchMenu(downX, downY)) { mIsDragged = (isLeftMenu() && isLeftDrag) || (isRightMenu() && isRightDrag); } } else { // 关闭状态,获取当前即将要开启的菜单。 if (isRightDrag) { mCurrentMenu = mMenus.get(Gravity.LEFT); mIsDragged = mCurrentMenu != null; } else if (isLeftDrag) { mCurrentMenu = mMenus.get(Gravity.RIGHT); mIsDragged = mCurrentMenu != null; } } if (mIsDragged) { // 开始拖动后,分发down事件给DragHelper,并且发送一个cancel取消点击事件 MotionEvent obtain = MotionEvent.obtain(ev); obtain.setAction(MotionEvent.ACTION_DOWN); mDragHelper.processTouchEvent(obtain); if (getParent() != null) { // 解决和父控件的滑动冲突。 getParent().requestDisallowInterceptTouchEvent(true); } } } // 最后一个是内容,倒数第1第2个设置了layout_gravity = right or left的是菜单,其余的忽略 @SuppressLint("RtlHardcoded") @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { super.addView(child, index, params); LayoutParams lp = (LayoutParams) child.getLayoutParams(); int gravity = GravityCompat.getAbsoluteGravity(lp.gravity, ViewCompat.getLayoutDirection(child)); switch (gravity) { case Gravity.RIGHT: mMenus.put(Gravity.RIGHT, child); break; case Gravity.LEFT: mMenus.put(Gravity.LEFT, child); break; default: break; } } public void setSwipeEnable(boolean enable) { mSwipeEnable = enable; } /** * 获取ContentView,最上层显示的View即为ContentView */ public View getContentView() { return getChildAt(getChildCount() - 1); } /** * 判断down是否点击在Content上 */ public boolean isTouchContent(int x, int y) { View contentView = getContentView(); if (contentView == null) { return false; } Rect rect = new Rect(); contentView.getHitRect(rect); return rect.contains(x, y); } @SuppressLint("RtlHardcoded") private boolean isLeftMenu() { return mCurrentMenu != null && mCurrentMenu == mMenus.get(Gravity.LEFT); } @SuppressLint("RtlHardcoded") private boolean isRightMenu() { return mCurrentMenu != null && mCurrentMenu == mMenus.get(Gravity.RIGHT); } public boolean isTouchMenu(int x, int y) { if (mCurrentMenu == null) { return false; } Rect rect = new Rect(); mCurrentMenu.getHitRect(rect); return rect.contains(x, y); } /** * 关闭菜单 */ public void close() { if (mCurrentMenu == null) { mIsOpen = false; return; } mDragHelper.smoothSlideViewTo(getContentView(), getPaddingLeft(), getPaddingTop()); mIsOpen = false; if (mListeners != null) { int listenerCount = mListeners.size(); for (int i = listenerCount - 1; i >= 0; i--) { mListeners.get(i).onSwipeClose(this); } } invalidate(); } /** * 开启菜单 */ public void open() { if (mCurrentMenu == null) { mIsOpen = false; return; } if (isLeftMenu()) { mDragHelper.smoothSlideViewTo(getContentView(), mCurrentMenu.getWidth(), getPaddingTop()); } else if (isRightMenu()) { mDragHelper.smoothSlideViewTo(getContentView(), -mCurrentMenu.getWidth(), getPaddingTop()); } mIsOpen = true; if (mListeners != null) { int listenerCount = mListeners.size(); for (int i = listenerCount - 1; i >= 0; i--) { mListeners.get(i).onSwipeOpen(this); } } invalidate(); } /** * 菜单是否开始拖动 */ public boolean isOpen() { return mIsOpen; } /** * 是否正在做开启动画 */ private boolean isOpenAnimating() { if (mCurrentMenu != null) { int contentLeft = getContentView().getLeft(); int menuWidth = mCurrentMenu.getWidth(); if (mIsOpen && ((isLeftMenu() && contentLeft < menuWidth) || (isRightMenu() && -contentLeft < menuWidth))) { return true; } } return false; } /** * 是否正在做关闭动画 */ private boolean isCloseAnimating() { if (mCurrentMenu != null) { int contentLeft = getContentView().getLeft(); if (!mIsOpen && ((isLeftMenu() && contentLeft > 0) || (isRightMenu() && contentLeft < 0))) { return true; } } return false; } /** * 当菜单被ContentView遮住的时候,要设置菜单为Invisible,防止已隐藏的菜单接收到点击事件。 */ private void updateMenu() { // FIXME: 2017/4/1 在某些手机上,设置setVisibility会导致dragHelper失效,先用Clickable属性代替 View contentView = getContentView(); if (contentView != null) { int contentLeft = contentView.getLeft(); if (contentLeft == 0) { for (View view : mMenus.values()) { if (view.isClickable()) { view.setClickable(false); } } } else { if (mCurrentMenu != null && !mCurrentMenu.isClickable()) { mCurrentMenu.setClickable(true); } } } } /** * 添加一个监听器用于监听SwipeItemLayout的开启和关闭 * * @param listener SwipeListener */ public void addSwipeListener(SwipeListener listener) { if (listener == null) { return; } if (mListeners == null) { mListeners = new ArrayList<>(); } mListeners.add(listener); } /** * 移除监听器 */ public void removeSwipeListener(SwipeListener listener) { if (listener == null) { return; } if (mListeners == null) { return; } mListeners.remove(listener); } @Override public void computeScroll() { super.computeScroll(); if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } private class DragCallBack extends ViewDragHelper.Callback { @Override public boolean tryCaptureView(View child, int pointerId) { // menu和content都可以抓取,因为在menu的宽度为MatchParent的时候,是无法点击到content的 return child == getContentView() || mMenus.containsValue(child); } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { // 如果child是内容, 那么可以左划或右划,开启或关闭菜单 if (child == getContentView()) { if (isRightMenu()) { return left > 0 ? 0 : left < -mCurrentMenu.getWidth() ? -mCurrentMenu.getWidth() : left; } else if (isLeftMenu()) { return left > mCurrentMenu.getWidth() ? mCurrentMenu.getWidth() : left < 0 ? 0 : left; } } // 如果抓取到的child是菜单,那么不移动child,而是移动contentView else if (isRightMenu()) { View contentView = getContentView(); int newLeft = contentView.getLeft() + dx; if (newLeft > 0) { newLeft = 0; } else if (newLeft < -child.getWidth()) { newLeft = -child.getWidth(); } contentView.layout(newLeft, contentView.getTop(), newLeft + contentView.getWidth(), contentView.getBottom()); return child.getLeft(); } else if (isLeftMenu()) { View contentView = getContentView(); int newLeft = contentView.getLeft() + dx; if (newLeft < 0) { newLeft = 0; } else if (newLeft > child.getWidth()) { newLeft = child.getWidth(); } contentView.layout(newLeft, contentView.getTop(), newLeft + contentView.getWidth(), contentView.getBottom()); return child.getLeft(); } return 0; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); updateMenu(); } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { Log.e(TAG, "onViewReleased: " + xvel + " ,releasedChild = " + releasedChild); if (isLeftMenu()) { if (xvel > mVelocity) { open(); } else if (xvel < -mVelocity) { close(); } else { if (getContentView().getLeft() > mCurrentMenu.getWidth() / 3 * 2) { open(); } else { close(); } } } else if (isRightMenu()) { if (xvel < -mVelocity) { open(); } else if (xvel > mVelocity) { close(); } else { if (getContentView().getLeft() < -mCurrentMenu.getWidth() / 3 * 2) { open(); } else { close(); } } } } } public interface SwipeListener { void onSwipeOpen(SwipeItemLayout view); void onSwipeClose(SwipeItemLayout view); }}2,SwipeMenuRecyclerView
package com.aitsuki.swipe;import android.content.Context;import android.graphics.Rect;import android.support.annotation.Nullable;import android.support.v7.widget.RecyclerView;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;/** * 仿IOS message列表,QQ好友列表的交互体验 * 当有菜单打开的时候,只要不是点击在菜单上,关闭该菜单。 * 只能同时打开一个菜单,防止多点触控打开菜单 */public class SwipeMenuRecyclerView extends RecyclerView { public SwipeMenuRecyclerView(Context context) { super(context); } public SwipeMenuRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public SwipeMenuRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getActionMasked(); // 手指按下的时候,如果有开启的菜单,只要手指不是落在该Item上,则关闭菜单, 并且不分发事件。 if (action == MotionEvent.ACTION_DOWN) { int x = (int) ev.getX(); int y = (int) ev.getY(); View openItem = findOpenItem(); if (openItem != null && openItem != getTouchItem(x, y)) { SwipeItemLayout swipeItemLayout = findSwipeItemLayout(openItem); if (swipeItemLayout != null) { swipeItemLayout.close(); return false; } } } else if (action == MotionEvent.ACTION_POINTER_DOWN) { // FIXME: 2017/3/22 不知道怎么解决多点触控导致可以同时打开多个菜单的bug,先暂时禁止多点触控 return false; } return super.dispatchTouchEvent(ev); } /** * 获取按下位置的Item */ @Nullable private View getTouchItem(int x, int y) { Rect frame = new Rect(); for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child.getVisibility() == VISIBLE) { child.getHitRect(frame); if (frame.contains(x, y)) { return child; } } } return null; } /** * 找到当前屏幕中开启的的Item */ @Nullable private View findOpenItem() { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { SwipeItemLayout swipeItemLayout = findSwipeItemLayout(getChildAt(i)); if (swipeItemLayout != null && swipeItemLayout.isOpen()) { return getChildAt(i); } } return null; } /** * 获取该View */ @Nullable private SwipeItemLayout findSwipeItemLayout(View view) { if (view instanceof SwipeItemLayout) { return (SwipeItemLayout) view; } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; int count = group.getChildCount(); for (int i = 0; i < count; i++) { SwipeItemLayout swipeLayout = findSwipeItemLayout(group.getChildAt(i)); if (swipeLayout != null) { return swipeLayout; } } } return null; }}
3,上边两个就已经实现了,接下来是使用,跟平时的recycleview类似的用法。
package com.aitsuki.swipedemo;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import android.widget.Toast;import com.aitsuki.swipe.SwipeItemLayout;import com.aitsuki.swipedemo.entity.Data;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); DemoAdapter adapter = new DemoAdapter(getData(), mItemTouchListener); recyclerView.setAdapter(adapter); } private List<Data> getData() { List<Data> mData = new ArrayList<>(); for (int i = 0; i < 50; i++) { Data data = new Data("item","left","right"); mData.add(data); } return mData; } ItemTouchListener mItemTouchListener = new ItemTouchListener() { @Override public void onItemClick(String str) { Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show(); } @Override public void onLeftMenuClick(String str) { Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show(); } @Override public void onRightMenuClick(String str) { Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show(); } }; interface ItemTouchListener { void onItemClick(String str); void onLeftMenuClick(String str); void onRightMenuClick(String str); } private static class DemoAdapter extends RecyclerView.Adapter<SimpleViewHolder> { private ItemTouchListener mItemTouchListener; private List<Data> mData; DemoAdapter(List<Data> data, ItemTouchListener itemTouchListener) { this.mData = data; this.mItemTouchListener = itemTouchListener; } @Override public int getItemCount() { return mData.size(); } @Override public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_left_and_right_menu, parent, false); return new SimpleViewHolder(rootView); } @Override public void onBindViewHolder(final SimpleViewHolder holder, int position) { holder.mContent.setText(mData.get(position).content.concat(" " + position)); if (mItemTouchListener != null) { holder.itemView.setOnClickListener(v -> mItemTouchListener.onItemClick(holder.mContent.getText().toString())); if (holder.mLeftMenu != null) { holder.mLeftMenu.setOnClickListener(v -> { mItemTouchListener.onLeftMenuClick("left " + holder.getAdapterPosition()); holder.mSwipeItemLayout.close(); }); } if (holder.mRightMenu != null) { holder.mRightMenu.setOnClickListener(v -> { mItemTouchListener.onRightMenuClick("right " + holder.getAdapterPosition()); holder.mSwipeItemLayout.close(); }); } } } } private static class SimpleViewHolder extends RecyclerView.ViewHolder { private final View mLeftMenu; private final View mRightMenu; private final TextView mContent; private final SwipeItemLayout mSwipeItemLayout; SimpleViewHolder(View itemView) { super(itemView); mSwipeItemLayout = (SwipeItemLayout) itemView.findViewById(R.id.swipe_layout); mContent = (TextView) itemView.findViewById(R.id.tv_content); mLeftMenu = itemView.findViewById(R.id.left_menu); mRightMenu = itemView.findViewById(R.id.right_menu); } }}
一些其他的东西。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.aitsuki.swipedemo.MainActivity"> <com.aitsuki.swipe.SwipeMenuRecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"> </com.aitsuki.swipe.SwipeMenuRecyclerView></LinearLayout>
<com.aitsuki.swipe.SwipeItemLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swipe_layout" android:layout_width="match_parent" android:layout_height="@dimen/swipe_item_height"> <TextView android:id="@+id/left_menu" android:layout_width="@dimen/swipe_item_menu_width" android:layout_height="match_parent" android:layout_gravity="left" android:background="@color/red500" android:gravity="center" android:text="left" android:textColor="@color/white"/> <TextView android:id="@+id/right_menu" android:layout_width="@dimen/swipe_item_menu_width" android:layout_height="match_parent" android:layout_gravity="right" android:background="@color/blue500" android:gravity="center" android:text="right" android:textColor="@color/white"/> <include layout="@layout/swipe_content"/></com.aitsuki.swipe.SwipeItemLayout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="@dimen/swipe_item_height" android:background="?android:colorBackground" android:foreground="?listChoiceBackgroundIndicator"> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textColor="@color/primaryText" tools:text="Content"/></FrameLayout>
public class Data { public String content; public String left; public String right; public Data(String content, String left, String right) { this.content = content; this.left = left; this.right = right; }}
结束。。。
阅读全文
0 0
- 一个简单地list侧滑菜单,自己实现不是梦
- C++自己实现一个简单地Vector
- 仿QQ5.0侧滑菜单——抽屉式菜单(简单地实现)
- 一个很好地List实现源码
- C++中,数组名不是一个简单地指针常量
- 简单实现侧滑菜单
- 如何实现一个简单地Filter
- 一个简单地Shell-like 的实现
- 自己实现android侧滑菜单
- 关于Scroller的使用以及自己实现一个侧滑菜单
- 关于Scroller的使用以及自己实现一个侧滑菜单
- 一个简单的List实现
- 自己实现一个简单数据库
- 一个简单的侧滑菜单SimpleSlidingMenu
- 自己实现的简单List和Map
- 自己实现的一个list双链表
- android 侧滑菜单的简单实现
- qq侧滑菜单的简单实现
- 查看和删除进程
- gradle配置签名文件
- bootstrap datepicker 与modal使用 datepicker属性 方法 事件
- 使用java连接MySql,将所有数据转换成Json
- 共享行业的分布式MQTT设计
- 一个简单地list侧滑菜单,自己实现不是梦
- win10 掿建python spark开发环境.和安装hadoop环境
- 关于客户端通过证书获取https接口数据
- S3c2440 Nand/Nor Falsh区别
- 获取一个地点的经纬度
- 40个Java多线程问题总结
- 11 《痛苦与狂喜:米开朗基罗传》-豆瓣评分8.9
- SpringBoot入门系列:第一篇 Hello World(转载)
- 搬瓦工VPS(bandwagonhost):洛杉矶quadranet机房 VPS 测评