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

相关文章

XMG xib中不属于一个类的控件,拖线到指定的类中

1.比如我现在有一个view绑定为GreenView,我们按住control向类里面拖线的方式想要达到目的,显然拖不进去。例图如下 那么我们此时还想要达到目的,就需要自己去GreenView的类内部去写IBo 然后这面连接起来 2.第二,大哥郝良建给做的扩展 可以在.h或者.m中写一个NSObject的属性 然后在xib中对应的位置创建一个NSObject的属性

Avalonia 常用控件二 Menu相关

1、Menu 添加代码如下 <Button HorizontalAlignment="Center" Content="Menu/菜单"><Button.Flyout><MenuFlyout><MenuItem Header="打开"/><MenuItem Header="-"/><MenuItem Header="关闭"/></MenuFlyout></Button.Flyout></B

3_创建Tab控件

1,新建MFC 对话框项目,为对话框添加Tab控件,选中Tab控件,新建控件变量m_tab_ctrl 2,为Tab控件添加tab项 m_tab_ctrl.InsertItem(0, L”000”),参数1,哪个位置;参数2,item的名称 3,为Tab控件添加监听事件, void C测试Dlg::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESUL

示例:推荐一个基于第三方开源控件库DataGridFilter封装的FilterColumnDataGrid,可以像Excel拥有列头筛选器

一、目的:基于第三方开源控件库DataGridFilter封装的FilterColumnDataGrid,可以像Excel拥有列头筛选器,感兴趣的可以去下方链接地址查看开源控件库地址。本控件封装的目的在于将第三方库的皮肤和样式封装到皮肤库中可统一设置样式,同时生成nuget方便调用 二、效果如下 三、环境 VS2022 Net7 四、使用方式 1、安装nuget包:H.Con

TextGroupView (TextView组合控件)

TextGroupView ImageView + TextView + TextView +TextView+ EditText +ImageView + ImageView 实现的组合控件 JitPack依赖 A.项目/build.grade allprojects {repositories {...maven { url 'https://jitpack.io' }}} B.

Android 扇形网络控件 - 无网络视图(动画)

前言 一般在APP没有网络的情况下,我们都会用一个无网络的提示图标,在提示方面为了统一app的情况,我们一般使用简单的提示图标,偶尔只需要改变一下图标的颜色就一举两得,而不需要让PS来换一次颜色。当然app有图标特殊要求的就另当别论了。 效果图 当你第一眼看到这样的图,二话不说直接让UI给你切一张图标来的快对吧,我其实开始也是这么想的,但是到了做的app越来越多的时候,你就会发现就算是用

轻量级控件SnackBar使用以及源码分析

本篇博客将会给大家带来一个轻量级控件SnackBar,为什么要讲SnackBar?Snackbar:的提出实际上是界于Toast和Dialog的中间产物。因为Toast与Dialog各有一定的不足,使用Toast的时候, 用户无法交互;使用Dialog:用户可以交互,但是体验会打折扣,会阻断用户的连贯性操作;但是使用Snackbar既可以做到轻量级的用户提醒效果,又可以有交互的功能,本博客将会从S

C#界面动态布局 界面控件随着界面大小尺寸变化而变化

要想写一个漂亮的界面,光靠利用Anchor和Dock属性是远远不够的,我们需要用到相对布局,就是不管窗口大小怎么变化,控件相对父控件的相对位置保持不变。可惜c#里没有提供按照百分比布局。所以只能自己再resize()事件里调整控件位置。 首先在窗体的构造函数里保存父窗体的长宽,以及每个控件的X,Y坐标的相对位置:  int count = this.Controls.Count * 2 + 2;

做测开的第4年,开始有点失望了

大家好,我是洋子,看到新一年的毕业季,意味着我工作马上满4年了 从一毕业就进入测试行业到现在,我已经褪去学生的气息,成为一名社畜工具人 这4年当中,我有3年半都在同一家互联网大厂工作,岗位是测开,厂内大家都叫QA 2024年已经过半了,整个就业大环境依然对打工人很不友好,裁员还是家常便饭,唯一看到的风口还是大模型,大模型的快速发展,激起了广泛讨论,未来究竟自己会不会被AI所取代? 说到大模

漂亮的多选属性控件 画?调出帮助功能

转自:http://www.cocoachina.com/applenews/devnews/2014/0108/7677.html 漂亮的多选属性控件-SAMultisectorControl  SAMultisectorControl可以圆形实现控制多个属性的功能,设计精美。滑动圆形滑块即可方便地调整属性数值范围。SAMultisectorControl使用了CoreGraph