PHP防QQ列表右划,RecyclerView仿QQ消息列表左滑弹出菜单,标记已读和删除
发布日期:2022-02-18 13:19:52 浏览次数:8 分类:技术文章

本文共 12796 字,大约阅读时间需要 42 分钟。

仿QQ消息列表左滑弹出菜单,标记已读和删除

话不多说,看到这样的效果心动了么?

73ba0bf09d34

这里写图片描述

1.先上build .gradle,dependencies里面要这样写,其实就是导入v7里面的recyclerView。要用butterknife的话, 记得加到这里来:

dependencies {

compile fileTree(include: ['*.jar'], dir: 'libs')

testCompile 'junit:junit:4.12'

compile 'com.android.support:support-v4:23.4.0'

compile 'com.android.support:recyclerview-v7:23.2.0'

//butterknife

compile 'com.jakewharton:butterknife:6.1.0'

}

2.再来列表的item, item_msg_remind.xml:

消息主体,width记得match_parent,直接把后面两个布局顶出去

//省略布局

android:id="@+id/ll_msg_remind_main">

//省略布局

android:id="@+id/msg_remind_point" />

//省略布局

android:orientation="vertical">

//省略布局

android:id="@+id/tv_remind_title"

android:layout_weight="1"

android:text="隔壁的二蛋" />

//省略布局

android:id="@+id/tv_remind_content"

android:layout_weight="2"

android:ellipsize="end"

android:maxLines="2"

android:text="对方撤回了一条消息并砍了你的狗" />

//省略布局>

//省略布局

android:id="@+id/tv_msg_remind_check"

android:text="标记已读" />

//省略布局>

//省略布局

android:id="@+id/tv_msg_remind_delete"

android:text="删除"/>

3.那什么,Adapter来了,MsgRemindAdapter:

消息adapter

-->特别注意extends后面Adapter<>里面要写自己定义的ViewHolder

public class MsgRemindAdapter extends RecyclerView.Adapter

implements ItemSlideHelper.Callback {

private Context context;

private List mDatas = new ArrayList();

private RecyclerView mRecyclerView;

public MsgRemindAdapter(Context context, List mDatas) {

this.context = context;

this.mDatas = mDatas;

}

onCreateViewHolder

@Override

public RemindViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View view = LayoutInflater.from(parent.getContext()).

inflate(R.layout.item_msg_remind, parent, false);

return new RemindViewHolder(view);

}

//将recyclerView绑定Slide事件

@Override

public void onAttachedToRecyclerView(RecyclerView recyclerView) {

super.onAttachedToRecyclerView(recyclerView);

mRecyclerView = recyclerView;

mRecyclerView.addOnItemTouchListener(new ItemSlideHelper(mRecyclerView.getContext(), this));

}

onBindViewHolder(),绑定

@Override

public void onBindViewHolder(final RemindViewHolder holder, final int position) {

/**

* 消息状态

*/

if (mDatas.get(position).isChecked()) {

holder.msgRemindPoint.setBackgroundResource(R.drawable.shape_remind_point_gray);

} else {

holder.msgRemindPoint.setBackgroundResource(R.drawable.shape_remind_point_theme);

}

//消息标题

holder.tvRemindTitle.setText(mDatas.get(position).getTitle());

//消息内容

holder.tvRemindContent.setText(mDatas.get(position).getContent());

-->特别注意,敲黑板了啊!!!在执行notify的时候,取position要取holder.getAdapterPosition(),消息被删除之后,他原来的position是final的,所以取到的值不准确,会报数组越界。

消息主体监听,这里我是让他添加一条数据,替换成你需要的操作即可

holder.llMsgRemindMain.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

addData(mDatas.size());

}

});

标记已读监听

holder.tvMsgRemindCheck.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

mDatas.get(holder.getAdapterPosition()).setChecked(true);

notifyItemChanged(holder.getAdapterPosition());

}

});

删除消息监听

holder.tvMsgRemindDelete.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

removeData(holder.getAdapterPosition());

}

});

此方法用来计算水平方向移动的距离

@Override

public int getHorizontalRange(RecyclerView.ViewHolder holder) {

if (holder.itemView instanceof LinearLayout) {

ViewGroup viewGroup = (ViewGroup) holder.itemView;

//viewGroup包含3个控件,即消息主item、标记已读、删除,返回为标记已读宽度+删除宽度

return viewGroup.getChildAt(1).getLayoutParams().width

+ viewGroup.getChildAt(2).getLayoutParams().width;

}

return 0;

}

@Override

public RecyclerView.ViewHolder getChildViewHolder(View childView) {

return mRecyclerView.getChildViewHolder(childView);

}

@Override

public View findTargetView(float x, float y) {

return mRecyclerView.findChildViewUnder(x, y);

}

自定义的ViewHolder

public class RemindViewHolder extends RecyclerView.ViewHolder {

@InjectView(R.id.msg_remind_point)

View msgRemindPoint;

@InjectView(R.id.tv_remind_title)

TextView tvRemindTitle;

@InjectView(R.id.tv_remind_content)

TextView tvRemindContent;

@InjectView(R.id.ll_msg_remind_main)

LinearLayout llMsgRemindMain;

@InjectView(R.id.tv_msg_remind_check)

TextView tvMsgRemindCheck;

@InjectView(R.id.tv_msg_remind_delete)

TextView tvMsgRemindDelete;

public RemindViewHolder(View itemView) {

super(itemView);

ButterKnife.inject(this, itemView);

}

}

添加单条数据

public void addData(int position) {

MsgVo vo = new MsgVo();

if (position % 2 == 1) {

vo.setChecked(false);

vo.setTitle("隔壁的二蛋");

vo.setContent("对方撤回了一条消息并砍了你的狗,问你服不服。");

} else {

vo.setChecked(false);

vo.setTitle("对面的三娃");

vo.setContent("今天晚上开黑,4缺1,来不来?");

}

mDatas.add(position, vo);

notifyItemInserted(position);

}

删除单条数据

public void removeData(int position) {

mDatas.remove(position);

notifyItemRemoved(position);

}

}

4.要做这么高难度的滑动,是要一个ItemSlideHelper的:

/**

* 消息列表左滑菜单帮助类

*/

public class ItemSlideHelper implements RecyclerView.OnItemTouchListener, GestureDetector.OnGestureListener {

private final int DEFAULT_DURATION = 200;

private View mTargetView;

private int mActivePointerId;

private int mTouchSlop;

private int mMaxVelocity;

private int mMinVelocity;

private int mLastX;

private int mLastY;

private boolean mIsDragging;

private Animator mExpandAndCollapseAnim;

private GestureDetectorCompat mGestureDetector;

private Callback mCallback;

public ItemSlideHelper(Context context, Callback callback) {

this.mCallback = callback;

//手势用于处理fling

mGestureDetector = new GestureDetectorCompat(context, this);

ViewConfiguration configuration = ViewConfiguration.get(context);

mTouchSlop = configuration.getScaledTouchSlop();

mMaxVelocity = configuration.getScaledMaximumFlingVelocity();

mMinVelocity = configuration.getScaledMinimumFlingVelocity();

}

@Override

public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

int action = MotionEventCompat.getActionMasked(e);

int x = (int) e.getX();

int y = (int) e.getY();

//如果RecyclerView滚动状态不是空闲targetView不是空

if (rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {

if (mTargetView != null) {

//隐藏已经打开

smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2);

mTargetView = null;

}

return false;

}

//如果正在运行动画 ,直接拦截什么都不做

if (mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning()) {

return true;

}

boolean needIntercept = false;

switch (action) {

case MotionEvent.ACTION_DOWN:

mActivePointerId = MotionEventCompat.getPointerId(e, 0);

mLastX = (int) e.getX();

mLastY = (int) e.getY();

/*

* 如果之前有一个已经打开的项目,当此次点击事件没有发生在右侧的菜单中则返回TRUE,

* 如果点击的是右侧菜单那么返回FALSE这样做的原因是因为菜单需要响应Onclick

* */

if (mTargetView != null) {

return !inView(x, y);

}

//查找需要显示菜单的view;

mTargetView = mCallback.findTargetView(x, y);

break;

case MotionEvent.ACTION_MOVE:

int deltaX = (x - mLastX);

int deltaY = (y - mLastY);

if (Math.abs(deltaY) > Math.abs(deltaX))

return false;

//如果移动距离达到要求,则拦截

needIntercept = mIsDragging = mTargetView != null && Math.abs(deltaX) >= mTouchSlop;

break;

case MotionEvent.ACTION_CANCEL:

case MotionEvent.ACTION_UP:

/*

* 走这是因为没有发生过拦截事件

* */

if (isExpanded()) {

if (inView(x, y)) {

// 如果走这那行这个ACTION_UP的事件会发生在右侧的菜单中

} else {

//拦截事件,防止targetView执行onClick事件

needIntercept = true;

}

//折叠菜单

smoothHorizontalExpandOrCollapse(DEFAULT_DURATION / 2);

}

mTargetView = null;

break;

}

return needIntercept;

}

private boolean isExpanded() {

return mTargetView != null && mTargetView.getScrollX() == getHorizontalRange();

}

private boolean isCollapsed() {

return mTargetView != null && mTargetView.getScrollX() == 0;

}

/*

* 根据targetView的scrollX计算出targetView的偏移,这样能够知道这个point

* 是在右侧的菜单中

* */

private boolean inView(int x, int y) {

if (mTargetView == null)

return false;

int scrollX = mTargetView.getScrollX();

int left = mTargetView.getWidth() - scrollX;

int top = mTargetView.getTop();

int right = left + getHorizontalRange();

int bottom = mTargetView.getBottom();

Rect rect = new Rect(left, top, right, bottom);

return rect.contains(x, y);

}

@Override

public void onTouchEvent(RecyclerView rv, MotionEvent e) {

if (mExpandAndCollapseAnim != null && mExpandAndCollapseAnim.isRunning() || mTargetView == null)

return;

//如果要响应fling事件设置将mIsDragging设为false

if (mGestureDetector.onTouchEvent(e)) {

mIsDragging = false;

return;

}

int x = (int) e.getX();

int y = (int) e.getY();

int action = MotionEventCompat.getActionMasked(e);

switch (action) {

case MotionEvent.ACTION_DOWN:

//RecyclerView 不会转发这个Down事件

break;

case MotionEvent.ACTION_MOVE:

int deltaX = (int) (mLastX - e.getX());

if (mIsDragging) {

horizontalDrag(deltaX);

}

mLastX = x;

break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

if (mIsDragging) {

if (!smoothHorizontalExpandOrCollapse(0) && isCollapsed())

mTargetView = null;

mIsDragging = false;

}

break;

}

}

@Override

public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

}

/**

* 根据touch事件来滚动View的scrollX

*

* @param delta

*/

private void horizontalDrag(int delta) {

int scrollX = mTargetView.getScrollX();

int scrollY = mTargetView.getScrollY();

if ((scrollX + delta) <= 0) {

mTargetView.scrollTo(0, scrollY);

return;

}

int horRange = getHorizontalRange();

scrollX += delta;

if (Math.abs(scrollX) < horRange) {

mTargetView.scrollTo(scrollX, scrollY);

} else {

mTargetView.scrollTo(horRange, scrollY);

}

}

/**

* 根据当前scrollX的位置判断是展开还是折叠

*

* @param velocityX 如果不等于0那么这是一次fling事件,否则是一次ACTION_UP或者ACTION_CANCEL

*/

private boolean smoothHorizontalExpandOrCollapse(float velocityX) {

int scrollX = mTargetView.getScrollX();

int scrollRange = getHorizontalRange();

if (mExpandAndCollapseAnim != null)

return false;

int to = 0;

int duration = DEFAULT_DURATION;

if (velocityX == 0) {

//如果已经展一半,平滑展开

if (scrollX > scrollRange / 2) {

to = scrollRange;

}

} else {

if (velocityX > 0)

to = 0;

else

to = scrollRange;

duration = (int) ((1.f - Math.abs(velocityX) / mMaxVelocity) * DEFAULT_DURATION);

}

if (to == scrollX)

return false;

mExpandAndCollapseAnim = ObjectAnimator.ofInt(mTargetView, "scrollX", to);

mExpandAndCollapseAnim.setDuration(duration);

mExpandAndCollapseAnim.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

}

@Override

public void onAnimationEnd(Animator animation) {

mExpandAndCollapseAnim = null;

if (isCollapsed())

mTargetView = null;

}

@Override

public void onAnimationCancel(Animator animation) {

//onAnimationEnd(animation);

mExpandAndCollapseAnim = null;

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

mExpandAndCollapseAnim.start();

return true;

}

public int getHorizontalRange() {

RecyclerView.ViewHolder viewHolder = mCallback.getChildViewHolder(mTargetView);

return mCallback.getHorizontalRange(viewHolder);

}

@Override

public boolean onDown(MotionEvent e) {

return false;

}

@Override

public void onShowPress(MotionEvent e) {

}

@Override

public boolean onSingleTapUp(MotionEvent e) {

return false;

}

@Override

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

return false;

}

@Override

public void onLongPress(MotionEvent e) {

}

@Override

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

if (Math.abs(velocityX) > mMinVelocity && Math.abs(velocityX) < mMaxVelocity) {

if (!smoothHorizontalExpandOrCollapse(velocityX)) {

if (isCollapsed())

mTargetView = null;

return true;

}

}

return false;

}

/**

* 左滑菜单Callback

*/

public interface Callback {

int getHorizontalRange(RecyclerView.ViewHolder holder);

RecyclerView.ViewHolder getChildViewHolder(View childView);

View findTargetView(float x, float y);

}

}

5.最后来Activity,布局文件中添加:

android:id="@+id/rv_msg_remind"

android:layout_width="match_parent"

android:layout_height="match_parent" />

MsgRemindActivity:

/**

* 消息主界面

*/

public class MsgRemindActivity extends Activity {

@InjectView(R.id.rv_msg_remind)

RecyclerView rvMsgRemind;

private MsgRemindAdapter msgRemindAdapter;

private List mDatas;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_msg_remind);

ButterKnife.inject(this);

initData();

//设置布局类型为listview的LinearLayoutManager

rvMsgRemind.setLayoutManager(new LinearLayoutManager(this));

rvMsgRemind.addItemDecoration(new RecyclerViewListDecoration(this,

RecyclerViewListDecoration.VERTICAL_LIST));

//固定recyclerview大小

rvMsgRemind.setHasFixedSize(true);

rvMsgRemind.setAdapter(msgRemindAdapter);

}

private void initData() {

mDatas = new ArrayList();

for (int i = 'A'; i < 'G'; i++) {

MsgVo vo = new MsgVo();

if (i % 2 == 1) {

vo.setChecked(true);

vo.setTitle("原始消息,已读状态" + (char) i);

vo.setContent("乌啦啦啦啦啦啦啦啦啦");

} else {

vo.setChecked(false);

vo.setTitle("原始消息,未读状态");

vo.setContent("唔噜噜噜噜噜噜噜噜噜噜");

}

mDatas.add(vo);

}

msgRemindAdapter = new MsgRemindAdapter(this, mDatas);

}

}

6.以上代码中的

rvMsgRemind.addItemDecoration(new RecyclerViewListDecoration(this,

RecyclerViewListDecoration.VERTICAL_LIST));

是设置recyclerView的分割线,跟ListView有点点不一样哈。

好了,就这样了吧。

特别感谢鸿洋老司机提供了学习RecyclerView 的车牌:

特别感谢Android小先森提供的思路:

demo地址放出:

转载地址:https://blog.csdn.net/weixin_28926205/article/details/116010482 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:车牌识别的matlab程序(程序_讲解_模板),车牌识别的matlab程序(程序-讲解-模板)资料...
下一篇:matlab simple,[求助]Matlab2016b里没有simple函数

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月11日 07时56分40秒