SwipeCardView有点类似于stackview的控件

2024-06-22 12:48

本文主要是介绍SwipeCardView有点类似于stackview的控件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

业余时间写了一个类似stackview的控件,可以循环抽取.还不是很完善,算是给有需要的朋友提供个基本思路吧.有更好的建议请告知.

github地址:https://github.com/X-FAN/SwipeCardView

先上效果图

这里写图片描述

源码作了简单注释

public class SwipeCardView extends ViewGroup {private int mInitX = 0;//最顶层view相对父view左上角x坐标private int mOffSet = 50;private int mRecordCount = 0;private int mRealOffset = 0;private int mDuration;private float mScale = 0.05f;private boolean mReLayout = false;//是否再次重新布局private BindData mBindData;private LayoutInflater mInflater;private View mTopView;//最顶上的Viewprivate View mRemovedView;private OnTopClickListener mOnTopClickListener;public SwipeCardView(Context context) {this(context, null);}public SwipeCardView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SwipeCardView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeCardView, defStyleAttr, 0);mRealOffset = a.getDimensionPixelSize(R.styleable.SwipeCardView_offset, 20);mDuration = a.getInteger(R.styleable.SwipeCardView_animatorDuration, 500);mScale = a.getFloat(R.styleable.SwipeCardView_scale, 0.05f);a.recycle();mInflater = LayoutInflater.from(context);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int width;int height;int defaultWidth = 200;if (widthMode == MeasureSpec.EXACTLY) {width = widthSize;} else if (widthMode == MeasureSpec.AT_MOST) {width = Math.min(defaultWidth, widthSize);} else {width = defaultWidth;}int defaultHeight = 200;if (heightMode == MeasureSpec.EXACTLY) {height = heightSize;} else if (heightMode == MeasureSpec.AT_MOST) {height = Math.min(defaultHeight, heightSize);} else {height = defaultHeight;}setMeasuredDimension(width, height);int count = getChildCount();for (int i = 0; i < count; i++) {//测量子viewView child = getChildAt(i);if (child.getVisibility() != GONE) {measureChild(child, widthMeasureSpec, heightMeasureSpec);}}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {layoutChildren(l, t, r, b);}private void layoutChildren(int left, final int top, int right, int bottom) {if (mReLayout) {int count = getChildCount();for (int i = 0; i < count; i++) {View view = getChildAt(i);scaleUpChildView(view, count);}mReLayout = false;resetBottomView(count);addView(mRemovedView, 0);//将删除的View重新添加到最底端mRemovedView = null;} else {int count = getChildCount();mTopView = getChildAt(count - 1);mTopView.setTag(true);//开始默认可以滑动int width = mTopView.getMeasuredWidth();int height = mTopView.getMeasuredHeight();mOffSet = (int) (width * mScale / 2 + mRealOffset);//需要向左移动距离float totalWidth = width + mRealOffset * (count - 1);//整个子view加起来所占的宽度mInitX = (int) (getMeasuredWidth() - totalWidth) / 2;int initY = (getMeasuredHeight() - height) / 2;for (int i = 0; i < count; i++) {View view = getChildAt(i);view.layout(mInitX, initY, width + mInitX, height + initY);scaleChildView(view, count - 1 - i);}}setTopView();}/*** 配置顶层view*/private void setTopView() {mTopView = getChildAt(getChildCount() - 1);//获取最上层的Viewif (mTopView != null) {mTopView.setOnTouchListener(new SwipeCardListener(mTopView, mInitX) {@Overridevoid leftOut(View view) {mRemovedView = view;mReLayout = true;removeView(view);}@Overridevoid onClick(View view) {if (mOnTopClickListener != null) {mOnTopClickListener.onTopClickListener(view);}}});}}/*** 给最底层的View重新配置合适的值** @param count*/private void resetBottomView(int count) {mRemovedView.setX(mInitX);mRemovedView.offsetLeftAndRight(count * mOffSet);mRemovedView.setScaleX(1 - count * mScale);mRemovedView.setScaleY(1 - count * mScale);}/*** 初始化SwipeCard** @param layoutId* @param datas* @param <T>*/public <T> void initSwipeCard(@LayoutRes int layoutId, List<T> datas) {int count = datas.size();for (int i = 0; i < count; i++) {//添加view并绑定数据View view = mInflater.inflate(layoutId, this, false);mBindData.bindData(view, datas.get(i));addView(view, 0);//添加到最低端}}public interface BindData<T> {void bindData(View view, T data);}/*** 缩放并平移子view*/private void scaleChildView(View view, int index) {view.offsetLeftAndRight(mOffSet * index);view.setScaleX(1 - index * mScale);view.setScaleY(1 - index * mScale);}/*** 慢慢放到上层view的位置** @param view*/private void scaleUpChildView(final View view, final int count) {float scaleX = view.getScaleX();float scaleY = view.getScaleY();view.animate().scaleX(scaleX + mScale).scaleY(scaleY + mScale).x(view.getX() - mOffSet).setDuration(mDuration).setListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {mRecordCount++;if (count == mRecordCount) {mTopView.setTag(true);//设置为可以滑动mRecordCount = 0;}}}).start();}public <T> void setBindDataListener(BindData bindData) {mBindData = bindData;}public void setOnTopClickListener(OnTopClickListener onTopClickListener) {mOnTopClickListener = onTopClickListener;}public interface OnTopClickListener {void onTopClickListener(View view);}}
abstract class SwipeCardListener implements View.OnTouchListener {private final float mOutDistance;//定义滑动多少距离后,触发view从界面左面离开动作private int mWidth;//view的宽度private float mInitX;//view初始的x坐标private float mTouchDownX;//按下时的手指x坐标private float mRecordX;//记录移动后view的x坐标private View mView;private GestureDetectorCompat mGestureDetector;SwipeCardListener(View view, int initX) {mView = view;mInitX = initX;mWidth = view.getWidth();mOutDistance = mWidth / 4;mGestureDetector = new GestureDetectorCompat(view.getContext(), new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onSingleTapUp(MotionEvent e) {onClick(mView);return super.onSingleTapUp(e);}});}@Overridepublic boolean onTouch(final View v, MotionEvent event) {mGestureDetector.onTouchEvent(event);if (mView.getTag() == null || !(boolean) mView.getTag()) {return false;}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mTouchDownX = event.getRawX();return true;case MotionEvent.ACTION_MOVE:float d = event.getRawX() - mTouchDownX;if (Math.abs(d) > 0) {mTouchDownX = event.getRawX();mRecordX += d;mView.setX(mInitX + mRecordX);//移动View}break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:if (mRecordX < 0 && Math.abs(mRecordX) > mOutDistance) {mView.animate().x(-mWidth).setListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {if (mView != null) {mView.setTag(false);mView.setOnTouchListener(null);mView.clearAnimation();leftOut(mView);mView = null;}}}).start();//滑出父view的范围} else {mView.animate().x(mInitX).start();//让View回滚到初始位置}mRecordX = 0;break;}return false;}abstract void leftOut(View view);abstract void onClick(View view);
}

这篇关于SwipeCardView有点类似于stackview的控件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084336

相关文章

lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示

在使用 LVGL 8.3.6 创建一个垂直布局,其中 label 控件位于 image 控件下方,你可以使用 lv_obj_set_flex_flow 来设置布局为垂直,并确保 label 控件在 image 控件后添加。这里是如何步骤性地实现它的一个基本示例: 创建父容器:首先创建一个容器对象,该对象将作为布局的基础。设置容器为垂直布局:使用 lv_obj_set_flex_flow 设置容器

STL经典案例(四)——实验室预约综合管理系统(项目涉及知识点很全面,内容有点多,耐心看完会有收获的!)

项目干货满满,内容有点过多,看起来可能会有点卡。系统提示读完超过俩小时,建议分多篇发布,我觉得分篇就不完整了,失去了这个项目的灵魂 一、需求分析 高校实验室预约管理系统包括三种不同身份:管理员、实验室教师、学生 管理员:给学生和实验室教师创建账号并分发 实验室教师:审核学生的预约申请 学生:申请使用实验室 高校实验室包括:超景深实验室(可容纳10人)、大数据实验室(可容纳20人)、物联网实验

小程序button控件上下边框的显示和隐藏

问题 想使用button自带的loading图标功能,但又不需要button显示边框线 button控件有一条淡灰色的边框,在控件上了样式 border:none; 无法让button边框隐藏 代码如下: <button class="btn">.btn{border:none; /*一般使用这个就是可以去掉边框了*/} 解决方案 发现button控件有一个伪元素(::after

MFC中Spin Control控件使用,同时数据在Edit Control中显示

实现mfc spin control 上下滚动,只需捕捉spin control 的 UDN_DELTAPOD 消息,如下:  OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult) {  LPNMUPDOWN pNMUpDown = reinterpret_cast(pNMHDR);  // TODO: 在此添加控件通知处理程序代码    if

最近心情有点复杂:论心态

一月一次的彷徨又占据了整个身心;彷徨源至不自信;而不自信则是感觉自己的价值没有很好的实现亦或者说是自己不认可自己的目前的生活和状态吧。 我始终相信一句话:任何人的生活形态完全是由自己决定的;外在的总归不能直达一个人的内心深处。所以少年 为了自己想要的生活 多坚持努力吧、不为别人只为自己心中的那一丝执着。 由此我看到了一个故事: 一个心情烦躁的人去拜访禅师。他问禅师:我这辈子就这么注定了吗?您

MFC 控件重绘(2) NM_CUSTOMDRAW, WM_DRAWITEM, 虚函数DrawItem

控件重绘有三种方法: 1 设定界面属性 2 利用Windows的消息机制,通过Windows消息映射(Message Mapping)和反映射(Message Reflecting),在合适的时机修改控件的状态和行为。此方式涉及NM_CUSTOMDRAW和WM_DRAWITEM 3 利用虚函数机制,重载虚函数。即DrawItem虚函数。 对于NM_CUSTOMDRAW,某些支持此消息的控件

C# 通过拖控件移动窗体

目录 引言一、通过控件事件移动窗体1、创建窗体界面2、添加控件事件3、添加代码 二、通过windowsAPI移动窗体1、 构建窗体和添加事件2、代码展示 引言 在C#Form窗体设计中,如果我们不需要使用默认边框设计自己个性化的窗体(FromBorderStyle=none时),这时候你会发现拖动窗体的功能就没有了,这里需要自己构建方法让用户可以拖动整个窗体,这里我们使用前辈的

Qt-常用控件(3)-多元素控件、容器类控件和布局管理器

1. 多元素控件 Qt 中提供的多元素控件有: QListWidgetQListViewQTableWidgetQTableViewQTreeWidgetQTreeView xxWidget 和 xxView 之间的区别,以 QTableWidget 和 QTableView 为例. QTableView 是基于 MVC 设计的控件.QTableView 自身不持有数据,使用 QTab

ASP.NET手动触发页面验证控件事件

开发环境:.NET Framework 3.5.1 sp1 参考文章: http://www.codeproject.com/KB/aspnet/JavascriptValidation.aspx http://msdn.microsoft.com/zh-cn/library/aa479045.aspx http://www.cnblogs.com/minsentinel/archive/

兔子--计算listview的高度,解决listview与scrollview控件冲突

/** * 计算ListView的高度 * * @param listView */ public void setListViewHeightBasedOnChildren(ListView listView) { // 获取ListView对应的Adapter OrderGoodsAdapter listAdapter = (OrderGoodsAdapter) listView.getAda