Android 整个屏幕可滑动,tab,viewpage是列表,tab不锁在顶
页面整体可滑动,包括顶部黑色控件、Tab 和列表。
步骤 1:主布局文件(activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
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:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 顶部黑色控件 -->
<View
android:id="@+id/topView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@android:color/black"/>
<!-- TabLayout -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/black"
app:tabSelectedTextColor="@color/black"
app:tabTextColor="@color/gray"/>
<!-- ViewPager2 -->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
步骤 2:MainActivity实现
public class MainActivity extends AppCompatActivity {
private ViewPager2 viewPager;
private TabLayout tabLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化视图
viewPager = findViewById(R.id.viewPager);
tabLayout = findViewById(R.id.tabLayout);
// 设置ViewPager2适配器
setupViewPager();
// 联动Tab和ViewPager2
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
tab.setText("Tab " + (position + 1));
}).attach();
}
private void setupViewPager() {
// 创建Fragment列表
List<Fragment> fragments = Arrays.asList(
new ListFragment(),
new ListFragment()
);
// 设置适配器
viewPager.setAdapter(new FragmentStateAdapter(this) {
@NonNull
@Override
public Fragment createFragment(int position) {
return fragments.get(position);
}
@Override
public int getItemCount() {
return fragments.size();
}
});
}
}
步骤 3:列表Fragment实现(ListFragment.java)
public class ListFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
RecyclerView recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(new ListAdapter());
return view;
}
static class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_list, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.textView.setText("Item " + (position + 1));
}
@Override
public int getItemCount() {
return 50; // 模拟50个列表项
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textItem);
}
}
}
}
步骤 4:布局文件补充
fragment_list.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
item_list.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textItem"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center"
android:textSize="18sp"/>
遇到的问题:ListFragment()的列表高度只有300,ClassifyListFragment有1000,NestedScrollView在ClassifyListFragment滑动到1000的时候,再切换回ListFragment()后,ListFragment()的高度变高了很多,多出的是空白高度
处理方式:
重新计算布局
确保在 ListFragment 中处理布局高度,尤其是在其 onResume() 方法中:
@Override
public void onResume() {
super.onResume();
if (listSize != 0) {
if (getContext() != null) {
// 重新计算布局高度
View view = getView();
if (view != null) {
// 如果 ListFragment 在显示时,手动更新其布局高度
view.post(() -> {
int height = getContext().getResources().getDimensionPixelSize(R.dimen.test_tab1_height) * listSize;
view.getLayoutParams().height = height;
view.requestLayout();
});
}
}
}
}
以下是我的布局
fragment_test_roll
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
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:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="206dp"
android:src="@mipmap/bg_test_top" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_gravity="bottom"
android:layout_marginStart="14dp"
android:layout_marginTop="14dp"
android:layout_marginEnd="14dp"
android:clipChildren="false">
<ImageView
android:layout_width="match_parent"
android:layout_height="54dp"
android:scaleType="fitXY"
android:src="@mipmap/bg_text_sroll" />
<com.jiuhong.mbtirgtest.ui.customView.TwoLineMarqueeView
android:id="@+id/marqueeView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp" />
</FrameLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="226dp"
android:layout_marginTop="17dp"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_hot_test_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingStart="14dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_hot_test_one"
android:layout_width="161dp"
android:layout_height="103dp"
android:layout_marginBottom="10dp"
android:scaleType="fitXY" />
<ImageView
android:id="@+id/iv_hot_test_two"
android:layout_width="161dp"
android:layout_height="103dp"
android:scaleType="fitXY" />
</LinearLayout>
<ImageView
android:id="@+id/iv_hot_test_three"
android:layout_width="168dp"
android:layout_height="226dp"
android:scaleType="fitXY"
android:layout_marginStart="10dp"
android:layout_gravity="bottom"
android:layout_marginEnd="5dp"/>
</LinearLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="200dp"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:tabIndicatorColor="#FF5449C8"
app:tabGravity="fill"
app:tabTextColor="#FF333333"
app:tabSelectedTextColor="#FF333333"
app:tabTextAppearance="@style/CustomTabTextAppearance"
app:tabMode="fixed"
android:layout_marginStart="14dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="10dp"/>
<!-- ViewPager2 -->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="13dp"
android:paddingEnd="13dp"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
TestRollFragment
package com.jiuhong.mbtirgtest.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.jiuhong.mbtirgtest.R;
import com.jiuhong.mbtirgtest.common.EventKey;
import com.jiuhong.mbtirgtest.common.Global;
import com.jiuhong.mbtirgtest.handle.MyUriHandler;
import com.jiuhong.mbtirgtest.ui.customView.TwoLineMarqueeView;
import com.jiuhong.mbtirgtest.util.GlideUtils;
import com.songcha.library_business.helper.UMengHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class TestRollFragment extends Fragment {
private ViewPager2 viewPager;
private TabLayout tabLayout;
TwoLineMarqueeView marqueeView;
ImageView iv_hot_test_one, iv_hot_test_two, iv_hot_test_three;
String hotTopUrlOne = "";
String hotTopUrlTwo = "";
String hotTopUrlThree = "";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 返回 Fragment 的布局
View rootView = inflater.inflate(R.layout.fragment_test_roll, container, false);
// 初始化视图
viewPager = rootView.findViewById(R.id.viewPager);
tabLayout = rootView.findViewById(R.id.tabLayout);
// 设置ViewPager2适配器
setupViewPager();
// 联动Tab和ViewPager2
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
tab.setText("Tab " + (position + 1));
}).attach();
marqueeView = rootView.findViewById(R.id.marqueeView);
iv_hot_test_one = rootView.findViewById(R.id.iv_hot_test_one);
iv_hot_test_two = rootView.findViewById(R.id.iv_hot_test_two);
iv_hot_test_three = rootView.findViewById(R.id.iv_hot_test_three);
//设置滚动文字
setMarqueeText();
iv_hot_test_one.setOnClickListener(v -> {
MyUriHandler.handle(hotTopUrlOne);
postKey("top");
});
iv_hot_test_two.setOnClickListener(v -> {
MyUriHandler.handle(hotTopUrlTwo);
postKey("bottom");
});
iv_hot_test_three.setOnClickListener(v -> {
MyUriHandler.handle(hotTopUrlThree);
postKey("right");
});
//顶部热门测评
setHotTest();
return rootView;
}
private void postKey(String position) {
if (getContext() != null) {
HashMap<String, Object> map = new HashMap<>();
map.put("position", position);
UMengHelper.onUMEvent(getContext(), EventKey.CLICK_HOME_FLOAT, map);
}
}
private void setMarqueeText() {
// 设置新的文本列表
List<String> texts = new ArrayList<>();
if (!Global.textRollModelList.isEmpty()) {
for (int i = 0; i < Global.textRollModelList.size(); i++) {
texts.add(Global.textRollModelList.get(i).getText());
}
marqueeView.setTexts(texts);
// 设置滚动速度
marqueeView.setSpeed(1.5f);
}
}
private void setupViewPager() {
// 创建Fragment列表
List<Fragment> fragments = Arrays.asList(
new ListFragment(),
ClassifyListFragment.newInstance(3)
);
// 设置适配器
viewPager.setAdapter(new FragmentStateAdapter(this) {
@NonNull
@Override
public Fragment createFragment(int position) {
return fragments.get(position);
}
@Override
public int getItemCount() {
return fragments.size();
}
});
}
private void setHotTest() {
for (int i = 0; i < Global.hotTestLists.size(); i++) {
if (getActivity() != null) {
if (!Global.hotTestLists.isEmpty()) {
GlideUtils.loadImageNoCache(getActivity(), Global.hotTestLists.get(0).getImage(), iv_hot_test_one);
// tv_hot_test_one.setText(Global.hotTestLists.get(0).getTitle());
hotTopUrlOne = Global.hotTestLists.get(0).getUri();
}
if (Global.hotTestLists.size() > 1) {
GlideUtils.loadImageNoCache(getActivity(), Global.hotTestLists.get(1).getImage(), iv_hot_test_two);
// tv_hot_test_two.setText(Global.hotTestLists.get(1).getTitle());
hotTopUrlTwo = Global.hotTestLists.get(1).getUri();
}
if (Global.hotTestLists.size() > 2) {
GlideUtils.loadImageNoCache(getActivity(), Global.hotTestLists.get(2).getImage(), iv_hot_test_three);
// tv_hot_test_three.setText(Global.hotTestLists.get(2).getTitle());
hotTopUrlThree = Global.hotTestLists.get(2).getUri();
}
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
fragment_list
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
ListFragment
package com.jiuhong.mbtirgtest.ui.fragment;
import static android.content.ContentValues.TAG;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import com.jiuhong.mbtirgtest.R;
import com.jiuhong.mbtirgtest.common.EventKey;
import com.jiuhong.mbtirgtest.common.Global;
import com.jiuhong.mbtirgtest.handle.MyUriHandler;
import com.jiuhong.mbtirgtest.httpUtil.HttpUtil;
import com.jiuhong.mbtirgtest.httpUtil.api;
import com.jiuhong.mbtirgtest.model.GetTestModel;
import com.jiuhong.mbtirgtest.ui.adapter.MainTabAdapter;
import com.songcha.library_business.helper.UMengHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ListFragment extends Fragment {
RecyclerView recyclerView;
int listSize = 0;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
recyclerView = view.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
getHttpData();
recyclerView.setNestedScrollingEnabled(false);
return view;
}
private void getHttpData() {
//获取签到的数量
Map<String, String> extraParams = new HashMap<>();
HttpUtil.sendGetRequest(getContext(), api.getMbttMoney, new HttpUtil.HttpCallback() {
@Override
public void onSuccess(String responseBody) {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
Log.d("测试列表", responseBody);
Gson gson = new Gson();
List<GetTestModel.DataBean> finalData = new ArrayList<>();
GetTestModel getSignNumModel = gson.fromJson(responseBody, GetTestModel.class);
for (int i = 0; i < Global.testH5Url.size(); i++) {
for (int a = 0; a < getSignNumModel.getData().size(); a++) {
if (Global.testH5Url.get(i).getVersion().equals(getSignNumModel.getData().get(a).getVersion())) {
if (Global.testH5Url.get(i).getBanner() != null && !Global.testH5Url.get(i).getBanner().isEmpty()) {
//使用配置的banner图
getSignNumModel.getData().get(a).setImg(Global.testH5Url.get(i).getBanner());
}
if (Global.testH5Url.get(i).getTitle() != null && !Global.testH5Url.get(i).getTitle().isEmpty()) {
//使用配置的标题
getSignNumModel.getData().get(a).setDescription(Global.testH5Url.get(i).getTitle());
}
if (Global.testH5Url.get(i).getDesc() != null && !Global.testH5Url.get(i).getDesc().isEmpty()) {
//描述
getSignNumModel.getData().get(a).setDescriptionExplain(Global.testH5Url.get(i).getDesc());
}
//version相同
if (Global.testH5Url.get(i).getSort() != 0) {
//sort不为0
if ((getSignNumModel.getData().get(a).getSign().equals("10085")) && getSignNumModel.getData().get(a).getStage() == 3) {
finalData.add(getSignNumModel.getData().get(a));
}
}
}
}
}
listSize = finalData.size();
Log.d("", "" + finalData);
MainTabAdapter adapter = new MainTabAdapter(finalData, new MainTabAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
GetTestModel.DataBean item = finalData.get(position);
for (int i = 0; i < Global.testH5Url.size(); i++) {
if (item.getVersion().equals(Global.testH5Url.get(i).getVersion())) {
MyUriHandler.handle(Global.testH5Url.get(i).getUri());
}
}
if (getContext() != null) {
HashMap<String, Object> map = new HashMap<>();
map.put("version", item.getVersion());
UMengHelper.onUMEvent(getContext(), EventKey.CLICK_DAILY_TEST_ITEM, map);
}
}
});
recyclerView.setAdapter(adapter);
});
}
}
@Override
public void onFailure(Exception e) {
// 请求失败,处理错误
Log.e(TAG, "Error123: " + e.getMessage(), e);
}
}, extraParams);
}
@Override
public void onResume() {
super.onResume();
if (listSize != 0) {//拿到数据才计算,第一次进来是没有数据的,不重新计算
// 重新计算布局高度
View view = getView();
if (view != null) {
// 如果 ListFragment 在显示时,手动更新其布局高度
view.post(() -> {
int height = getContext().getResources().getDimensionPixelSize(R.dimen.test_tab1_height) * listSize;
view.getLayoutParams().height = height;
view.requestLayout();
});
}
}
}
}