您的位置:首页 > 其它

与ViewPager完美结合的顺滑引导条系列之二

2018-01-30 22:44 260 查看

引言

上一篇的最后提出了

索引滑动时游标不动

一次完整的滑动必须是索引或游标,不能共存

频繁不规则地滑动ViewPager或引导条时,会引起NullPointerException,因为RecyclerView找不到内部的View(所以才重写了dispatchTouchEvent)

这三个需要修改的缺陷。

针对第一点:索引滑动是因为RecyclerView会滑动,游标是添加在RelativeLayout上的,所以游标在响应手势的情况下是不会滑动的

针对第二点:由于一次完整的滑动结束后才会重置状态,导致滑动过程中不能切换滑动对象

针对第三点:由于RecyclerView的回收复用机制,导致无法获得想要项的具体View

从这三个问题出发,这次我改用HorizontalScrollView和View来进行组合,封装控件GuideSlideView。

这里立即就爆出一个可优化的点,控件没有复用,先把他当作缺陷列出,后续改进。

正文

GuideSlideView的布局如下

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<HorizontalScrollView
android:id="@+id/horizontalscrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none">

<LinearLayout
android:id="@+id/linearlayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</HorizontalScrollView>

<View
android:id="@+id/lineView"
android:layout_width="10dp"
android:layout_height="3dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="7dp"
android:background="#444444" />
</RelativeLayout>
</merge>


索引项中的View全部inflate出来后添加到LinearLayout,通过HorizontalScrollView的scrollBy进行与ViewPager的联动。

重点代码分析

horizontalScrollView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (whichOne != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
setScrollViewPager(event.getX() - lastX);
lastX = event.getX();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
lastX = 0;
break;
}

return true;
}

return false;
}
});

public ViewPager.OnPageChangeListener getOnPageChangeListener() {
return new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.i("onPageScrolled", "position-->" + position + "     positionOffset-->" + positionOffset + "     positionOffsetPixels-->" + positionOffsetPixels);
setScroll(position, positionOffset);
}
};
}

private void setScroll(int position, float offset) {

Log.i("setScroll", "position-->" + position + "     offset-->" + offset);

if (!whetherOrNotToMove(position, offset)) {
return;
}

if (!positiveDirection) {//根据方向设置偏移
offset = offset - 1;
}

int x = calculateMoveLength(position, offset);

move(x);
}

private boolean whetherOrNotToMove(int position, float offset) {
if (position > currentPosition) {//第一次正向滑动触发
positiveDirection = true;
directionPrepare(position);
return false;
}
if (position < currentPosition) {//第一次反向滑动触发
positiveDirection = false;
directionPrepare(position);
return false;
}
if (position == 0 && offset == 0) {//滑到第一个 初始状态
positiveDirection = true;
directionPrepare(position);
return false;
}

lineView.setVisibility(VISIBLE);
ViewHolder viewHolder;
if (positiveDirection) {
viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
} else {
viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
viewHolder.lineView.setVisibility(INVISIBLE);
viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition + 1));
}
viewHolder.lineView.setVisibility(INVISIBLE);

if (offset == 0) {
viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
viewHolder.lineView.setVisibility(VISIBLE);
lineView.setVisibility(INVISIBLE);
}
return true;
}

private void directionPrepare(int position) {
resetState(position);
if (whichOne != 1) {
if (positiveDirection) {
layoutLineView(position);
} else {
layoutLineView(position + 1);
}
}

ViewHolder viewHolder;
if (positiveDirection) {
viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
} else {
viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition + 1));
}
viewHolder.lineView.setVisibility(VISIBLE);
lineView.setVisibility(INVISIBLE);
}

private void resetState(int position) {//重置状态
currentPosition = position;
whichOne = 0;
lastx = 0;
}

private void layoutLineView(int currentPosition) {//将游标布局到指定位置
View item = linearLayout.getChildAt(currentPosition);
lineViewMarginLeft = item.getLeft() - horizontalScrollView.getScrollX() + (item.getWidth() - lineView.getWidth()) / 2;

RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) lineView.getLayoutParams();
params.leftMargin = lineViewMarginLeft;
lineView.setLayoutParams(params);

}

private int calculateMoveLength(int position, float offset) {
int temp = position;
if (temp >= linearLayout.getChildCount()) {
temp = linearLayout.getChildCount() - 1;
}
View currentView = linearLayout.getChildAt(temp);//当前所指view

int lastPosition = position + 1;
if (lastPosition >= linearLayout.getChildCount()) {
lastPosition = linearLayout.getChildCount() - 1;
}
View nextView = linearLayout.getChildAt(lastPosition);//将要滑动到的view

Log.i("viewnull", temp + "   currentView-->" + currentView + "----------" + lastPosition + "     nextView-->" + nextView);

int width = (currentView.getWidth() + nextView.getWidth()) / 2;//计算两view之间的总的滑动距离
return Math.round(width * offset);//此次滑动的距离
}

private void move(int x) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) lineView.getLayoutParams();
if (whichOne == 0) {//判断   一次完整的滑动   是否结束      一次滑动结束后 whichOne=0
if (positiveDirection) {
if (params.leftMargin % screenWidth > screenWidth / 2 && horizontalScrollView.canScrollHorizontally(1)) {//游标在右半边且recyclerView可以右滑 则滑动recyclerView
whichOne = whichOne | 1;
} else {
whichOne = whichOne | 2;
}
} else {
if (params.leftMargin % screenWidth < screenWidth / 2 && horizontalScrollView.canScrollHorizontally(-1)) {//游标在左半边且recyclerView可以左滑 则滑动recyclerView
whichOne = whichOne | 1;
} else {
whichOne = whichOne | 2;
}
}
}

if (whichOne == 1) {
horizontalScrollView.scrollBy(x - lastx, 0);//因为scrollBy的滑动是累加的,所以必须减去上次滑动的距离,实际移动的距离为差值  exp:第一次滑动1 第二次滑动2,第二次滑动3,scrollBy会滑动6,但实际我只想滑动3
lastx = x;
} else {
params.leftMargin = lineViewMarginLeft + x;
lineView.setLayoutParams(params);
}
}

//GuideSlideView 滑动联动ViewPager
private void setScrollViewPager(float length) {
viewPager.scrollBy(-Math.round(length), 0);
}


同样的重点在onPageScrolled回调中获取位置和偏移量,然后调用setScroll,有点不同的是每个ViewHolder都有自己的游标,滑动结束后,滑动到具体位置时需要控制ViewHolder和满屏跑游标的显示状态。

还有一点不同的就是对HorizontalScrollView设置了OnTouchListener,这是为了满足多触点的需求,当ViewPager滑到一半,另一个触点拖动引导条时,ViewPager会随之滑动。

看过之前一篇文章的话,这里针对滑动的逻辑并没有太大出入,只是用HorizontalScrollView替换了RecyclerView

效果



截图可能不太清楚,当快速滑动时,光标会有闪烁现象

缺陷

控件没有复用

快速滑动时,光标会闪

为了解决上述问题见与ViewPager完美结合的顺滑引导条系列之三
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  viewpager 索引