【MyAndroid】RecyclerView+cardView卡片叠层效果展示(3)--100个经典UI设计模板(98/100)
布局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CardOverlayRecyclerViewActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
自定义布局管理器
public class CardLayoutManager extends RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT);
}
/**
* 自定义LayoutManager核心是摆放控件,所以onLayoutChildren方法是我们要改写的核心
*
* @param recycler
* @param state
*/
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
//缓存
detachAndScrapAttachedViews(recycler);
//获取所有item(包括不可见的)个数
int count = getItemCount();
//由于我们是倒序摆放,所以初始索引从后面开始
int initIndex = count - CardConfig.SHOW_MAX_COUNT;
if (initIndex < 0) {
initIndex = 0;
}
for (int i = initIndex; i < count; i++) {
//从缓存中获取view
View view = recycler.getViewForPosition(i);
//添加到recyclerView
addView(view);
//测量一下view
measureChild(view, 0, 0);
//居中摆放,getDecoratedMeasuredWidth方法是获取带分割线的宽度,比直接使用view.getWidth()精确
int realWidth = getDecoratedMeasuredWidth(view);
int realHeight = getDecoratedMeasuredHeight(view);
int widthPadding = (int) ((getWidth() - realWidth) / 2f);
int heightPadding = (int) ((getHeight() - realHeight) / 2f);
//摆放child
layoutDecorated(view, widthPadding, heightPadding,
widthPadding + realWidth, heightPadding + realHeight);
//根据索引,来位移和缩放child
int level = count - i - 1;
//level范围(CardConfig.SHOW_MAX_COUNT-1)- 0
// 最下层的不动和最后第二层重叠
if (level == CardConfig.SHOW_MAX_COUNT - 1) {
level--;
}
view.setTranslationY(level * CardConfig.TRANSLATION_Y);
view.setScaleX(1 - level * CardConfig.SCALE);
view.setScaleY(1 - level * CardConfig.SCALE);
}
}
}
实现ItemTouchHelperCallback接口。启用ItemTouchHelper
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
private List<String> mDatas;
private RecyclerView.Adapter mAdapter;
public ItemTouchHelperCallback(List<String> mDatas, RecyclerView.Adapter mAdapter) {
this.mDatas = mDatas;
this.mAdapter = mAdapter;
}
/**
makeMovementFlags(p1,p2)
p1:侧滑删除使用:0忽略手势;ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN
p2:拖动滑动使用:0忽略手势;ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN
*/
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
String s = mDatas.remove(viewHolder.getLayoutPosition());
mDatas.add(0, s);
mAdapter.notifyDataSetChanged();
}
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
//计算移动距离
float distance = (float) Math.hypot(dX, dY);
float maxDistance = recyclerView.getWidth() / 2f;
//比例
float fraction = distance / maxDistance;
if (fraction > 1) {
fraction = 1;
}
//为每个child执行动画
int count = recyclerView.getChildCount();
for (int i = 0; i < count; i++) {
//获取的view从下层到上层
View view = recyclerView.getChildAt(i);
int level = CardConfig.SHOW_MAX_COUNT - i - 1;
//level范围(CardConfig.SHOW_MAX_COUNT-1)-0,每个child最大只移动一个CardConfig.TRANSLATION_Y和放大CardConfig.SCALE
if (level == CardConfig.SHOW_MAX_COUNT - 1) { // 最下层的不动和最后第二层重叠
view.setTranslationY(CardConfig.TRANSLATION_Y * (level - 1));
view.setScaleX(1 - CardConfig.SCALE * (level - 1));
view.setScaleY(1 - CardConfig.SCALE * (level - 1));
} else if (level > 0) {
view.setTranslationY(level * CardConfig.TRANSLATION_Y - fraction * CardConfig.TRANSLATION_Y);
view.setScaleX(1 - level * CardConfig.SCALE + fraction * CardConfig.SCALE);
view.setScaleY(1 - level * CardConfig.SCALE + fraction * CardConfig.SCALE);
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
//控制滑动距离生效灵敏度
@Override
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
return 0.3f;
}
然后结合Activity使用
rv.setLayoutManager(new CardLayoutManager());
for (int i = 1; i < 21; i++) {
mStrings.add(String.valueOf(i));
}
MyAdapter myAdapter = new MyAdapter(this, mStrings);
rv.setAdapter(myAdapter);
ItemTouchHelperCallback itemTouchHelperCallback = new ItemTouchHelperCallback(mStrings, myAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchHelperCallback);
itemTouchHelper.attachToRecyclerView(rv);
搞定收工
自研产品推荐
历时一年半多开发终于smartApi-v1.0.0版本在2023-09-15晚十点正式上线
smartApi是一款对标国外的postman的api调试开发工具,由于开发人力就作者一个所以人力有限,因此v1.0.0版本功能进行精简,大功能项有:
- api参数填写
- api请求响应数据展示
- PDF形式的分享文档
- Mock本地化解决方案
- api列表数据本地化处理
- 再加上UI方面的打磨
为了更好服务大家把之前的公众号和软件激活结合,如有疑问请大家反馈到公众号即可,下个版本30%以上的更新会来自公众号的反馈。
嗯!先解释不上服务端原因,API调试工具的绝大多数时候就是一个数据模型、数据处理、数据模型理解共识的问题解决工具,所以作者结合自己十多年开发使用的一些痛点来打造的,再加上服务端开发一般是面向企业的,作者目前没有精力和时间去打造企业服务。再加上没有资金投入所以服务端开发会滞后,至于什么时候会进行开发,这个要看募资情况和用户反馈综合考虑。虽然目前国内有些比较知名的api工具了,但作者使用后还是觉得和实际使用场景不符。如果有相关吐槽也可以在作者的公众号里反馈蛤!
下面是一段smartApi使用介绍:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~