我们实现的效果:
(为了更加可定制化,我在原图基础上新增了新的效果)
可以快速滚动,并且无限循环
这个是对一些参数的进行设定
对图片的包裹效果
因为本身继承自ViewGroup,所以基本控件都是可以包裹的
因为代码量有点大,感觉把代码全部粘贴上来也不现实。所以想了解我的思路的盆友可以先来这里下载代码。然后边看代码边看我的分析,下载地址 :https://github.com/ImmortalZ/StereoView
通过我们实现的效果图可以发现:
1.切换的时候是一个3D立体的效果
2.布局中的每一个Item可以自由切换,且无限循环滚动
要解决上面的效果,我们需要什么技术点呢?
1.要想实现一个3D效果,我们可以借助Android中的Camera、Matrix
2.要想实现滚动,毫无疑问,我们需要借助Scroller
当然一切看起来很简单,其实不然,除此之外,你还需要对于滑动冲突进行处理等等,下面我开始介绍啦。
这就是我们这次项目的大致
因为我们是要打造一个容器类,所以肯定得继承自 ViewGroup
按照一般的思路,我们肯定是先要进行一些变量的申明,onMeasure,onLayout操作
private void init(Context context) {
mCamera = new Camera();
mMatrix = new Matrix();
if (mScroller == null) {
mScroller = new Scroller(context);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
//滑动到设置的StartScreen位置
scrollTo(0, mStartScreen * mHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childTop = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
child.layout(0, childTop,
child.getMeasuredWidth(), childTop + child.getMeasuredHeight());
childTop = childTop + child.getMeasuredHeight();
}
}
}
完成这些操作后,我们需要在onTouchEvent中进行滑动事件的处理
3.1 完成无限循环滑动滚动
我们的item数量是有限的,如何实现无限循环滚动呢?很简单,以3个item为例子(分别为1,2,3),我们让屏幕显示的是2
如此反复,屏幕所在的位置始终是第2个item所在的位置,这样就实现了我们的无限循环滚动,向下滚动也是如此
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
//当上一次滑动没有结束时,再次点击,强制滑动在点击位置结束
mScroller.setFinalY(mScroller.getCurrY());
mScroller.abortAnimation();
scrollTo(0, getScrollY());
}
mDownY = y;
break;
case MotionEvent.ACTION_MOVE:
int realDelta = (int) (mDownY - y);
mDownY = y;
if (mScroller.isFinished()) {
//因为要循环滚动
recycleMove(realDelta);
}
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000);
float yVelocity = mVelocityTracker.getYVelocity();
//滑动的速度大于规定的速度,或者向上滑动时,上一页页面展现出的高度超过1/2。则设定状态为State.ToPre
if (yVelocity > standerSpeed || ((getScrollY() + mHeight / 2) / mHeight < mStartScreen)) {
mState = State.ToPre;
} else if (yVelocity < -standerSpeed || ((getScrollY() + mHeight / 2) / mHeight > mStartScreen)) {
//滑动的速度大于规定的速度,或者向下滑动时,下一页页面展现出的高度超过1/2。则设定状态为State.ToNext
mState = State.ToNext;
} else {
mState = State.Normal;
}
//根据mState进行相应的变化
changeByState(yVelocity);
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
//返回true,消耗点击事件
return true;
}
当手从屏幕上移开时,我们来看下这个方法changeByState(yVelocity);
我们以mState = State.ToPre 为例子来说明
/**
-
mState = State.ToPre 时进行的动作
-
@param yVelocity 竖直方向的速度
*/
private void toPreAction(float yVelocity) {
int startY;
int delta;
int duration;
mState = State.ToPre;
addPre();//增加新的页面
//计算松手后滑动的item个数
int flingSpeedCount= (yVelocity - standerSpeed) > 0 ? (int) (yVelocity - standerSpeed) : 0;
addCount = flingSpeedCount/ flingSpeed + 1;
//mScroller开始的坐标
startY = getScrollY() + mHeight;
setScrollY(startY);
//mScroller 移动的距离
delta = -(startY - mStartScreen * mHeight) - (addCount - 1) * mHeight;
duration = (Math.abs(delta)) * 3;
mScroller.startScroll(0, startY, 0, delta, duration);
addCount–;
}
然后会进入addPre方法中
/**
- 把最后一个item移动到第一个item位置
*/
private void addPre() {
mCurScreen = ((mCurScreen - 1) + getChildCount()) % getChildCount();
int childCount = getChildCount();
View view = getChildAt(childCount - 1);
removeViewAt(childCount - 1);
addView(view, 0);
if (iStereoListener != null) {
iStereoListener.toPre(mCurScreen);
}
}
最后mScroller.startScroll(0, startY, 0, delta, duration); 开始执行。
执行的过程中会回调这个函数方法computeScroll
完成到这一步,我们的无限滑动滚动就算是完成了
3.2 实现3D切换效果。
正常情况下,我们自定义ViewGroup并不需要重写dispatchDraw 方法。
而这里我们则需要重写
@Override
protected void dispatchDraw(Canvas canvas) {
if (!isAdding && isCan3D) {
//当开启3D效果并且当前状态不属于 computeScroll中 addPre() 或者addNext()
//如果不做这个判断,addPre() 或者addNext()时页面会进行闪动一下
//我当时写的时候就被这个坑了,后来通过log判断,原来是computeScroll中的onlayout,和子Child的draw触发的顺序导致的。
//知道原理的朋友希望可以告知下
for (int i = 0; i < getChildCount(); i++) {
drawScreen(canvas, i, getDrawingTime());
}
} else {
isAdding = false;
super.dispatchDraw(canvas);
}
}
好,我们来drawScreen这个方法
private void drawScreen(Canvas canvas, int i, long drawingTime) {
int curScreenY = mHeight * i;
//屏幕中不显示的部分不进行绘制
if (getScrollY() + mHeight < curScreenY) {
return;
}
if (curScreenY < getScrollY() - mHeight) {
return;
}
float centerX = mWidth / 2;
float centerY = (getScrollY() > curScreenY) ? curScreenY + mHeight : curScreenY;
float degree = mAngle * (getScrollY() - curScreenY) / mHeight;
if (degree > 90 || degree < -90) {
return;
}
canvas.save();
mCamera.save();
mCamera.rotateX(degree);
mCamera.getMatrix(mMatrix);
mCamera.restore();
mMatrix.preTranslate(-centerX, -centerY);
mMatrix.postTranslate(centerX, centerY);
canvas.concat(mMatrix);
drawChild(canvas, getChildAt(i), drawingTime);
canvas.restore();
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
尾声
面试成功其实都是必然发生的事情,因为在此之前我做足了充分的准备工作,不单单是纯粹的刷题,更多的还会去刷一些Android核心架构进阶知识点,比如:JVM、高并发、多线程、缓存、热修复设计、插件化框架解读、组件化框架设计、图片加载框架、网络、设计模式、设计思想与代码质量优化、程序性能优化、开发效率优化、设计模式、负载均衡、算法、数据结构、高级UI晋升、Framework内核解析、Android组件内核等。
不仅有学习文档,视频+笔记提高学习效率,还能稳固你的知识,形成良好的系统的知识体系。这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
大厂面试真题
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《2017-2021字节跳动Android面试历年真题解析》
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
-1711765657066)]
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
[外链图片转存中…(img-y25GB202-1711765657066)]
大厂面试真题
PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-55SVw0Fj-1711765657067)]
《2017-2021字节跳动Android面试历年真题解析》
[外链图片转存中…(img-WqvuizyW-1711765657067)]