Imitate zhihu wrote a set of UI interface, combined with the knowledge learned before, one day out, in fact, there is nothing, is some places did not touch the pit.
Results show
Knowledge points involved
- Basic viewpager writing can be seen in this super simple Viewpager control implementation Demo
- Tablayout + ViewPager creates a TAB page
- The realization of RecycleView can see this introduction to the use of RecyclerView
- Viewpager needs to add the head layout of RecycleView to slide up the recycleView, but the recycleView does not add the head layout method, the specific implementation is by loading different items. See RecycleView I wrote to load different types of items
Page analysis
Implementation approach
- The outer viewpager page, by clicking the RadioButton at the bottom of the page to switch the page (forbidding the viewpager to respond to sliding events), with the fragment implementation.
- The viewPager page in the middle layer is bound to tabLayout and fragment to implement the top TAB.
- The top-level viewpager page is just a simple rotograph that you don’t want to write. The viewPagerIndicator open source library is used at the bottom to realize the small dot indicator.
- Add depend on the compile ‘com. Android. Support: design: 24.2.0’ compile ‘com. Android. Support: cardview – v7:24.2.0’ compile ‘com. Android. Support: recyclerview – v7:24.2.0’ viewpagerindicator open source library can be downloaded from my projects
1. Layout implementation of the outer ViewPager
-
Layout code implementation
<?xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <zhj.viewpagerdemo.view.NoScrollViewPager android:id="@+id/vp_main" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1"/> <RadioGroup android:id="@+id/rg_group" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:paddingTop="5dp"> <RadioButton android:id="@+id/rb_home" style="@style/BottomTabStyle" android:checked="true" android:drawableBottom="@drawable/btn_tab_home_selector"/> <RadioButton android:id="@+id/rb_news" style="@style/BottomTabStyle" android:drawableBottom="@drawable/btn_tab_news_selector"/> <RadioButton android:id="@+id/rb_service" style="@style/BottomTabStyle" android:drawableBottom="@drawable/btn_tab_service_selector"/> <RadioButton android:id="@+id/rb_gov" style="@style/BottomTabStyle" android:drawableBottom="@drawable/btn_tab_gov_selector"/> <RadioButton android:id="@+id/rb_setting" style="@style/BottomTabStyle" android:drawableBottom="@drawable/btn_tab_setting_selector"/> </RadioGroup> </LinearLayout>Copy the code
-
Disallow sliding viewpager implementations and inherit viewpager
public class NoScrollViewPager extends ViewPager { public NoScrollViewPager(Context context) { super(context); } public NoScrollViewPager(Context context, AttributeSet attrs) { super(context, attrs); } If false is returned, the event is not intercepted @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } // Override the onTouchEvent event to do nothing @Override public boolean onTouchEvent(MotionEvent ev) { return false; }}Copy the code
2. Outer viewpager code implementation
public class MainActivity extends AppCompatActivity {
private MyFragmentPagerAdapter myFragmentPagerAdapter;
private RadioGroup rgGroup;
private List<Fragment> fragments;
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE); // Hide the original navigation bar
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.vp_main);
// Create a Fragment collection object and pass it to the FragmentPagerAdapter
fragments=new ArrayList<Fragment> (); fragments.add(new Fragment1());
fragments.add(new Fragment2());
fragments.add(new Fragment3());
fragments.add(new Fragment4());
fragments.add(new Fragment5());
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),fragments);
mViewPager.setAdapter(myFragmentPagerAdapter);
rgGroup = (RadioGroup) findViewById(R.id.rg_group);
rgGroup.check(R.id.rb_home);
// Switch pages when the bottom button is clicked
rgGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
if (i == R.id.rb_home) {
mViewPager.setCurrentItem(0.false);// Remove the animation for switching pages
} else if (i == R.id.rb_news) {
mViewPager.setCurrentItem(1.false);
} else if (i == R.id.rb_service) {
mViewPager.setCurrentItem(2.false);
} else if (i == R.id.rb_gov) {
mViewPager.setCurrentItem(3.false);
} else if (i == R.id.rb_setting) {
mViewPager.setCurrentItem(4.false); }}});// Prevent frequent view destruction
mViewPager.setOffscreenPageLimit(4); }}Copy the code
Most of the time, ViewPager and Fragment will appear together in a project; each ViewPager page is a Fragment. Android provides some special adapters to make ViewPager working with fragments, namely FragmentPagerAdapter and FragmentStatePagerAdapter.
FragmentPagerAdapter inherits from PagerAdapter and is used to display multiple Fragment pages, and each Fragment is stored in the Fragment Manager. FragmentPagerAdapter is best suited for small and relatively static pages, such as a few TAB pages. Every fragment accessed by a user is stored in memory, although its view hierarchy may be destroyed when it is not visible. This can lead to a lot of memory because fragment instances can have any number of states. Set for more pages, it is recommended to use more FragmentStatePagerAdapter. When using FragmentPagerAdapter, the corresponding ViewPager must have a valid set of ids. The derived classes of FragmentPagerAdapter need only implement getItem(int) and getCount().
-
FragmentPagerAdapter Implementation of the adapter
public class MyFragmentPagerAdapter extends FragmentPagerAdapter{ private List<Fragment> fragments; public MyFragmentPagerAdapter(FragmentManager fm,List<Fragment> fragments) { super(fm); this.fragments=fragments; } @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount(a) { return fragments.size(a); }}Copy the code
Several fragments inside are simple layout, I will not paste. I’ll focus on Fragment1, because it contains two other viewPagers.
3. Middle layer layout implementation of viewPager
- A simple tabLayout is used with viewPager to set the tabLayout style.
<? xmlversion="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#1e8ae8"
app:tabGravity="fill"
app:tabIndicatorColor="#fff"
app:tabIndicatorHeight="4dp"
app:tabMode="fixed"
app:tabSelectedTextColor="#fff"
app:tabTextColor="#97c8f4"
>
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp_menu_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</android.support.v4.view.ViewPager>
</LinearLayout>Copy the code
4. Middle layer viewpager code implementation
public class Fragment1 extends Fragment {
private ViewPager mViewPager;
private MyTabFragmentPagerAdapter mMyTabFragmentPagerAdapter;
private List<Fragment> fragments;
private TabLayout.Tab one;
private TabLayout.Tab two;
private TabLayout.Tab three;
private TabLayout.Tab four;
private TabLayout mTabLayout;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
mTabLayout = (TabLayout) view.findViewById(R.id.tab_main);
mViewPager = (ViewPager) view.findViewById(R.id.vp_menu_pager);
initdata();
return view;
}
// Initialize the data
private void initdata() {
fragments=new ArrayList<Fragment> (); fragments.add(new TabFragment1());
fragments.add(new TabFragment2());
fragments.add(new TabFragment3());
fragments.add(new TabFragment4());
mMyTabFragmentPagerAdapter = new MyTabFragmentPagerAdapter(getActivity().getSupportFragmentManager()
,fragments);
mViewPager.setOffscreenPageLimit(4);
mViewPager.setAdapter(mMyTabFragmentPagerAdapter);
// Bind TabLayout and ViewPager together so that changes made by each side can directly affect the other side, freeing developers to monitor changes made by both sides
mTabLayout.setupWithViewPager(mViewPager);
// Specify the Tab position
one = mTabLayout.getTabAt(0);
two = mTabLayout.getTabAt(1);
three = mTabLayout.getTabAt(2);
four = mTabLayout.getTabAt(3); }}Copy the code
-
The FragmentPagerAdapter adapter of the viewPager in the middle tier
public class MyTabFragmentPagerAdapter extends FragmentPagerAdapter { private String[] mTitles = new String[]{"Recommended"."Round table"."Hot"."Collection"}; private List<Fragment> fragments; public MyTabFragmentPagerAdapter(FragmentManager fm,List<Fragment> fragments) { super(fm); this.fragments=fragments; } @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount(a) { return mTitles.length; } // Set the TAB title @Override public CharSequence getPageTitle(int position) { returnmTitles[position]; }}Copy the code
5. Implementation of the top-level viewpager
-
The recycleView is added to the fragment, and the viewpager is added to the RecycleView as a header.
-
Here is the code for the header layout
<?xml version="1.0" encoding="utf-8"? > <LinearLayout 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="wrap_content" android:orientation="vertical"> <android.support.v4.view.ViewPager android:id="@+id/vp_tab_headview" android:layout_width="match_parent" android:layout_height="180dp"> </android.support.v4.view.ViewPager> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="#fff"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true">// Add viewPagerIndicator to the layout to set the appearance with custom properties<com.viewpagerindicator.CirclePageIndicator android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" app:fillColor="#f00" app:pageColor="#e0e0e0" app:radius="4dp" app:strokeWidth="0dp"/> </RelativeLayout> </RelativeLayout> </LinearLayout>Copy the code
-
Here is the logical code for TabFragment1
// Omit the adapter of the viewpager because it is not important public class TabFragment1 extends Fragment { private RecyclerView mRecyclerView; private List<String> mDatas; private ArrayList<ImageView> imageList; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.tab_fragment1, container, false); mRecyclerView = (RecyclerView) view.findViewById(R.id.recycle); mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL.false)); initdata();// Initialize the data mRecyclerView.setAdapter(new MyAdapter(mDatas)); return view; } private void initdata() { // Initialize recycleView data mDatas = new ArrayList<String> ();for (int i = 0; i < 45; i++) { mDatas.add("item" + i); } // Initialize the viewpager data int[] imageResIDs = {R.drawable.a, R.drawable.b, R.drawable.c}; imageList = new ArrayList<ImageView> ();for (int i = 0; i < imageResIDs.length; i++) { ImageView image = new ImageView(getActivity()); image.setBackgroundResource(imageResIDs[i]); imageList.add(image); }}class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<String> mDatas; private static final int HEAD_VIEW = 0;/ / head layout private static final int BODY_VIEW = 1;// Content layout private MyPagerAdapter mPagerAdapter = new MyPagerAdapter(a);// Create a construct parameter to accept the data set public MyAdapter(List<String> datas) { this.mDatas = datas; } / / create the ViewHolder @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == HEAD_VIEW) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.headview_recycleview, parent, false); MyHeadViewHolder viewHolder = new MyHeadViewHolder(view); return viewHolder; } if (viewType == BODY_VIEW) { // Load the layout file View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_recycle, parent, false); MyBodyViewHolder viewHolder = new MyBodyViewHolder(view); return viewHolder; } return null; } / / bind ViewHolder @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { // Populate the view with data if (holder instanceof MyHeadViewHolder) {((MyHeadViewHolder) holder).mViewPager.setAdapter(mPagerAdapter); ((MyHeadViewHolder) holder).indicator.onPageSelected(0); ((MyHeadViewHolder) holder).indicator.setViewPager(((MyHeadViewHolder) holder).mViewPager); ((MyHeadViewHolder) holder).indicator.setSnap(true); } if (holder instanceof MyBodyViewHolder) {((MyBodyViewHolder) holder).tv.setText(mDatas.get(position- 1)); }}@Override public int getItemCount() { return mDatas.size() + 1; } // If it is the first item, the header layout is loaded @Override public int getItemViewType(int position) { if (position == 0) { return HEAD_VIEW; } else { return BODY_VIEW; }}}// Viewholder for the header layout class MyHeadViewHolder extends RecyclerView.ViewHolder { ViewPager mViewPager; CirclePageIndicator indicator; / / define the indicator public MyHeadViewHolder(View itemView) { super(itemView); mViewPager = (ViewPager) itemView.findViewById(R.id.vp_tab_headview); indicator = (CirclePageIndicator) itemView.findViewById(R.id.indicator); }}class MyBodyViewHolder extends RecyclerView.ViewHolder { TextView tv; public MyBodyViewHolder(View itemView) { super(itemView); tv = (TextView) itemView.findViewById(R.id.recycle_tv); }}}Copy the code
- Problems with using ViewPager+Fragment
As shown, switching may cause the page to be destroyed.
- Solution: To prevent frequent destruction of views, setOffscreenPageLimit(2) or override the destroyItem method of PagerAdaper to leave it empty.
Here is the project address.
Reference blog.csdn.net/never_cxb/a… www.open-open.com/lib/view/op… www.imooc.com/article/274…