Android 仿支付宝城市服务栏目tab选择滑动子View效果

code小生,一个专注 Android 领域的技术分享平台

作者:不会飞的小猪
链接:https://www.jianshu.com/p/1646571b497c
声明:本文是 不会飞的小猪 原创授权,转载等请联系作者获得授权。

一. 图示

640支付宝效果 640实现的效果

二. 思路讲解

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.TabLayout
           android:id="@+id/tabLayout"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">
</android.support.design.widget.TabLayout>

       <FrameLayout
           android: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">


       <LinearLayout
           android: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, 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() {
           @Override
           public boolean onTouch(View v, MotionEvent event) {
               Log.d(TAG,"wrapperFl onTouch");
               tabInterceptTouchEventTag = true;//让tab来处理滑动
               return false;
           }
       });
       tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
           @Override
           public 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;
               }
           }

           @Override
           public void onTabUnselected(TabLayout.Tab tab) {

           }

           @Override
           public void onTabReselected(TabLayout.Tab tab) {

           }
       });
       scrollView.setScrollViewListener(this);
       scrollView.setOnTouchListener(new View.OnTouchListener() {
           @Override
           public 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

640

仿某某效果

「DragMoreScrollView」一种相册交互效果的实现

Android 下实现高效的模糊效果

玩转仿探探卡片式滑动效果

猜你喜欢

转载自blog.csdn.net/h176nhx7/article/details/80575796
今日推荐