业务也不知道从哪儿找来了一个叫超真实的app,然后要模仿那种伪3D的商品效果展示。先看最后实现出来的效果。
业务给每个产品提供12张图,就是每30°提供一张,随着手指的滑动,不断切换图片,达到一种3D的展示效果。
已上传jitpack,可以直接导入
maven { url 'https://jitpack.io' }
implementation 'com.github.BaojunCZ:showFake3D:v1.1'
具体使用方法请看github项目地址
一共三个构造方法
完整构造
init(ArrayListpics, int moveOffset, int intertiaStart, int intertiaOffset, int intertiaEnd)
一参图片地址集合,图片是已从右往左为基准,二参是滑动整个宽度切换多少张图片,三四五参是惯性相关参数,分别是惯性开始速度,衰弱速度,结束速度
init(ArrayListpics, int moveOffset)
init(ArrayListpics)
具体实现
首先获取整个空间的宽段,然后进行分割我默认设置为15,也就是手指从最左侧滑动到最右侧,一共切换15张图片。
然后在onTouchEvent中处理图片切换。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
leftScreen = false;
start = event.getX();
startTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
float distance = event.getX() - start;
distance = distance > 0 ? distance : -distance;
long partTime = System.currentTimeMillis() - startTime;
float v = distance / partTime;
v = v > 0 ? v : -v;
float StandardV = singleSpec / 60;
Log.e(Tag, "distance=" + distance + ">>singleSpec=" + singleSpec + ">>v=" + v + ">>StandardV=" + StandardV);
if (distance < singleSpec) {
leftScreen = false;
} else if (v < StandardV) {
leftScreen = false;
} else {
leftScreen = true;
Log.e(Tag, "ACTION_UP>>" + NowID);
intertia(NowID, intertiaStart);
}
break;
case MotionEvent.ACTION_MOVE:
int nowX = (int) event.getX();
//计算移动距离
float spec = nowX - start;
int mo;
String id;
if (spec > 0) {
//从左向右滑
Log.e(Tag, "0 spec=" + spec + ">>singleSpec=" + singleSpec);
mo = (int) (spec / singleSpec);
if (mo > pics.size())
mo = mo - pics.size();
mo = pics.size() + 1 - mo;
positive = false;
} else {
//从右向左滑
Log.e(Tag, "1 spec=" + spec + ">>singleSpec=" + singleSpec);
spec = 0 - spec;
mo = (int) (spec / singleSpec);
if (mo > pics.size())
mo = mo - pics.size();
positive = true;
}
if (mo != 0 && mo != 13) {
id = pics.get(mo - 1);
if (mo != NowID && !leftScreen) {
Log.e(Tag, "ACTION_MOVE>>" + NowID);
NowID = mo;
Uri uri = Uri.parse(id);
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setControllerListener(new BaseControllerListener())
.build();
iv.setController(controller);
}
}
break;
}
return true;
}
计算手指移动距离,判断手指滑动的方向,从左到右和从右到左肯定是不同的,给的图是默认从右向左滑动的。
滑动超过12张图片的距离,就继续循环加载。
手指离开的时候需要设置一个惯性,本来一直考虑惯性衰减的问题,初速度是多少,加速度是多少,后来突然发现好像没必要这么复杂,惯性给一个固定的动画速度去衰减就行了。
启动惯性为了避免太假,所以要做两层判断,首先至少要滑动一个单位的距离,其次速度要大于60ms每张的速度才能触发惯性。
下面是惯性的代码
//惯性
private void intertia(int ID, final int time) {
if (ID == 0)
ID = pics.size();
if (ID == pics.size() + 1)
ID = 1;
Log.e(Tag, "intertia>>ID=" + ID + ">>time=" + time + ">>leftScreen=" + leftScreen);
if (leftScreen) {
if (ID < pics.size() + 1) {
Uri uri = Uri.parse(pics.get(ID - 1));
final int finalID = ID;
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setControllerListener(new BaseControllerListener() {
@Override
public void onSubmit(String id, Object callerContext) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
//惯性最后一定回到第一张图片
if (time < intertiaEnd || finalID != 1) {
if (positive)
intertia(finalID + 1, time + intertiaOffset);
else
intertia(finalID - 1, time + intertiaOffset);
}
}
}, time);
Log.e(Tag, "onSubmit>>" + finalID);
super.onSubmit(id, callerContext);
}
})
.build();
iv.setController(controller);
}
}
}
很简单就是固定衰弱速度,到达设置的最慢速度就停止,同时为了好看一点,所以每次都让他回到第一张图再停止。
这样就基本完成了,可以看到我用的fresco来加载图片,其实最开始用的Glide,但是有个严重的问题,glide加载图片速度慢,导致图片切换一直在闪,所以就换了fresco,完美解决这个问题,Glide确实是轻量化,fresco太大但是功能更全面一点,加载速度很快,很流畅。但是第一次加载的时候也会闪,缓存结束后就ok了,很顺滑,所以在打开之后我先把图片全部加载一遍,作缓存。
//图片预加载图片,先将图片全部加载出来,解决滑动过程中白屏的问题
private void setCache(final int ID) {
if (ID < pics.size()) {
Uri uri = Uri.parse(pics.get(ID));
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setControllerListener(new BaseControllerListener() {
@Override
public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {
super.onFinalImageSet(id, imageInfo, animatable);
Log.e(Tag, "onFinalImageSet");
setCache(ID + 1);
}
})
.build();
iv.setController(controller);
} else {
Uri uri = Uri.parse(pics.get(0));
iv.setImageURI(uri);
if (cacheListener != null)
cacheListener.finish();
}
}
github 项目地址