先看效果图(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项目地址
以后尽可能的坚持每周末写一篇…道友监督哈!