From ba4aec0e0893bbabf561e415d0ae9e0d24e1ef00 Mon Sep 17 00:00:00 2001 From: DelLevin-Home Date: Mon, 9 Feb 2026 22:05:05 +0800 Subject: [PATCH] 1111 --- .idea/vcs.xml | 6 ++ .../top/iletter/lvnote/BookListFragment.java | 65 ++++++++++++++ .../java/top/iletter/lvnote/FragmentHome.java | 85 ++++++++++++++++-- .../top/iletter/lvnote/FragmentProfile.java | 3 +- .../java/top/iletter/lvnote/MediaAdapter.java | 52 +++++++++++ .../top/iletter/lvnote/MediaListFragment.java | 89 +++++++++++++++++++ .../top/iletter/lvnote/MovieListFragment.java | 68 ++++++++++++++ .../java/top/iletter/lvnote/NoteAdapter.java | 53 +++++++++++ .../top/iletter/lvnote/NoteListFragment.java | 54 +++++++++++ .../main/res/layout/fragment_book_list.xml | 16 ++++ app/src/main/res/layout/fragment_home.xml | 49 ++++++---- .../main/res/layout/fragment_media_list.xml | 30 +++++++ .../main/res/layout/fragment_movie_list.xml | 16 ++++ .../main/res/layout/fragment_note_list.xml | 31 +++++++ app/src/main/res/layout/item_media.xml | 70 +++++++++++++++ app/src/main/res/layout/item_note.xml | 46 ++++++++++ app/src/main/res/values/strings.xml | 2 + 17 files changed, 705 insertions(+), 30 deletions(-) create mode 100644 .idea/vcs.xml create mode 100644 app/src/main/java/top/iletter/lvnote/BookListFragment.java create mode 100644 app/src/main/java/top/iletter/lvnote/MediaAdapter.java create mode 100644 app/src/main/java/top/iletter/lvnote/MediaListFragment.java create mode 100644 app/src/main/java/top/iletter/lvnote/MovieListFragment.java create mode 100644 app/src/main/java/top/iletter/lvnote/NoteAdapter.java create mode 100644 app/src/main/java/top/iletter/lvnote/NoteListFragment.java create mode 100644 app/src/main/res/layout/fragment_book_list.xml create mode 100644 app/src/main/res/layout/fragment_media_list.xml create mode 100644 app/src/main/res/layout/fragment_movie_list.xml create mode 100644 app/src/main/res/layout/fragment_note_list.xml create mode 100644 app/src/main/res/layout/item_media.xml create mode 100644 app/src/main/res/layout/item_note.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/BookListFragment.java b/app/src/main/java/top/iletter/lvnote/BookListFragment.java new file mode 100644 index 0000000..6497729 --- /dev/null +++ b/app/src/main/java/top/iletter/lvnote/BookListFragment.java @@ -0,0 +1,65 @@ +package top.iletter.lvnote; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +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; + +public class BookListFragment extends Fragment { + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_book_list, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + ViewPager2 viewPagerSecondary = view.findViewById(R.id.view_pager_secondary); + viewPagerSecondary.setAdapter(new SecondaryPagerAdapter(this)); + + TabLayout tabSecondary = requireActivity().findViewById(R.id.tab_secondary); + tabSecondary.setVisibility(View.VISIBLE); + + new TabLayoutMediator(tabSecondary, viewPagerSecondary, (tab, position) -> { + switch (position) { + case 0: tab.setText("已读"); break; + case 1: tab.setText("在读"); break; + case 2: tab.setText("想读"); break; + } + }).attach(); + } + + static class SecondaryPagerAdapter extends FragmentStateAdapter { + public SecondaryPagerAdapter(@NonNull Fragment fragment) { + super(fragment); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + return MediaListFragment.newInstance( + position == 0 ? "read" : + position == 1 ? "reading" : "plan_to_read" + ); + } + + @Override + public int getItemCount() { + return 3; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/FragmentHome.java b/app/src/main/java/top/iletter/lvnote/FragmentHome.java index 13bfcce..3b570c9 100644 --- a/app/src/main/java/top/iletter/lvnote/FragmentHome.java +++ b/app/src/main/java/top/iletter/lvnote/FragmentHome.java @@ -8,14 +8,28 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; 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; public class FragmentHome extends Fragment { + + private TabLayout tabMain; + private TabLayout tabSecondary; + private ViewPager2 viewPagerMain; + + // 三个主页面 + private final Fragment movieFragment = new MovieListFragment(); + private final Fragment bookFragment = new BookListFragment(); + private final Fragment noteFragment = new NoteListFragment(); + + @Nullable @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_home, container, false); } @@ -23,14 +37,67 @@ public class FragmentHome extends Fragment { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - TabLayout tabLayout = view.findViewById(R.id.tab_layout); - ViewPager2 viewPager = view.findViewById(R.id.view_pager); + tabMain = view.findViewById(R.id.tab_main); + tabSecondary = view.findViewById(R.id.tab_secondary); + viewPagerMain = view.findViewById(R.id.view_pager_main); - // 设置三标签:看过/正在看/准备看 - tabLayout.addTab(tabLayout.newTab().setText("看过")); - tabLayout.addTab(tabLayout.newTab().setText("正在看")); - tabLayout.addTab(tabLayout.newTab().setText("准备看")); + // 设置外层 ViewPager2 适配器 + viewPagerMain.setAdapter(new MainPagerAdapter(this)); - // TODO: 后续实现 ViewPager2 适配器 + // 绑定一级导航 + new TabLayoutMediator(tabMain, viewPagerMain, (tab, position) -> { + switch (position) { + case 0: + tab.setText("观影记"); + break; + case 1: + tab.setText("书摘"); + break; + case 2: + tab.setText("随笔"); + break; + } + }).attach(); + + // 一级 Tab 切换监听(控制二级导航显示/隐藏) + tabMain.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + // 随笔页面隐藏二级导航 + if (tab.getPosition() == 2) { + tabSecondary.setVisibility(View.GONE); + } else { + tabSecondary.setVisibility(View.VISIBLE); + } + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) {} + + @Override + public void onTabReselected(TabLayout.Tab tab) {} + }); + } + + // 外层 ViewPager2 适配器 + static class MainPagerAdapter extends FragmentStateAdapter { + public MainPagerAdapter(@NonNull Fragment fragment) { + super(fragment); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + switch (position) { + case 0: return new MovieListFragment(); + case 1: return new BookListFragment(); + default: return new NoteListFragment(); + } + } + + @Override + public int getItemCount() { + return 3; + } } } \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/FragmentProfile.java b/app/src/main/java/top/iletter/lvnote/FragmentProfile.java index ae98153..2c2d94e 100644 --- a/app/src/main/java/top/iletter/lvnote/FragmentProfile.java +++ b/app/src/main/java/top/iletter/lvnote/FragmentProfile.java @@ -12,7 +12,6 @@ import androidx.fragment.app.Fragment; public class FragmentProfile extends Fragment { - // ⚠️ 必须有无参构造函数! public FragmentProfile() { // Required empty public constructor } @@ -33,7 +32,7 @@ public class FragmentProfile extends Fragment { TextView movieCount = view.findViewById(R.id.tv_movie_count); TextView bookCount = view.findViewById(R.id.tv_book_count); - if (movieCount != null) movieCount.setText("24"); + if (movieCount != null) movieCount.setText("14"); if (bookCount != null) bookCount.setText("18"); } } \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/MediaAdapter.java b/app/src/main/java/top/iletter/lvnote/MediaAdapter.java new file mode 100644 index 0000000..25c296d --- /dev/null +++ b/app/src/main/java/top/iletter/lvnote/MediaAdapter.java @@ -0,0 +1,52 @@ +package top.iletter.lvnote; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +public class MediaAdapter extends RecyclerView.Adapter { + + private final List items; + + public MediaAdapter(List items) { + this.items = items; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_media, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + String[] parts = items.get(position).split(" · "); + holder.title.setText(parts[0]); + holder.meta.setText(parts.length > 1 ? parts[1] + " · " + parts[2] : ""); + holder.rating.setText(position % 3 == 0 ? "9.7" : position % 3 == 1 ? "9.2" : "8.9"); + } + + @Override + public int getItemCount() { + return items.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView title, meta, rating; + + ViewHolder(View itemView) { + super(itemView); + title = itemView.findViewById(R.id.tv_title); + meta = itemView.findViewById(R.id.tv_meta); + rating = itemView.findViewById(R.id.tv_rating); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/MediaListFragment.java b/app/src/main/java/top/iletter/lvnote/MediaListFragment.java new file mode 100644 index 0000000..e45e534 --- /dev/null +++ b/app/src/main/java/top/iletter/lvnote/MediaListFragment.java @@ -0,0 +1,89 @@ +package top.iletter.lvnote; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +public class MediaListFragment extends Fragment { + + private static final String ARG_STATUS = "status"; + private String status; + + public static MediaListFragment newInstance(String status) { + MediaListFragment fragment = new MediaListFragment(); + Bundle args = new Bundle(); + args.putString(ARG_STATUS, status); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + status = getArguments().getString(ARG_STATUS); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_media_list, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + RecyclerView recyclerView = view.findViewById(R.id.recycler_media); + recyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); + + // 模拟数据(根据状态返回不同假数据) + List items = generateFakeData(status); + MediaAdapter adapter = new MediaAdapter(items); + recyclerView.setAdapter(adapter); + } + + private List generateFakeData(String status) { + List data = new ArrayList<>(); + switch (status) { + case "watched": + data.add("肖申克的救赎 · 弗兰克·德拉邦特 · 1994"); + data.add("阿甘正传 · 罗伯特·泽米吉斯 · 1994"); + data.add("盗梦空间 · 克里斯托弗·诺兰 · 2010"); + break; + case "watching": + data.add("权力的游戏 S08 · HBO · 2019"); + data.add("怪奇物语 S04 · Netflix · 2022"); + break; + case "plan_to_watch": + data.add("奥本海默 · 克里斯托弗·诺兰 · 2023"); + data.add("沙丘2 · 丹尼斯·维伦纽瓦 · 2024"); + break; + case "read": + data.add("三体 · 刘慈欣 · 2006"); + data.add("活着 · 余华 · 1993"); + break; + case "reading": + data.add("人类简史 · 尤瓦尔·赫拉利 · 2011"); + break; + case "plan_to_read": + data.add("百年孤独 · 加西亚·马尔克斯 · 1967"); + data.add("追风筝的人 · 卡勒德·胡赛尼 · 2003"); + break; + } + return data; + } +} \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/MovieListFragment.java b/app/src/main/java/top/iletter/lvnote/MovieListFragment.java new file mode 100644 index 0000000..c7da612 --- /dev/null +++ b/app/src/main/java/top/iletter/lvnote/MovieListFragment.java @@ -0,0 +1,68 @@ +package top.iletter.lvnote; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +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; + +public class MovieListFragment extends Fragment { + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_movie_list, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + ViewPager2 viewPagerSecondary = view.findViewById(R.id.view_pager_secondary); + viewPagerSecondary.setAdapter(new SecondaryPagerAdapter(this)); + + // 获取父 Fragment 的二级 TabLayout + TabLayout tabSecondary = requireActivity().findViewById(R.id.tab_secondary); + tabSecondary.setVisibility(View.VISIBLE); + + // 绑定二级导航 + new TabLayoutMediator(tabSecondary, viewPagerSecondary, (tab, position) -> { + switch (position) { + case 0: tab.setText("看过"); break; + case 1: tab.setText("正在看"); break; + case 2: tab.setText("准备看"); break; + } + }).attach(); + } + + // 二级 ViewPager2 适配器(三个状态页面) + static class SecondaryPagerAdapter extends FragmentStateAdapter { + public SecondaryPagerAdapter(@NonNull Fragment fragment) { + super(fragment); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + return MediaListFragment.newInstance( + position == 0 ? "watched" : + position == 1 ? "watching" : "plan_to_watch" + ); + } + + @Override + public int getItemCount() { + return 3; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/NoteAdapter.java b/app/src/main/java/top/iletter/lvnote/NoteAdapter.java new file mode 100644 index 0000000..0351682 --- /dev/null +++ b/app/src/main/java/top/iletter/lvnote/NoteAdapter.java @@ -0,0 +1,53 @@ +package top.iletter.lvnote; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +public class NoteAdapter extends RecyclerView.Adapter { + + private final List notes; + + public NoteAdapter(List notes) { + this.notes = notes; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_note, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + String note = notes.get(position); + String[] parts = note.split(" · "); + holder.title.setText(parts[0]); + holder.content.setText(parts.length > 1 ? parts[1] : "无内容"); + holder.time.setText("2024-02-09 15:30"); + } + + @Override + public int getItemCount() { + return notes.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView title, content, time; + + ViewHolder(View itemView) { + super(itemView); + title = itemView.findViewById(R.id.tv_note_title); + content = itemView.findViewById(R.id.tv_note_content); + time = itemView.findViewById(R.id.tv_note_time); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/top/iletter/lvnote/NoteListFragment.java b/app/src/main/java/top/iletter/lvnote/NoteListFragment.java new file mode 100644 index 0000000..246941d --- /dev/null +++ b/app/src/main/java/top/iletter/lvnote/NoteListFragment.java @@ -0,0 +1,54 @@ +package top.iletter.lvnote; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.Arrays; +import java.util.List; + +public class NoteListFragment extends Fragment { + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_note_list, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + RecyclerView recyclerView = view.findViewById(R.id.recycler_notes); + TextView tvEmpty = view.findViewById(R.id.tv_empty); + + recyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); + + // 模拟笔记数据(空列表演示空状态) + List notes = Arrays.asList( + "2024-02-09 · 今日观影笔记", + "2024-02-08 · 读书随想", + "2024-02-05 · 生活碎片" + ); + + if (notes.isEmpty()) { + tvEmpty.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); + } else { + tvEmpty.setVisibility(View.GONE); + recyclerView.setVisibility(View.VISIBLE); + NoteAdapter adapter = new NoteAdapter(notes); + recyclerView.setAdapter(adapter); + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_book_list.xml b/app/src/main/res/layout/fragment_book_list.xml new file mode 100644 index 0000000..75b0b3b --- /dev/null +++ b/app/src/main/res/layout/fragment_book_list.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 648879f..eccebbb 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,28 +1,39 @@ - - + + android:layout_height="48dp" + app:tabMode="fixed" + app:tabGravity="fill" + app:tabIndicatorColor="?colorPrimary" + app:tabIndicatorHeight="3dp" + app:layout_constraintTop_toTopOf="parent" /> - - + + - - - - \ No newline at end of file + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_media_list.xml b/app/src/main/res/layout/fragment_media_list.xml new file mode 100644 index 0000000..167aade --- /dev/null +++ b/app/src/main/res/layout/fragment_media_list.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_movie_list.xml b/app/src/main/res/layout/fragment_movie_list.xml new file mode 100644 index 0000000..750d777 --- /dev/null +++ b/app/src/main/res/layout/fragment_movie_list.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_note_list.xml b/app/src/main/res/layout/fragment_note_list.xml new file mode 100644 index 0000000..730c6b2 --- /dev/null +++ b/app/src/main/res/layout/fragment_note_list.xml @@ -0,0 +1,31 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_media.xml b/app/src/main/res/layout/item_media.xml new file mode 100644 index 0000000..c71bad8 --- /dev/null +++ b/app/src/main/res/layout/item_media.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_note.xml b/app/src/main/res/layout/item_note.xml new file mode 100644 index 0000000..790f38e --- /dev/null +++ b/app/src/main/res/layout/item_note.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7dea023..0fd6b88 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,8 @@ 看过 正在看 准备看 + + 添加影视 添加书籍 \ No newline at end of file