仿支付宝城市服务栏目tab选择滑动子View效果 Android-code小生
code小生,一个专注 Android 领域的技术分享平台
作者:不会飞的小猪链接:https://www.jianshu.com/p/1646571b497c声明:本文是 不会飞的小猪 原创授权,转载等请联系作者获得授权。一. 图示支付宝效果实现的效果二. 思路讲解
TabLayout+ScrollView实现即可。每一个tab对应scrollview中包裹的一层布局,以上有4个tab,也就需要inflate 4个布局文件,用来表示每一层的样式内容。
1.页面加载完毕后,记住每一层父布局在Screen中所要滑动至顶部的距离Distance;
2.操作:
a. 当点击tab时:滑动scrollview该层的距离Distance;
b. 当滑动scrollview至对应的层时: 选定对应的tab。三. 代码
1. 布局代码
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.ganshenml.tabscrollviewdemo.MainActivity"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.design.widget.TabLayoutandroid:id="@+id/tabLayout"android:layout_width="match_parent"android:layout_height="wrap_content"></android.support.design.widget.TabLayout> <FrameLayoutandroid:id="@+id/wrapperFl"android:layout_width="match_parent"android:layout_height="match_parent"></FrameLayout> </FrameLayout> <com.ganshenml.tabscrollviewdemo.ObservableScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayoutandroid:id="@+id/containerLl"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"> </LinearLayout> </com.ganshenml.tabscrollviewdemo.ObservableScrollView></LinearLayout>
布局代码很简单,可能会有的疑问点有:
a. wrapperFl是用来干啥的?
b. ObservableScrollView又是个什么东西荣城名苑 ?
(后面说明)
2. 逻辑代码
public class MainActivity extends AppCompatActivity implements ObservableScrollView.ScrollViewListener { private static final String TAG = "MainActivity"; private FrameLayout wrapperFl; private TabLayout tabLayout; private ObservableScrollView scrollView; private LinearLayout containerLl; private boolean firstAlreadyInflated = true; private ViewGroup firstFloorVg; private ViewGroup secondFloorVg; private ViewGroup thirdFloorVg; private ViewGroup fourthFloorVg; private int secondFloorVgPositionDistance;//第二层滑动至顶部的距离 private int thirdFloorVgPositionDistance; private int fourthFloorVgPositionDistance; private int currentPosition = 0; private boolean tabInterceptTouchEventTag = true;//标志位,用来区分是点击了tab还是手动滑动scrollview @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initListeners(); } private void initViews() { wrapperFl = (FrameLayout) findViewById(R.id.wrapperFl); tabLayout = (TabLayout) findViewById(R.id.tabLayout); scrollView = (ObservableScrollView) findViewById(R.id.scrollView); containerLl = (LinearLayout) findViewById(R.id.containerLl); for (int i = 0; i < 4; i++) {tabLayout.addTab(tabLayout.newTab().setText("tab" + (i + 1))); } firstFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_first, null); secondFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_second,刺客信条2存档 null); thirdFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_third, null); fourthFloorVg = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.item_floor_fourth, null); containerLl.addView(firstFloorVg); containerLl.addView(secondFloorVg); containerLl.addView(thirdFloorVg); containerLl.addView(fourthFloorVg); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (firstAlreadyInflated) {//获取各层离screen顶部的位置以及计算滑动值相应顶部所需要的距离firstAlreadyInflated = false;int[] firstFloorVgPosition = new int[2];int[] secondFloorVgPosition = new int[2];int[] thirdFloorVgPosition = new int[2];int[] fourthFloorVgPosition = new int[2];firstFloorVg.getLocationOnScreen(firstFloorVgPosition);secondFloorVg.getLocationOnScreen(secondFloorVgPosition);thirdFloorVg.getLocationOnScreen(thirdFloorVgPosition);fourthFloorVg.getLocationOnScreen(fourthFloorVgPosition);int firstFloorVgPositionAnchor = firstFloorVgPosition[1];int secondFloorVgPositionAnchor = secondFloorVgPosition[1];int thirdFloorVgPositionAnchor = thirdFloorVgPosition[1];int fourthFloorVgPositionAnchor = fourthFloorVgPosition[1];Log.d(TAG, "第一层距离屏幕的距离是:" + firstFloorVgPosition[1]);Log.d(TAG, "第二层距离屏幕的距离是:" + secondFloorVgPosition[1]);Log.d(TAG, "第三层距离屏幕的距离是:" + thirdFloorVgPosition[1]);Log.d(TAG, "第四层距离屏幕的距离是:" + fourthFloorVgPosition[1]);secondFloorVgPositionDistance = secondFloorVgPositionAnchor - firstFloorVgPositionAnchor;thirdFloorVgPositionDistance = thirdFloorVgPositionAnchor - firstFloorVgPositionAnchor;fourthFloorVgPositionDistance = fourthFloorVgPositionAnchor - firstFloorVgPositionAnchor; } } private void initListeners() { wrapperFl.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v鹤山人才网, MotionEvent event) { Log.d(TAG,"wrapperFl onTouch"); tabInterceptTouchEventTag = true;//让tab来处理滑动 return false;} }); tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {@Overridepublic void onTabSelected(TabLayout.Tab tab) { currentPosition = tab.getPosition(); if(!tabInterceptTouchEventTag){//手动滑动页面时则不再次处理滑动 return; } scrollView.computeScroll(); switch (currentPosition) { case 0:scrollView.smoothScrollTo(0大众公园 , 0);break; case 1:scrollView.smoothScrollTo(0, secondFloorVgPositionDistance);break; case 2:scrollView.smoothScrollTo(0, thirdFloorVgPositionDistance);break; case 3:scrollView.smoothScrollTo(0, fourthFloorVgPositionDistance);break; default:break; }}@Overridepublic void onTabUnselected(TabLayout.Tab tab) {}@Overridepublic void onTabReselected(TabLayout.Tab tab) {} }); scrollView.setScrollViewListener(this); scrollView.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v军中绿花原唱, MotionEvent event) { Log.d(TAG,周盛俊杰 "scrollView onTouch"); tabInterceptTouchEventTag = false;//让scrollview处理滑动 return false;} }); } @Override public void onScrollChanged(ObservableScrollView scrollView, int x, int y百忍图, int oldx, int oldy) { if (tabInterceptTouchEventTag) {//让tab来处理滑动return; } Log.d(TAG, "当前scrollView的位置——>" + y); if (y < secondFloorVgPositionDistance) {if (currentPosition != 0) { scrollView.computeScroll(); tabLayout.getTabAt(0).select();} } else if (y < thirdFloorVgPositionDistance) {if (currentPosition != 1) { scrollView.computeScroll(); tabLayout.getTabAt(1).select();} } else if (y < fourthFloorVgPositionDistance) {if (currentPosition != 2) { scrollView.computeScroll(); tabLayout.getTabAt(2).select();} } else {if (currentPosition != 3) { scrollView.computeScroll(); tabLayout.getTabAt(3).select();} } }}
a. tabInterceptTouchEventTag 作为标志位是为了防止因scrollview触发了tab Selected从而再次引起scrollview滑动导致的滑动不流畅。
b. wrapperFl的存在则是要去给 tabInterceptTouchEventTag 赋值,因为TabLayout的Touch、Click、Focus等事件被消化掉了,无法在这些事件中监听到对应的值的变化,所以通过wrapperFl来进行Touch事件的监听雅马哈暴龙 。
c. ObservableScrollView 是继承自Scrollview末世刁民 ,新增和改变了其中的以下方法:
public void setScrollViewListener(ScrollViewListener scrollViewListener) { this.scrollViewListener = scrollViewListener; } @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 interface ScrollViewListener { void onScrollChanged(ObservableScrollView scrollView, int x小兵兼职吧 , int y淘气虎 , int oldx, int oldy); }
这样就可以在activity主界面中监听到scrollview的滑动事件,从而获取当前整个scrollview所处的位置,进而去判定是否需要选定对应的tab。
ps:如果要做成支付宝那样TabLayout上还有个TopView的内容样式,则可以参考之前写过的博客的做法:View滑动固定效果实现>>https://blog.csdn.net/ganshenml/article/details/78341223
最后,完整代码可以查看GitHub>>https://github.com/ganshenml/TabScrollViewDemo
仿某某效果
「DragMoreScrollView」一种相册交互效果的实现
Android 下实现高效的模糊效果
玩转仿探探卡片式滑动效果