在浏览网页的时候,如果网页内容过长,添加网页内部导航会增加用户体验,也就是添加锚点。
这里是用 TabLayout+ScrollView 为页面添加锚点,实现仿html页面导航功能。
先顺一下思路,2点功能:
这里需要考虑一个问题:如果点击TabLayout条目,内容区域ScrollView发生改变,这种状态下ScrollView滑动是不会导致TabLayout条目发生改变的。
1、TabLayout的选中监听
mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
int position = tab.getPosition();
// 根据点击的位置,使ScrollView 滑动到对应区域
......
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
2、ScrollView 的滑动监听
sv_bodyContainer.setScrollViewListener(new ScrollChangedScrollView.ScrollViewListener() {
@Override
public void onScrollChanged(ScrollView scrollView, int x, int y, int oldx, int oldy) {
// 根据滑动到的当前位置,改变TabLayout的选中位置
}
@Override
public void onScrollStop(boolean isStop) {
}
});
3、获得ScrollView滑动距离
int scrollY = scrollView.getScrollY();
4、TabLayout 滑动到指定位置
mTabLayout.setScrollPosition(int position, float positionOffset, boolean updateSelectedText);
5、区分好两个操作动作:1. ScrollView 滚动,改变TabLayout ;2. 点击TabLayout 标签,ScrollView 发生滑动。避免当TabLayout 点击条目的时候,因ScrollView 发生滑动,同时又导致TabLayout改变的循环情况。可用一个标记位来标明当前的动作是由TabLayout 点击条目发起还是ScrollView 手动滑动发起。
6、如果ScrollView是在一个内容模块中滑动,要避免重复调用TabLyout的滑动。
布局文件 activity_tab_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_tab_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.skx.tomike.activity.TabLayoutActivity">
<android.support.design.widget.TabLayout
android:id="@+id/anchor_tagContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#a930c3a6"
app:tabIndicatorColor="@color/skx_323232"
app:tabMode="scrollable"
app:tabSelectedTextColor="@color/skx_323232"
app:tabTextColor="@color/skx_878787" />
<com.skx.tomike.customview.ScrollChangedScrollView
android:id="@+id/anchor_bodyContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/anchor_tagContainer">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="240dp"
android:scaleType="fitXY"
android:src="@drawable/image_07" />
<TextView
android:id="@+id/tv_1"
android:layout_width="match_parent"
android:layout_height="486dp"
android:background="@color/skx_33ff4081"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_2"
android:layout_width="match_parent"
android:layout_height="420dp"
android:background="@color/skx_2cb298"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_3"
android:layout_width="match_parent"
android:layout_height="630dp"
android:background="#568463"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_4"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="@color/skx_2c3e50"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_5"
android:layout_width="match_parent"
android:layout_height="420dp"
android:background="@color/skx_4dbbcf"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_6"
android:layout_width="match_parent"
android:layout_height="320dp"
android:background="@color/skx_007aff"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_7"
android:layout_width="match_parent"
android:layout_height="503dp"
android:background="@color/skx_7f000000"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_8"
android:layout_width="match_parent"
android:layout_height="272dp"
android:background="@color/skx_85f3f3f3"
android:gravity="center"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_9"
android:layout_width="match_parent"
android:layout_height="338dp"
android:background="@color/skx_959ea7"
android:gravity="center"
android:textSize="36sp" />
LinearLayout>
com.skx.tomike.customview.ScrollChangedScrollView>
RelativeLayout>
Acitivity 代码,注释得也算详细了
public class TabLayoutActivity extends SkxBaseActivity {
private TabLayout tab_tagContainer;
private ScrollChangedScrollView sv_bodyContainer;
private TextView tv_1;
private TextView tv_2;
private TextView tv_3;
private TextView tv_4;
private TextView tv_5;
private TextView tv_6;
private TextView tv_7;
private TextView tv_8;
private TextView tv_9;
// 头部导航标签
private String[] navigationTag = {"图片", "啤酒", "饮料", "矿泉水", "瓜子", "花生", "八宝粥", "泡面", "鸡爪", "火腿肠"};
/**
* 是否是ScrollView主动动作
* false:是ScrollView主动动作
* true:是TabLayout 主动动作
*/
private boolean tagFlag = false;
/**
* 用于切换内容模块,相应的改变导航标签,表示当一个所处的位置
*/
private int lastTagIndex = 0;
/**
* 用于在同一个内容模块内滑动,锁定导航标签,防止重复刷新标签
* true: 锁定
* false ; 没有锁定
*/
private boolean content2NavigateFlagInnerLock = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initializeView();
refreshView();
installListener();
}
@Override
public void initializeView() {
super.initializeView();
setContentView(R.layout.activity_tab_layout);
tab_tagContainer = (TabLayout) findViewById(R.id.anchor_tagContainer);
sv_bodyContainer = (ScrollChangedScrollView) findViewById(R.id.anchor_bodyContainer);
tv_1 = (TextView) findViewById(R.id.tv_1);
tv_2 = (TextView) findViewById(R.id.tv_2);
tv_3 = (TextView) findViewById(R.id.tv_3);
tv_4 = (TextView) findViewById(R.id.tv_4);
tv_5 = (TextView) findViewById(R.id.tv_5);
tv_6 = (TextView) findViewById(R.id.tv_6);
tv_7 = (TextView) findViewById(R.id.tv_7);
tv_8 = (TextView) findViewById(R.id.tv_8);
tv_9 = (TextView) findViewById(R.id.tv_9);
}
@Override
public void refreshView() {
super.refreshView();
tv_1.setText(navigationTag[1]);
tv_2.setText(navigationTag[2]);
tv_3.setText(navigationTag[3]);
tv_4.setText(navigationTag[4]);
tv_5.setText(navigationTag[5]);
tv_6.setText(navigationTag[6]);
tv_7.setText(navigationTag[7]);
tv_8.setText(navigationTag[8]);
tv_9.setText(navigationTag[9]);
// 添加页内导航标签
for (String item : navigationTag) {
tab_tagContainer.addTab(tab_tagContainer.newTab().setText(item));
}
}
@Override
public void installListener() {
super.installListener();
sv_bodyContainer.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//表明当前的动作是由 ScrollView 触发和主导
if (event.getAction() == MotionEvent.ACTION_DOWN) {
tagFlag = true;
}
return false;
}
});
sv_bodyContainer.setScrollViewListener(new ScrollChangedScrollView.ScrollViewListener() {
@Override
public void onScrollChanged(ScrollView scrollView, int x, int y, int oldx, int oldy) {
scrollRefreshNavigationTag(scrollView);
}
@Override
public void onScrollStop(boolean isStop) {
}
});
tab_tagContainer.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
//表明当前的动作是由 TabLayout 触发和主导
tagFlag = false;
// 根据点击的位置,使ScrollView 滑动到对应区域
int position = tab.getPosition();
// 计算点击的导航标签所对应内容区域的高度
int targetY = 0;
switch (position) {
case 0:
break;
case 1:
targetY = tv_1.getTop();
break;
case 2:
targetY = tv_2.getTop();
break;
case 3:
targetY = tv_3.getTop();
break;
case 4:
targetY = tv_4.getTop();
break;
case 5:
targetY = tv_5.getTop();
break;
case 6:
targetY = tv_6.getTop();
break;
case 7:
targetY = tv_7.getTop();
break;
case 8:
targetY = tv_8.getTop();
break;
case 9:
targetY = tv_9.getTop();
break;
default:
break;
}
// 移动到对应的内容区域
sv_bodyContainer.smoothScrollTo(0, targetY + 5);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
/**
* 内容区域滑动刷新导航标签
*
* @param scrollView 内容模块容器
*/
private void scrollRefreshNavigationTag(ScrollView scrollView) {
if (scrollView == null) {
return;
}
// 获得ScrollView滑动距离
int scrollY = scrollView.getScrollY();
// 确定ScrollView当前展示的顶部内容属于哪个内容模块
if (scrollY > tv_9.getTop()) {
refreshContent2NavigationFlag(9);
} else if (scrollY > tv_8.getTop()) {
refreshContent2NavigationFlag(8);
} else if (scrollY > tv_7.getTop()) {
refreshContent2NavigationFlag(7);
} else if (scrollY > tv_6.getTop()) {
refreshContent2NavigationFlag(6);
} else if (scrollY > tv_5.getTop()) {
refreshContent2NavigationFlag(5);
} else if (scrollY > tv_4.getTop()) {
refreshContent2NavigationFlag(4);
} else if (scrollY > tv_3.getTop()) {
refreshContent2NavigationFlag(3);
} else if (scrollY > tv_2.getTop()) {
refreshContent2NavigationFlag(2);
} else if (scrollY > tv_1.getTop()) {
refreshContent2NavigationFlag(1);
} else {
refreshContent2NavigationFlag(0);
}
}
/**
* 刷新标签
*
* @param currentTagIndex 当前模块位置
*/
private void refreshContent2NavigationFlag(int currentTagIndex) {
// 上一个位置与当前位置不一致是,解锁内部锁,是导航可以发生变化
if (lastTagIndex != currentTagIndex) {
content2NavigateFlagInnerLock = false;
}
if (!content2NavigateFlagInnerLock) {
// 锁定内部锁
content2NavigateFlagInnerLock = true;
// 动作是由ScrollView触发主导的情况下,导航标签才可以滚动选中
if (tagFlag) {
tab_tagContainer.setScrollPosition(currentTagIndex, 0, true);
}
}
lastTagIndex = currentTagIndex;
}
}
监听ScrollView 的滑动状态,滑动停止。
package com.skx.tomike.customview;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;
/**
* @author shiguotao
*
* 滑动监听的ScrollView
*/
public class ScrollChangedScrollView extends ScrollView {
private ScrollViewListener scrollViewListener = null;
private int handlerWhatId = 65984;
private int timeInterval = 20;
private int lastY = 0;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == handlerWhatId) {
if (lastY == getScrollY()) {
if (scrollViewListener != null) {
scrollViewListener.onScrollStop(true);
}
} else {
if (scrollViewListener != null) {
scrollViewListener.onScrollStop(false);
}
handler.sendMessageDelayed(handler.obtainMessage(handlerWhatId, this), timeInterval);
lastY = getScrollY();
}
}
}
};
public ScrollChangedScrollView(Context context) {
super(context);
}
public ScrollChangedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollChangedScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
public interface ScrollViewListener {
/**
* 滑动监听
*
* @param scrollView ScrollView控件
* @param x x轴坐标
* @param y y轴坐标
* @param oldx 上一个x轴坐标
* @param oldy 上一个y轴坐标
*/
void onScrollChanged(ScrollView scrollView, int x, int y, int oldx, int oldy);
/**
* 是否滑动停止
*
* @param isScrollStop true:滑动停止;false:未滑动停止
*/
void onScrollStop(boolean isScrollStop);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_UP) {
handler.sendMessageDelayed(handler.obtainMessage(handlerWhatId, this), timeInterval);
}
return super.onTouchEvent(ev);
}
}