Android实现3D画廊效果
龙旋
共 9709字,需浏览 20分钟
· 2021-12-12
使用ViewPager打造的3D画廊,先看效果图:
1.中间item放大
2.中间item覆盖在两侧item上
3.点击或者滑动两侧的item可以切换ViewPager的当前展示页面
下面我们看代码怎么实现:
先看下布局,主界面只有一个RecyclerView就不贴代码了.
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:id="@+id/rl_vp_container"
android:orientation="vertical">
android:layout_toLeftOf="@+id/viewpager"
android:id="@+id/view_left"
android:layout_width="match_parent"
android:layout_height="303dp"
/>
android:layout_toRightOf="@+id/viewpager"
android:id="@+id/view_right"
android:layout_width="match_parent"
android:layout_height="303dp"
/>
android:layout_centerHorizontal="true"
android:layout_width="200dp"
android:layout_height="303dp"
android:id="@+id/viewpager"
/>
android:layout_centerHorizontal="true"
android:layout_below="@+id/viewpager"
android:layout_marginTop="8dp"
android:id="@+id/tv_star_desc"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
android:scaleX="0.85"
android:scaleY="0.85"
android:elevation="6dp"
>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
android:adjustViewBounds="true"
android:id="@+id/iv_star_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
/>
android:layout_marginRight="6dp"
android:layout_marginBottom="8dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#00000000"
>
android:gravity="bottom"
android:id="@+id/tv_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="20sp"
android:shadowDy="2"
android:shadowDx="0"
android:shadowRadius="4"
android:text="2"
android:shadowColor="#80000000"/>
android:gravity="bottom"
android:id="@+id/tv_star_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="12sp"
android:shadowDy="2"
android:shadowDx="0"
android:shadowRadius="4"
android:text="/10"
android:shadowColor="#80000000" />
android:visibility="visible"
android:id="@+id/iv_cover"
android:src="@mipmap/bg_40black"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
/>
public class RotationPageTransformer implements ViewPager.PageTransformer{
private static final float MIN_SCALE=0.85f;
private static final float MIN_ALPHA=0.6f;
public void setContext(Context context) {
mContext = context;
}
private Context mContext;
@Override
public void transformPage(View page, float position) {
float scaleFactor = Math.max(MIN_SCALE,1 - Math.abs(position));
float scaleAlpha = Math.max(MIN_ALPHA,1 - Math.abs(position));
ImageView imageTag= (ImageView) page.findViewById(R.id.iv_cover);
float rotate = 0;
//position小于等于1的时候,代表page已经位于中心item的最左边,
//此时设置为最小的缩放率以及最大的旋转度数
if (position <= -1){
page.setScaleX(MIN_SCALE);
page.setScaleY(MIN_SCALE);
imageTag.setAlpha((float) 1);
}//position从0变化到-1,page逐渐向左滑动
else if (position < 0){
imageTag.setAlpha(Math.abs(position));
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
}//position从0变化到1,page逐渐向右滑动
else if (position >=0 && position < 1){
imageTag.setAlpha(position);
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
}//position大于等于1的时候,代表page已经位于中心item的最右边
else if (position >= 1){
imageTag.setAlpha((float)1);
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
}
}
}
public class MainAdapter extends RecyclerView.Adapter {
private Context mContext;
private float downX ; //按下时 的X坐标
private float downY ; //按下时 的Y坐标
private int currentPosition;
private int mOriginSize;
private List
dataList=new ArrayList<>();
public MainAdapter(Context context,List
list) { this.mContext=context;
dataList.addAll(list);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_main_star1, parent, false);
return new MainStarViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// 所以在空白的左右两侧各填充了透明的View(StarView自定义View拦截左右方向滑动事件),用于交互,左侧View向右滑动和点击ViewPager切换
// ,右侧View向左滑动和点击切换ViewPager,其它不做处理
final MainStarViewHolder mainStarHolder = (MainStarViewHolder) holder;
mainStarHolder.mViewPager.setPageTransformer(true, new RotationPageTransformer());
mainStarHolder.mViewPager.setOffscreenPageLimit(2);
DisplayMetrics dm = mContext.getApplicationContext().getResources().getDisplayMetrics();
//不同分辨率适配
int width = dm.widthPixels;
if (width > 800 && width <= 1080) {
mainStarHolder.mViewPager.setPageMargin(-200);
} else if (width > 1080) {
mainStarHolder.mViewPager.setPageMargin(-280);
} else {
mainStarHolder.mViewPager.setPageMargin(-180);
}
mainStarHolder.viewLeft.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) { //左侧透明View仅在向右滑时切换ViewPager
float x= event.getX();
float y = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//将按下时的坐标存储
downX = x;
downY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//获取到距离差
float dx= x-downX;
float dy = y-downY;
//防止是按下也判断
//通过距离差判断方向
int orientation = getOrientation(dx, dy);
switch (orientation) {
case 'r': //向右滑动
mainStarHolder.mViewPager.setCurrentItem(mainStarHolder.mViewPager.getCurrentItem()-1,true);
return true;
case '0': //点击
mainStarHolder.mViewPager.setCurrentItem(mainStarHolder.mViewPager.getCurrentItem()-1,true);
return true;
case 'l': //向左滑动
return true;
}
break;
}
return true;
}
});
mainStarHolder.viewRight.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) { //右侧透明View仅在向右滑时切换ViewPager
float x= event.getX();
float y = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//将按下时的坐标存储
downX = x;
downY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//获取到距离差
float dx= x-downX;
float dy = y-downY;
//通过距离差判断方向
int orientation = getOrientation(dx, dy);
switch (orientation) {
case 'r': //向右滑动
return true;
case '0': //点击
mainStarHolder.mViewPager.setCurrentItem(mainStarHolder.mViewPager.getCurrentItem()+1,true);
return true;
case 'l': //向左滑动
if(event.getAction()== MotionEvent.ACTION_UP || event.getAction()== MotionEvent.ACTION_CANCEL ){
mainStarHolder.mViewPager.setCurrentItem(mainStarHolder.mViewPager.getCurrentItem()+1,true);
}
return true;
}
break;
}
return true;
}
});
mOriginSize = dataList.size();
initStarAdapter(mainStarHolder, dataList);
mainStarHolder.mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
currentPosition = position;
mainStarHolder.mViewPager.setTranslationX(1);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@Override
public int getItemCount() {
return 1;
}
private void initStarAdapter(MainStarViewHolder mainStarHolder, List
dataList) { final MainStarAdapter mainStarAdapter = new MainStarAdapter(dataList, mContext, mainStarHolder.mViewPager, mOriginSize);
mainStarHolder.mViewPager.setAdapter(mainStarAdapter);
mainStarHolder.mViewPager.setCurrentItem( 20*mOriginSize + currentPosition);
}
//获取滑动方向
private int getOrientation(float dx, float dy) {
if (Math.abs(dx)>Math.abs(dy)){
//X轴移动
if(dx==0){//点击
return '0';
}
return dx>0?'r':'l';
}else{
//Y轴移动
if(dy==0){//点击
return '0';
}
return dy>0?'b':'t';
}
}
}
public class CustomViewPager extends ViewPager {
private ArrayList
childCenterXAbs = new ArrayList<>(); private SparseArray
childIndex = new SparseArray<>();
public CustomViewPager(Context context) {
super(context);
init();
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
setClipToPadding(false);
setOverScrollMode(OVER_SCROLL_NEVER);
}
/**
* @param childCount
* @param n
* @return 第n个位置的child 的绘制索引
*/
@Override
protected int getChildDrawingOrder(int childCount, int n) {
if (n == 0 || childIndex.size() != childCount) {
childCenterXAbs.clear();
childIndex.clear();
int viewCenterX = getViewCenterX(this);
for (int i = 0; i < childCount; ++i) {
int indexAbs = Math.abs(viewCenterX - getViewCenterX(getChildAt(i)));
//两个距离相同,后来的那个做自增,从而保持abs不同
if (childIndex.get(indexAbs) != null) {
++indexAbs;
}
childCenterXAbs.add(indexAbs);
childIndex.append(indexAbs, i);
}
Collections.sort(childCenterXAbs);//1,0,2 0,1,2
}
//那个item距离中心点远一些,就先draw它。(最近的就是中间放大的item,最后draw)
return childIndex.get(childCenterXAbs.get(childCount - 1 - n));
}
private int getViewCenterX(View view) {
int[] array = new int[2];
view.getLocationOnScreen(array);
return array[0] + view.getWidth() / 2;
}
}
评论
用 Shader 实现旗帜飘扬动画效果
我觉得对于刚入门 3D 编程的朋友来说,如果能够完成代码创建模型数据->创建材质->编写Shader动画这一系列,想必会有满满的成就感。今天就用 Cocos Creator 的 utils.MeshUtils.createMesh 接口,带大家感受一下这个流程。这个流程不仅可以用于新手学
COCOS
2
SpringBoot+Minio实现上传凭证、分片上传、秒传和断点续传
关注我们,设为星标,每天7:40不见不散,架构路上与您共享回复架构师获取资源大家好,我是你们的朋友架构君,一个会写代码吟诗的架构师。Spring Boot整合Minio后,前端的文件上传有两种方式:1、文件上传到后端,由后端保存到Minio这种方式好处是完全由后端集中管理,可以很好的做到、身份验证、
Java架构师社区
0
超越原生,散点图实现华夫饼图
之前我们介绍过了如何使用新卡片图实现华夫饼图。参考:超越原生,PowerBI 华夫饼图实现但是利用卡片图实现的华夫饼图有一些缺点,形状之间的大小跟间距不太好把握,而且有时形状大一点的话显示就会不正常,需要做出二次调整。今天给大家介绍一种原生视觉对象生成华夫饼图的更佳方案,既简单又美观。上图是利用散点
PowerBI战友联盟
2
全新 SOTA backbone | 2024年了,再见ViT系列Backbone,实数难得,不知道效果如何?
点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达在构建用于精确匹配的深度固定长度表示时,确定指纹上的密集特征点,特别是在像素 Level 上,具有重大意义。为了探索指纹匹配的可解释性,作者提出了一种多阶段可解释的指纹匹配网络,名为通过视觉 Transformer 进行指纹匹配的
小白学视觉
10
Spring Boot + flowable 快速实现工作流
关注我们,设为星标,每天7:40不见不散,架构路上与您共享回复架构师获取资源大家好,我是你们的朋友架构君,一个会写代码吟诗的架构师。来源:blog.csdn.net/zhan107876/article/details/120815560总览一、flowable-ui部署运行二、绘制流程图绘图细节:
Java架构师社区
0
实现订单30分钟自动取消的策略
原文:juejin.cn/post/7285167401821798400简介在电商和其他涉及到在线支付的应用中,通常需要实现一个功能:如果用户在生成订单后的一定时间内未完成支付,系统将自动取消该订单。本文将详细介绍基于Spring Boot框架实现订单30分钟内未支付自动取消的几种方案,并提供实例
JAVA乐园
0
AI大模型之路 第三篇:从零实现词嵌入模型,加深理解!
你好,我是郭震今天我们研究「AI大模型第三篇」:词维度预测,很多读者听过词嵌入,这篇文章解答下面问题:词嵌入是什么意思?怎么做到的?原理是什么?从零实现一个专属你数据集的词嵌入我们完整从零走一遍,根基的东西要理解透,这样才能发明出更好的东西。1 skip-gram模型Skip-gram模型是一种广泛
Python与算法社区
11
从理解路由到实现一套Router(路由)
大厂技术 高级前端 Node进阶点击上方 程序员成长指北,关注公众号回复1,加入高级Node交流群平时在Vue项目中经常用到路由,但是也仅仅处于会用的层面,很多基础知识并不是真正的理解。于是就趁着十一”小长假“查阅了很多资料,总结下路由相关的知识
程序员成长指北
347