startX += (childWidth + normalViewGap);
if (startX > getWidth() - getPaddingRight()) {
mLastVisiPos = i;
break;
}
}
return dx;
}
涉及的方法:
/**
- 最大偏移量
- @return
*/
private float getMaxOffset() {
if (childWidth == 0 || getItemCount() == 0) return 0;
return (childWidth + normalViewGap) * (getItemCount() - 1);
}
/**
- 获取某个childView在水平方向所占的空间,将margin考虑进去
- @param view
- @return
*/
public int getDecoratedMeasurementHorizontal(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return getDecoratedMeasuredWidth(view) + params.leftMargin
- params.rightMargin;
}
/**
- 获取某个childView在竖直方向所占的空间,将margin考虑进去
- @param view
- @return
*/
public int getDecoratedMeasurementVertical(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return getDecoratedMeasuredHeight(view) + params.topMargin
- params.bottomMargin;
}
回收复用
这里使用Android仿豆瓣书影音频道推荐表单堆叠列表RecyclerView-LayoutManager中使用的回收技巧:
/**
- @param recycler
- @param state
- @param delta
*/
private int fill(RecyclerView.Recycler recycler, RecyclerView.State state, int delta) {
int resultDelta = delta;
//。。。省略
recycleChildren(recycler);
log(“childCount= [” + getChildCount() + “]” + “,[recycler.getScrapList().size():” + recycler.getScrapList().size());
return resultDelta;
}
/**
- 回收需回收的Item。
*/
private void recycleChildren(RecyclerView.Recycler recycler) {
List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
for (int i = 0; i < scrapList.size(); i++) {
RecyclerView.ViewHolder holder = scrapList.get(i);
removeAndRecycleView(holder.itemView, recycler);
}
}
回收复用这里就不验证了,感兴趣的小伙伴可自行验证。
动画效果
private int fillHorizontalLeft(RecyclerView.Recycler recycler, RecyclerView.State state, int dx) {
// 省略 …
//----------------3、开始布局-----------------
for (int i = mFirstVisiPos; i <= mLastVisiPos; i++) {
// 省略 …
// 缩放子view
final float minScale = 0.6f;
float currentScale = 0f;
final int childCenterX = (r + l) / 2;
final int parentCenterX = getWidth() / 2;
isChildLayoutLeft = childCenterX <= parentCenterX;
if (isChildLayoutLeft) {
final float fractionScale = (parentCenterX - childCenterX) / (parentCenterX * 1.0f);
currentScale = 1.0f - (1.0f - minScale) * fractionScale;
} else {
final float fractionScale = (childCenterX - parentCenterX) / (parentCenterX * 1.0f);
currentScale = 1.0f - (1.0f - minScale) * fractionScale;
}
item.setScaleX(currentScale);
item.setScaleY(currentScale);
item.setAlpha(currentScale);
layoutDecoratedWithMargins(item, l, t, r, b);
// 省略 …
}
return dx;
}
childView
越向屏幕中间移动缩放比越大,越向两边移动缩放比越小。
自动选中
1、滚动停止后自动选中
监听 onScrollStateChanged
,在滚动停止时计算出应当停留的 position
,再计算出停留时的 mHorizontalOffset
值,播放属性动画将当前 mHorizontalOffset
不断更新至最终值即可。相关代码如下:
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
switch (state) {
case RecyclerView.SCROLL_STATE_DRAGGING:
//当手指按下时,停止当前正在播放的动画
cancelAnimator();
break;
case RecyclerView.SCROLL_STATE_IDLE:
//当列表滚动停止后,判断一下自动选中是否打开
if (isAutoSelect) {
//找到离目标落点最近的item索引
smoothScrollToPosition(findShouldSelectPosition());
}
break;
default:
break;
}
}
/**
- 平滑滚动到某个位置
- @param position 目标Item索引
*/
public void smoothScrollToPosition(int position) {
if (position > -1 && position < getItemCount()) {
startValueAnimator(position);
}
}
private int findShouldSelectPosition() {
if (onceCompleteScrollLength == -1 || mFirstVisiPos == -1) {
return -1;
}
int position = (int) (Math.abs(mHorizontalOffset) / (childWidth + normalViewGap));
int remainder = (int) (Math.abs(mHorizontalOffset) % (childWidth + normalViewGap));
// 超过一半,应当选中下一项
if (remainder >= (childWidth + normalViewGap) / 2.0f) {
if (position + 1 <= getItemCount() - 1) {
return position + 1;
}
}
return position;
}
private void startValueAnimator(int position) {
cancelAnimator();
final float distance = getScrollToPositionOffset(position);
long minDuration = 100;
long maxDuration = 300;
long duration;
float distanceFraction = (Math.abs(distance) / (childWidth + normalViewGap));
if (distance <= (childWidth + normalViewGap)) {
duration = (long) (minDuration + (maxDuration - minDuration) * distanceFraction);
} else {
duration = (long) (maxDuration * distanceFraction);
}
selectAnimator = ValueAnimator.ofFloat(0.0f, distance);
selectAnimator.setDuration(duration);
selectAnimator.setInterpolator(new LinearInterpolator());
final float startedOffset = mHorizontalOffset;
selectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
mHorizontalOffset = (long) (startedOffset + value);
requestLayout();
}
});
selectAnimator.start();
}
2、点击非焦点view自动将其选中为焦点view
我们可以直接拿到 view
的 position
,直接调用 smoothScrollToPosition
方法,就可以实现自动选中为焦点。
中间view覆盖在两边view之上
效果是这样的:
从效果中可以看出,索引为2的view覆盖在1,3的上面,同时1又覆盖在0的上面,以此内推。
RecyclerView
继承于 ViewGroup
,那么在添加子view addView(View child, int index)
中 index
的索引值越大,越显示在上层。那么可以得出,为2的绿色卡片被添加是 index
最大,分析可以得出以下结论:
index
的大小:
0 < 1 < 2 > 3 > 4
中间最大,两边逐渐减小的原则。
获取到中间 view
的索引值,如果小于等于该索引值则调用 addView(item)
,反之调用 addView(item, 0)
;相关代码如下:
private int fillHorizontalLeft(RecyclerView.Recycler recycler, RecyclerView.State state, int dx) {
//省略 …
//----------------3、开始布局-----------------
for (int i = mFirstVisiPos; i <= mLastVisiPos; i++) {
//省略 …
int focusPosition = (int) (Math.abs(mHorizontalOffset) / (childWidth + normalViewGap));
if (i <= focusPosition) {
addView(item);
} else {
addView(item, 0);
}
//省略 …
}
return dx;
}
文章到这里就差不多要结束了。
源码地址:github.com/HpWens/MeiW…
更多系列教程GitHub白嫖入口:https://github.com/Timdk857/Android-Architecture-knowledge-2-
B站全套Android移动架构师进阶视频教程白嫖地址:https://space.bilibili.com/544650554
学习福利
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
详细整理在GitHub可以见;
Android架构视频+BAT面试专题PDF+学习笔记
3nCP58-1646390194717)]
详细整理在GitHub可以见;
Android架构视频+BAT面试专题PDF+学习笔记
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。