置顶TabLayout配合动态高度ViewPager效果实现

先看效果图(gif录制的有点问题…看个大概吧):

思路:
1、动态高度的ViewPager配合TabLayout使用
2、滑动时根据滑动的位置让Header的TabLayout显示或隐藏
3、滑动后根据当前滑动的位置让ScrollView滑动到指定的位置(ViewPager布局位置)

主页面布局代码


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.v_xuyan.androidtest.TabLayoutActivity">

    <ScrollView
        android:id="@+id/main_scroller"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            
            <View
                android:background="#00FF00"
                android:layout_width="match_parent"
                android:layout_height="70dp"/>

            <FrameLayout
                android:id="@+id/main_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

        LinearLayout>



    ScrollView>

    
    <com.example.v_xuyan.androidtest.view.WrapWidthTabLayout
        android:id="@+id/main_tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFFFFFF"
        android:visibility="gone"
        tools:visibility="visible"/>



RelativeLayout>

ContentFragment主布局代码


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    
    
    <View
        android:id="@+id/head_view"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:background="@color/colorAccent"/>


    
    <com.example.v_xuyan.androidtest.view.WrapWidthTabLayout
        android:id="@+id/tab_indicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabIndicatorHeight="2dp"
        app:tabIndicatorColor="@color/colorPrimary"/>

    
    <com.example.v_xuyan.androidtest.view.DynamicHeightViewPager
        android:id="@+id/content_view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    
    <View
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="900dp"/>

LinearLayout>

主页面使用ContentFragment布局

public class TabLayoutActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_layout);

        getSupportFragmentManager().beginTransaction()
                .replace(R.id.main_content,new ContentFragment()).commit();


    }
}

ContentFragment代码:

public class ContentFragment extends Fragment {

    private View mRootView;
    private TabLayout mTabLayout;
    private DynamicHeightViewPager mViewPager;
    private List mFragments;
    private List mTitles;
    private TabAdapter mAdapter;
    private View mHeader;
    private TabLayout mTabHeader;
    private ScrollView mScrollView;
    private boolean isHeaderShow;
    private int mHeaderTop;//主布局TabLayout内容距离顶部的距离
    int toTopDistance = 0;//主布局距离顶部的距离

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        mRootView = inflater.inflate(R.layout.content_item, container, false);
        mTabLayout = mRootView.findViewById(R.id.tab_indicator);
        mViewPager = mRootView.findViewById(R.id.content_view_pager);
        mHeader = mRootView.findViewById(R.id.head_view);

        initData();

        mAdapter = new TabAdapter(getChildFragmentManager());

        mViewPager.setAdapter(mAdapter);

        mTabLayout.setupWithViewPager(mViewPager);

        //添加页面切换监听,实现动态改变ViewPager的高度,并将我们主布局置顶
        //to measure height realize dynamic height
        mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                if (position > mAdapter.getCount() || mViewPager == null || mHeader == null)
                    return;
                int num;//模拟主布局TabLayout的内容区高度
                if (position == 0) {
                    num = 50;
                } else {
                    num = 20;
                }
                MyContentFragment fragment = (MyContentFragment) mAdapter.getItem(position);
                mViewPager.measuredCurrentView(fragment.getContainer());//将rootView传递到ViewPager,测量高度

                ViewGroup.LayoutParams params = mHeader.getLayoutParams();
                params.height = (int) ViewUtils.dp2px(getActivity(), num);
                mHeader.setLayoutParams(params);

                // to scroll top let the ContentFragment's View
                //滑动ViewPager让主布局置顶
                int distance = mHeaderTop - mScrollView.getScrollY();
                mScrollView.scrollBy(0,distance);

                //if ContentFragment's View is in the top then
                //一开始直接主布局,没有主布局上面内容区,所以直接滚动到初始位置即可
                //mScrollView.fullScroll(View.FOCUS_UP);

            }
        });

        //coordinate the onPageSelected,the first show time is called
        //结合ViewPager页面滑动监听,页面滑动监听在第一次显示的时候不会触发,该监听器在页面Layout完成后会执行
        mViewPager.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                MyContentFragment fragment = (MyContentFragment) mAdapter.getItem(0);
                if (mViewPager != null && fragment != null && mHeader != null) {
                    mViewPager.measuredCurrentView(fragment.getContainer());
                    ViewGroup.LayoutParams params = mHeader.getLayoutParams();
                    params.height = (int) ViewUtils.dp2px(getActivity(), 50);
                    mHeader.setLayoutParams(params);//设置主布局TabLayout上布局的高度,模拟数据区大小
                }
                if (mViewPager != null && mViewPager.getViewTreeObserver() != null) {
                    mViewPager.getViewTreeObserver().removeOnGlobalLayoutListener(this);//移除,之后利用滑动监听实现
                }
            }
        });

        setupTabHeader();

        return mRootView;
    }

    private void setupTabHeader() {
        if (getActivity() != null && !getActivity().isFinishing()) {
            mTabHeader = getActivity().findViewById(R.id.main_tab_layout);

            if (mTabHeader != null && mViewPager != null) {
                mTabHeader.setupWithViewPager(mViewPager);
            }

            mScrollView = getActivity().findViewById(R.id.main_scroller);
            if (mScrollView != null) {
                mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
                    @Override
                    public void onScrollChanged() {
                        //通过ScrollView的scroll监听事件,对滑动距离的监听,来控制主页面TabLayout布局是否显示
                        updateHeaderView();
                    }
                });
            }
        }
    }

    // onScrollChange is before onCreateView
    //设置主页面的TabLayout是否可见
    private void updateHeaderView() {
        getTopDistance();
        int start = toTopDistance + mTabLayout.getTop();
        int end = toTopDistance + mViewPager.getBottom() - mTabHeader.getMeasuredHeight();//to dismiss | subtract mTabHeader's Height
        if (mScrollView.getScrollY() > start && mScrollView.getScrollY() < end) {
            mTabHeader.setVisibility(View.VISIBLE);
            isHeaderShow = true;
        }
        if (mScrollView.getScrollY() > end || mScrollView.getScrollY() < start) {
            mTabHeader.setVisibility(View.GONE);
            isHeaderShow = false;
        }
    }

    //我们获取距离顶部的距离
    private void getTopDistance() {
        //to get mRootView top
        if (getActivity() != null && !getActivity().isFinishing())
            toTopDistance = getActivity().findViewById(R.id.main_content).getTop();
        mHeaderTop = toTopDistance + mHeader.getTop();
    }

    private void initData() {
        mTitles = new ArrayList<>();
        mTitles.add("解锋镝");
        mTitles.add("有生之莲");

        mFragments = new ArrayList<>();
        MyContentFragment fragment1 = new MyContentFragment();
        Bundle bundle1 = new Bundle();
        bundle1.putInt("num", 1);
        fragment1.setArguments(bundle1);
        mFragments.add(fragment1);
        MyContentFragment fragment2 = new MyContentFragment();
        Bundle bundle2 = new Bundle();
        bundle2.putInt("num", 2);
        fragment2.setArguments(bundle2);
        mFragments.add(fragment2);
    }


    class TabAdapter extends FragmentPagerAdapter {

        TabAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragments.get(position);
        }

        @Override
        public int getCount() {
            return mFragments.size();
        }

        @Nullable
        @Override
        public CharSequence getPageTitle(int position) {
            return mTitles.get(position);
        }
    }

}

最后是MyContentFragment的布局和代码:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/item_content_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

LinearLayout>


item布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/inner_item_text"
        android:textSize="15sp"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

LinearLayout>

public class MyContentFragment extends Fragment {

    private ViewGroup mContainer;
    private List mList;
    private RecyclerView itemList;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view =  inflater.inflate(R.layout.content_item_inner,container,false);
        mContainer = (ViewGroup) view;

        itemList = view.findViewById(R.id.item_content_list);

        initData();

        itemList.setLayoutManager(new LinearLayoutManager(getActivity()));
        itemList.setAdapter(new MyAdapter());

        return view;
    }

    private void initData() {
        Bundle mBundle = getArguments();
        mList = new ArrayList<>();
        if (mBundle == null)  return;
        int num = mBundle.getInt("num");
        switch (num) {
            case 1:
                for (int i = 0; i < 5; i++) {
                    mList.add("醉古夫  " + i);
                }
                break;
            case 2:
                for (int i = 0; i < 10; i++) {
                    mList.add("折柳心斋 "  + i);
                }
                break;
        }
    }

    //获取rootView传递到ViewPager测量高度
    public ViewGroup getContainer(){
        return mContainer;
    }


    class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{

        @Override
        public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(getActivity()).inflate(R.layout.inner_item_content, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
            holder.item.setText(mList.get(position));
        }

        @Override
        public int getItemCount() {
            return mList.size();
        }

        class ViewHolder extends RecyclerView.ViewHolder{

            private TextView item;
            ViewHolder(View itemView) {
                super(itemView);
                item = itemView.findViewById(R.id.inner_item_text);
            }
        }

    }

}

中间有个坑,就是ViewPager布局在ScrollView中,
如果不是固定高度或者自定义的ViewPager重写onMeasure的话,
ViewPager的高度是0,和ScrollView内部机制有关,有时间研究研究

附上项目链接:github项目地址

以后尽可能的坚持每周末写一篇…道友监督哈!

你可能感兴趣的:(Android自定义)