相信做前端的都做过页面锚点定位的功能,通过<a href="#head"> 去设置页面内锚点定位跳转。

本篇文章就使用tablayoutscrollview来实现android锚点定位的功能。

效果图:

实现思路

1、监听scrollview滑动到的位置,tablayout切换到对应标签

2、tablayout各标签点击,scrollview可滑动到对应区域

自定义scrollview

因为我们需要监听到滑动过程中scrollview的滑动距离,自定义scrollview通过接口暴露滑动的距离。

public class CustomScrollView extends ScrollView {

    public Callbacks mCallbacks;

    public CustomScrollView(Context context) {
super(context);
} public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
} public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} public void setCallbacks(Callbacks callbacks) {
this.mCallbacks = callbacks;
} @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mCallbacks != null) {
mCallbacks.onScrollChanged(l, t, oldl, oldt);
}
}
//定义接口用于回调
public interface Callbacks {
void onScrollChanged(int x, int y, int oldx, int oldy);
} }

布局文件里 tablayoutCustomScrollView,内容暂时使用LinearLayout填充。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/colorPrimary"
app:tabMode="scrollable"
app:tabSelectedTextColor="@color/colorPrimary" /> <com.tabscroll.CustomScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true"> <LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"> </LinearLayout> </com.tabscroll.CustomScrollView> </LinearLayout>

数据模拟

数据模拟,动态添加scrollview内的内容,这里自定义了AnchorView当作每一块的填充内容。

private String[] tabTxt = {"客厅", "卧室", "餐厅", "书房", "阳台", "儿童房"};
//内容块view的集合
private List<AnchorView> anchorList = new ArrayList<>();
//判读是否是scrollview主动引起的滑动,true-是,false-否,由tablayout引起的
private boolean isScroll;
//记录上一次位置,防止在同一内容块里滑动 重复定位到tablayout
private int lastPos; //模拟数据,填充scrollview
for (int i = 0; i < tabTxt.length; i++) {
AnchorView anchorView = new AnchorView(this);
anchorView.setAnchorTxt(tabTxt[i]);
anchorView.setContentTxt(tabTxt[i]);
anchorList.add(anchorView);
container.addView(anchorView);
} //tablayout设置标签
for (int i = 0; i < tabTxt.length; i++) {
tabLayout.addTab(tabLayout.newTab().setText(tabTxt[i]));
}

定义变量标志isScroll,用于判断scrollview的滑动由谁引起的,避免通过点击tabLayout引起的scrollview滑动问题。

定义变量标志lastPos,当scrollview 在同一模块中滑动时,则不再去调用tabLayout.setScrollPosition刷新标签。

自定义的AnchorView

public class AnchorView extends LinearLayout {

    private TextView tvAnchor;
private TextView tvContent; public AnchorView(Context context) {
this(context, null);
} public AnchorView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
} public AnchorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
} private void init(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.view_anchor, this, true);
tvAnchor = view.findViewById(R.id.tv_anchor);
tvContent = view.findViewById(R.id.tv_content);
Random random = new Random();
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
tvContent.setBackgroundColor(Color.rgb(r, g, b));
} public void setAnchorTxt(String txt) {
tvAnchor.setText(txt);
} public void setContentTxt(String txt) {
tvContent.setText(txt);
}
}

实现

scrollview的滑动监听:

scrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//当由scrollview触发时,isScroll 置true
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isScroll = true;
}
return false;
}
}); scrollView.setCallbacks(new CustomScrollView.Callbacks() {
@Override
public void onScrollChanged(int x, int y, int oldx, int oldy) {
if (isScroll) {
for (int i = tabTxt.length - 1; i >= 0; i--) {
//根据滑动距离,对比各模块距离父布局顶部的高度判断
if (y > anchorList.get(i).getTop() - 10) {
setScrollPos(i);
break;
}
}
}
}
}); //tablayout对应标签的切换
private void setScrollPos(int newPos) {
if (lastPos != newPos) {
//该方法不会触发tablayout 的onTabSelected 监听
tabLayout.setScrollPosition(newPos, 0, true);
}
lastPos = newPos;
}

tabLayout的点击切换:

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
//点击标签,使scrollview滑动,isScroll置false
isScroll = false;
int pos = tab.getPosition();
int top = anchorList.get(pos).getTop();
scrollView.smoothScrollTo(0, top);
} @Override
public void onTabUnselected(TabLayout.Tab tab) { } @Override
public void onTabReselected(TabLayout.Tab tab) { }
});

至此效果出来了,但是

问题来了 可以看到当点击最后一项时,scrollView滑动到底部时并没有呈现出我们想要的效果,希望滑到最后一个时,全屏只有最后一块内容显示。

所以这里需要处理下最后一个view的高度,当不满全屏时,重新设置他的高度,通过计算让其撑满屏幕。

//监听判断最后一个模块的高度,不满一屏时让最后一个模块撑满屏幕
private ViewTreeObserver.OnGlobalLayoutListener listener; listener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int screenH = getScreenHeight();
int statusBarH = getStatusBarHeight(MainActivity.this);
int tabH = tabLayout.getHeight();
//计算内容块所在的高度,全屏高度-状态栏高度-tablayout的高度-内容container的padding 16dp
int lastH = screenH - statusBarH - tabH - 16 * 3;
AnchorView lastView = anchorList.get(anchorList.size() - 1);
//当最后一个view 高度小于内容块高度时,设置其高度撑满
if (lastView.getHeight() < lastH) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.height = lastH;
lastView.setLayoutParams(params);
}
container.getViewTreeObserver().removeOnGlobalLayoutListener(listener); }
};
container.getViewTreeObserver().addOnGlobalLayoutListener(listener);

这样就达到了预期的效果了。

写到这里,tablayout + scrollview的锚点定位成型了,在实际项目中,我们还可以使用tablayout + recyclerview 来完成同样的效果,后续的话会带来这样的文章。

这段时间自己在做一个小程序,包括数据爬取 + 后台 + 小程序的,可能要过段时间才能出来,主要是数据爬虫那边比较麻烦的...期待下!

详细代码见

github地址:https://github.com/taixiang/tabScroll

欢迎关注我的博客:https://blog.manjiexiang.cn/

更多精彩欢迎关注微信号:春风十里不如认识你

有个「佛系码农圈」,欢迎大家加入畅聊,开心就好!



过期了,可加我微信 tx467220125 拉你入群。

Android 实现锚点定位的更多相关文章

  1. Android tabLayout+recyclerView实现锚点定位

    原文链接:https://mp.weixin.qq.com/s/L3o2i3WTmg1ScXEYDS8YCg 在上一篇文章 Android 实现锚点定位中,我们介绍了tablayout+scrollV ...

  2. html中设置锚点定位的几种常见方法(#号定位)

    在html中设置锚点定位我知道的有几种方法,在此和大家分享一下: 1.使用id定位: <a href="#1F">锚点1</a> <div id=&q ...

  3. 【TRICK】解决锚点定位向下浮动Xpx问题

    1. 问题描述 页面滚动后,菜单栏会固定在页头,当锚点定位时,菜单会遮盖部分定位后的内容,所以需要在锚点定位后自动向下漂移Xpx. 2. 解决办法 a. 利用空div 占位,如下: <a hre ...

  4. 当锚点定位遇上position: fixed

    <!DOCTYPE html><html> <head> <title>当锚点定位遇上position: fixed</title> < ...

  5. 锚点定位,jquery定位到页面指定位置

    jquery锚点定位 $('body,html').animate({scrollTop: $('#ter1').offset().top}, 500);#ter1是你要定位的id对象,500是0.5 ...

  6. js实现锚点定位

    js实现锚点定位的原理是,算出定位的标签距离顶部的高度,点击触发标签,重新赋值滚动条的高度到达指定位置. <!DOCTYPE html> <html> <head> ...

  7. html锚点定位不准确问题

    问题描述 当顶部固定时,点击锚点,会跳转到锚点以下. <style> #one,#two,#three{ height: 500px; } #top{ position: fixed; h ...

  8. 微信小程序基于scroll-view实现锚点定位

    代码地址如下:http://www.demodashi.com/demo/14009.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...

  9. html 锚点定位

    在html中设置锚点定位我知道的有几种方法.在此和大家分享一下: 1.使用id定位: <a href="#1F" name="1F">锚点1< ...

随机推荐

  1. 带你一文了解Python中的运算符

    运算符 目标 算数运算符 比较(关系)运算符 逻辑运算符 赋值运算符 运算符的优先级 数学符号表链接:https://zh.wikipedia.org/wiki/数学符号表 01. 算数运算符 是完成 ...

  2. 数据库语句收藏(MySQL)

    概览 => MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性. =>关键字不区分大小写 => S ...

  3. 解决transition动画与display冲突的几种方法

    如demo(如果没有显示,请查看源地址http://jsfiddle.net/ihardcoder/HNduT/2/)所示,基本的效果是在点击“Translate”按钮后,蓝色区域透明度变为0,然后隐 ...

  4. django在关闭debug后,admin界面 及静态文件无法加载的解决办法

    当debug为true的时候,ALLOWED_HOSTS是跳过不管用的.所以这里需要将debug关掉,令debug=false,ALLOWED_HOSTS=[ '*' ]表示所有的主机都可以访问 开启 ...

  5. Docker 简述

    转自:https://cloud.tencent.com/developer/article/1354393 虚拟机和 docker 的区别,如下图: Image (镜像) 镜像不包含任何动态数据,其 ...

  6. python 闯关之路二(模块的应用)

    1.有如下字符串:n = "路飞学城"(编程题) - 将字符串转换成utf-8的字符编码的字节,再将转换的字节重新转换为utf-8的字符编码的字符串 - 将字符串转换成gbk的字符 ...

  7. SpringBoot(6) SpringBoot配置全局异常

    1.全局异常 @ControllerAdvice 如果是返回json数据 则用 RestControllerAdvice,就可以不加 @ResponseBody //捕获全局异常,处理所有不可知的异常 ...

  8. TensorFlow.js入门(一)一维向量的学习

    TensorFlow的介绍   TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理.Tensor(张量)意味着N维数组,Flow(流)意味着 ...

  9. Python爬虫之多线程下载豆瓣Top250电影图片

    爬虫项目介绍   本次爬虫项目将爬取豆瓣Top250电影的图片,其网址为:https://movie.douban.com/top250, 具体页面如下图所示:   本次爬虫项目将分别不使用多线程和使 ...

  10. nginx 禁止未绑定的域名访问

    nginx 禁止未绑定的域名访问 方法1: server { listen 80 default_server; #不指定 server_name, server_name 默认为"&quo ...