仿照著知乎寫了一套UI界面形入,結(jié)合著以前學的知識全跨,一天就擼了出來,其實也沒啥東西亿遂,就是有些沒接觸的地方踩了坑浓若。
效果展示
涉及知識點
- 最基礎的viewpager編寫可以參看這篇超簡單ViewPager控件實現(xiàn)Demo
- tablayout+viewpager實現(xiàn)的過程可以參看這篇Material Design學習:TabLayout+Viewpager制作一個標簽頁
- recycleview的實現(xiàn)可以參看這篇RecyclerView的使用簡介
- viewpager需要添加為recycleview的頭布局才能在recycleview上滑的時候上滑渺杉,但是recycleview沒有添加頭布局方法,具體的實現(xiàn)是通過加載不同item的方式挪钓。
可以參看我寫的RecycleView加載不同類型的Item
頁面分析
實現(xiàn)思路
- 外層的viewpager頁面是越,通過點擊頁面底部的RadioButton來進行頁面切換(禁止此viewpager響應滑動事件),配合fragment實現(xiàn)碌上。
- 中間層的viewpager頁面英妓,通過和tablayout綁定,配合fragment绍赛,實現(xiàn)頂部頁簽蔓纠。
- 頂層的viewpager頁面,就是個簡單的輪播圖吗蚌,循環(huán)播放不想寫了腿倚。底部使用了viewpagerindicator開源庫實現(xiàn)小圓點指示器。
- 添加依賴
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開源庫可以從我的項目中下載
1. 外層viewpager的布局實現(xiàn)
- 布局代碼實現(xiàn)
<?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>
- 禁止滑動的viewpager實現(xiàn)蚯妇,繼承viewpager
public class NoScrollViewPager extends ViewPager {
public NoScrollViewPager(Context context) {
super(context);
}
public NoScrollViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
//表示事件是否攔截敷燎,返回false表示不攔截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
//重寫onTouchEvent事件,什么都不用做
@Override
public boolean onTouchEvent(MotionEvent ev) {
return false;
}
}
2. 外層viewpager的代碼實現(xiàn)
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); //隱藏掉系統(tǒng)原先的導航欄
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.vp_main);
//新建fragment集合對象箩言,傳遞給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);
//當點擊底部按鈕時切換頁面
rgGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
if (i == R.id.rb_home) {
mViewPager.setCurrentItem(0, false);//去掉切換頁面的動畫
} 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);
}
}
});
//防止頻繁的銷毀視圖
mViewPager.setOffscreenPageLimit(4);
}
}
在大部分時候硬贯,項目中的ViewPager會和Fragment同時出現(xiàn),每一個ViewPager的頁面就是一個Fragment陨收。
Android提供了一些專門的適配器來讓ViewPager與Fragment一起工作饭豹,也就是FragmentPagerAdapter與FragmentStatePagerAdapter。
FragmentPagerAdapter繼承自PagerAdapter 务漩,主要用來展示多個Fragment頁面拄衰,并且每一個Fragment都會被保存在fragment manager中。
FragmentPagerAdapter最適用于那種少量且相對靜態(tài)的頁面饵骨,例如幾個tab頁翘悉。每一個用戶訪問過的fragment都會被保存在內(nèi)存中,盡管他的視圖層級可能會在不可見時被銷毀居触。這可能導致大量的內(nèi)存因為fragment實例能夠擁有任意數(shù)量的狀態(tài)妖混。對于較多的頁面集合,更推薦使用FragmentStatePagerAdapter轮洋。
當使用FragmentPagerAdapter的時候?qū)腣iewPager必須擁有一個有效的ID集制市。
FragmentPagerAdapter的派生類只需要實現(xiàn)getItem(int)和getCount()即可。
- FragmentPagerAdapter適配器的實現(xiàn)
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() {
return fragments.size();
}
}
里面的幾個fragment都是簡單布局砖瞧,就不貼了息堂。主要講解下fragment1,因為它里面包含著另外兩個viewpager。
3. 中間層的viewpager的布局實現(xiàn)
- 一個簡單的tablayout配合viewpager使用荣堰,對tablayout的樣式做了設定床未。
<?xml version="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>
4. 中間層的viewpager的代碼實現(xiàn)
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;
}
//初始化數(shù)據(jù)
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);
//將TabLayout和ViewPager綁定在一起,使雙方各自的改變都能直接影響另一方振坚,解放了開發(fā)人員對雙方變動事件的監(jiān)聽
mTabLayout.setupWithViewPager(mViewPager);
//指定Tab的位置
one = mTabLayout.getTabAt(0);
two = mTabLayout.getTabAt(1);
three = mTabLayout.getTabAt(2);
four = mTabLayout.getTabAt(3);
}
}
- 中間層的viewpager的FragmentPagerAdapter適配器
public class MyTabFragmentPagerAdapter extends FragmentPagerAdapter {
private String[] mTitles = new String[]{"推薦", "圓桌", "熱門","收藏"};
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() {
return mTitles.length;
}
//用來設置tab的標題
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
}
5. 頂層的viewpager的實現(xiàn)
在布局只是在fragment中放入一個recycleview薇搁,而輪播的viewpager是作為頭布局加入recycleview中的。
下面是頭布局的代碼
<?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">
//在布局中添加viewpagerindicator渡八,通過自定義屬性設置好外觀
<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>
- 下面是TabFragment1 的邏輯代碼
//省略viewpager的adapter啃洋,因為不重要
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();//初始化數(shù)據(jù)
mRecyclerView.setAdapter(new MyAdapter(mDatas));
return view;
}
private void initdata() {
//初始化recycleview的數(shù)據(jù)
mDatas = new ArrayList<String>();
for (int i = 0; i < 45; i++) {
mDatas.add("item" + i);
}
//初始化viewpager的數(shù)據(jù)
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;//頭布局
private static final int BODY_VIEW = 1;//內(nèi)容布局
private MyPagerAdapter mPagerAdapter = new MyPagerAdapter();
//創(chuàng)建構造參數(shù),用來接受數(shù)據(jù)集
public MyAdapter(List<String> datas) {
this.mDatas = datas;
}
//創(chuàng)建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) {
//加載布局文件
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_recycle, parent, false);
MyBodyViewHolder viewHolder = new MyBodyViewHolder(view);
return viewHolder;
}
return null;
}
//綁定ViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//將數(shù)據(jù)填充到具體的view中
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;
}
//如果是第一項屎鳍,則加載頭布局
@Override
public int getItemViewType(int position) {
if (position == 0) {
return HEAD_VIEW;
} else {
return BODY_VIEW;
}
}
}
//頭布局的viewholder
class MyHeadViewHolder extends RecyclerView.ViewHolder {
ViewPager mViewPager;
CirclePageIndicator indicator; //定義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);
}
}
}
- ViewPager+Fragment配合使用中的問題
如圖宏娄,切換的時候可能導致頁面被銷毀。
- 解決方案:防止頻繁的銷毀視圖逮壁,可以setOffscreenPageLimit(2)或者重寫PagerAdaper的destroyItem方法為空孵坚。
這里是項目地址。
參考
http://blog.csdn.net/never_cxb/article/details/50520270
http://www.open-open.com/lib/view/open1431174803882.html
http://www.imooc.com/article/2742