Android 实现锚点定位思路详解

 更新时间:2018年07月23日 08:31:06   作者:程序猿tx  
本篇文章就使用tablayout、scrollview来实现android锚点定位的功能。通过<a href="#head" rel="external nofollow" > 去设置页面内锚点定位跳转。具体实现思路大家跟随脚本之家小编一起通过本文看下吧

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

本篇文章就使用tablayout、scrollview来实现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);
 }
}

布局文件里 tablayout 和 CustomScrollView,内容暂时使用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

总结

以上所述是小编给大家介绍的Android 实现锚点定位思路详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

相关文章

  • Android进度条控件progressbar使用方法详解

    Android进度条控件progressbar使用方法详解

    这篇文章主要为大家详细介绍了Android进度条控件progressbar的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • Android实现定制返回按钮动画效果的方法

    Android实现定制返回按钮动画效果的方法

    这篇文章主要介绍了Android实现定制返回按钮动画效果的方法,涉及Android控件及动画的相关操作技巧,需要的朋友可以参考下
    2016-02-02
  • Android利用MPAndroidChart绘制曲线图表的基础教程

    Android利用MPAndroidChart绘制曲线图表的基础教程

    最近在项目中要用到曲线图,于是在网上找了很多很多,有AChartengine,MPAndroidChart,helloChart等等,我还用过基于html5的jsChart来做过,不过最终还是选择了MPAndroidChart来做本文介绍了Android利用MPAndroidChart绘制曲线图表的基础教程,需要的朋友可以参考下。
    2018-03-03
  • 深入理解Android MD5数据加密

    深入理解Android MD5数据加密

    在Android中需要对各种数据进行加密的操作,比如用户短信备份的数据加密、用户账户登陆的密码加密以及应用于服务器连接传递重要数据的加密,用处非常的多,所以今天来总结一下MD5加密算法。
    2016-09-09
  • Android图片识别应用详解

    Android图片识别应用详解

    这篇文章主要为大家详细介绍了Android图片识别的应用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • android仿微信表情雨下落效果的实现方法

    android仿微信表情雨下落效果的实现方法

    这篇文章主要给大家介绍了关于android仿微信表情雨下落效果的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09
  • Android开发实现各种图形绘制功能示例

    Android开发实现各种图形绘制功能示例

    这篇文章主要介绍了Android开发实现各种图形绘制功能,结合实例形式分析了Android图形绘制常用的组件、函数使用方法及相关注意事项,需要的朋友可以参考下
    2017-09-09
  • Flutter实现微信朋友圈功能

    Flutter实现微信朋友圈功能

    这篇文章主要为大家详细介绍了Flutter实现微信朋友圈功,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • Android开发双向滑动选择器范围SeekBar实现

    Android开发双向滑动选择器范围SeekBar实现

    这篇文章主要为大家介绍了Android开发双向滑动范围选择器SeekBar实现,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 使用Win10+Android+夜神安卓模拟器,搭建ReactNative开发环境

    使用Win10+Android+夜神安卓模拟器,搭建ReactNative开发环境

    今天小编就为大家分享一篇关于使用Win10+Android+夜神安卓模拟器,搭建ReactNative开发环境,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10

最新评论