轉載請標明出處:
http://blog.csdn.net/developer_jiangqq/article/details/50145759
本文出自:【江清清的博客】
(一).前言:
【好消息】個人網站已經上線運行,後面博客以及技術乾貨等精彩文章會同步更新,請大家關注收藏:http://www.lcode.org
仿36Kr客戶端開發過程中,因爲他們網站上面的新聞文章分類比較多,所以我這邊還是打算模仿網易新聞APP的主界面新聞標籤Tab以及頁面滑動效果來進行實現。要實現的頂部的Tab標籤的效果有很多方法例如採用開源項目ViewPagerIndicator中的TabPageIndicator就可以實現,不過查看了源碼發現該控件其實就是繼承自HorizontalScrollView自定義出來的。那既然這樣我這邊就準備帶着大家直接使用HorizontalScrollView來實現頂部tab標籤效果。底部的頁面滑動直接採用Fragment+ViewPager+FragmentStatePagerAdapter實現即可。後面我也會更新一篇直接使用ViewPagerIndicator開源控件實現tab標籤效果的文章,敬請期待~
本例子具體代碼已經上傳到下面的項目中,歡迎各位去star和fork一下。
FastDev4Android框架項目地址:https://github.com/jiangqqlmj/FastDev4Android
(二).實現原理:
上面我這邊直接貼了36Kr官方APP的主界面頂部Tab標籤,我這邊直接模仿這個做。首先看上面截圖紅色框起來的部分,這邊的Tab是可以橫向滑動的那可以採用HorizontalScrollView控件實現,並且裏邊的每一項Tab Item都是可以進行添加和點擊。我們直接往HorizontalScrollView addView子控件即可。Tab下面是若干個新聞文章列表的頁面且可以進行左右滑動可以採用ViewPager實現,每個頁面採用Fragment實現。
上圖是具體控件的分佈和嵌套,下面我們來看一下具體實現:
(三).具體實現:
3.1.首先我們需要有一個繼承自FragmentActivity的MainInfoActivity,該用來承載Fragment,該Activity中實現基本沒有啥代碼,就不貼詳細到時候去FastDev4Android項目中下載即可,這邊我們看一下Activity的佈局文件:
<?xmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent"
>
<includelayout="@layout/common_top_bar_layout"/>
<fragment
android:id="@+id/info_fragment"
class="com.chinaztt.fda.fragment.InfoFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:layout="@layout/info_fragment_layout"
/>
</LinearLayout>
該佈局文件中直接把承載的InfoFragment寫在裏邊了,當我們Activity加載的時候該Fragment也被加載了。
3.2.接下來就是InfoFragment了,讓我們首先看下我這邊定義的佈局文件:
<?xmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<!--橫向滑動的容器-->
<HorizontalScrollView
android:id="@+id/horizontal_info"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<!--裝入每一個Tab項容器-->
<LinearLayout
android:id="@+id/linearlayout_container"
android:layout_width="fill_parent"
android:layout_height="49dp"
android:orientation="horizontal">
</LinearLayout>
</HorizontalScrollView>
<android.support.v4.view.ViewPager
android:id="@+id/info_viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
該佈局中主要分爲兩部分,第一部分就是HorizontalScrollView控件該用來實現橫向滑動,爲標籤Tab的容器,我們可以往裏邊動態的添加Tab Item。第二部分爲ViewPager控件該用來實現頁面的左右滑動,其中每一項Item爲Fragment。以上關鍵控件已經定義好了,下面就是需要在InfoFragment中實現Tab效果了。
首先定義和初始化控件:
/**
* 當前選擇的分類
*/
private int mCurClassIndex=0;
/**
* 選擇的分類字體顏色
*/
private int mColorSelected;
/**
* 非選擇的分類字體顏色
*/
private int mColorUnSelected;
/**
* 水平滾動的Tab容器
*/
private HorizontalScrollView mScrollBar;
/**
* 分類導航的容器
*/
private ViewGroup mClassContainer;
/**
* 水平滾動X
*/
private int mScrollX=0;
mScrollBar=(HorizontalScrollView)mView.findViewById(R.id.horizontal_info);
mClassContainer=(ViewGroup)mView.findViewById(R.id.linearlayout_container);
對於Tab Item的動態添加使用下面寫得方法addScrollView():
/**
* 動態添加頂部Tab滑動的標籤
* @param titles
*/
private void addScrollView(String[]titles){
LayoutInflater mLayoutInflater=LayoutInflater.from(FDApplication.getInstance());
final int count=titles.length;
for(int i=0;i<count;i++){
final String title=titles[i];
final Viewview=mLayoutInflater.inflate(R.layout.horizontal_item_layout,null);
final LinearLayout linearLayout=(LinearLayout)view.findViewById(R.id.horizontal_linearlayout_type);
final ImageView img_type=(ImageView)view.findViewById(R.id.horizontal_img_type);
final TextView type_name=(TextView)view.findViewById(R.id.horizontal_tv_type);
type_name.setText(title);
if(i==mCurClassIndex){
//已經選中
type_name.setTextColor(mColorSelected);
img_type.setImageResource(R.drawable.bottom_line_blue);
}else {
//未選中
type_name.setTextColor(mColorUnSelected);
img_type.setImageResource(R.drawable.bottom_line_gray);
}
final int index=i;
//點擊頂部Tab標籤,動態設置下面的ViewPager頁面
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//首先設置當前的Item爲正常狀態
View currentItem=mClassContainer.getChildAt(mCurClassIndex);
((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
mCurClassIndex=index;
//設置點擊狀態
img_type.setImageResource(R.drawable.bottom_line_blue);
type_name.setTextColor(mColorSelected);
//跳轉到指定的ViewPager
info_viewpager.setCurrentItem(mCurClassIndex);
}
});
mClassContainer.addView(view);
}
}
該方法傳入了標籤的數組,根據標籤的數量進行遍歷動態添加,主要步驟如下:- 加載每一項Tab Item的佈局,並且獲取Item中的相關控件並且設置數據和資源文件
- 判斷當前是否選中項,對於選中和未選中設置不同的字體顏色和資源文件
- 給每一項Item添加點擊事件,用來切換ViewPager跳轉到具體每一項頁面(Fragment)
- 最終每一項Tab Item加入到容器中
上面我們有講到,使用Fragment+ViewPager實現頁面滑動切換,那我們需要一個頁面的自定義適配器了,我這邊創建了CNKFixedPagerAdapter該類繼承自FragmengStatePagerAdaper具體實現代碼如下,比較簡單就不詳細講解了:
public class CNKFixedPagerAdapter extends FragmentStatePagerAdapter {
private String[] titles;
public void setTitles(String[] titles) {
this.titles = titles;
}
private List<Fragment> fragments;
public CNKFixedPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
@Override
public int getCount() {
return this.fragments.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment=null;
try {
fragment=(Fragment)super.instantiateItem(container,position);
}catch (Exception e){
}
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
public List<Fragment> getFragments(){
return fragments;
}
public void setFragments(List<Fragment> fragments) {
this.fragments = fragments;
}
}
然後我們實例化ViewPager以及自定義適配器和顯示的Fragment數據綁定即可:fragments=new ArrayList<>();
for(int i=0;i<12;i++){
OneFragment oneFragment=new OneFragment();
Bundle bundle=new Bundle();
bundle.putString("extra",titles[i]);
oneFragment.setArguments(bundle);
fragments.add(oneFragment);
}
mPagerAdater=new CNKFixedPagerAdapter(getChildFragmentManager());
mPagerAdater.setTitles(titles);
mPagerAdater.setFragments(fragments);
info_viewpager.setAdapter(mPagerAdater);
最後我們不要忘記有一點是:當我們的ViewPager頁面切換的時候我們需要實習改變頂部Tab Item的選中情況以及字體顏色等。所以我們需要給ViewPager添加頁面切換監聽器OnPageChangeListener,然後在回調的onPageSelected()方法中重新設置一下Tab Item的效果。
info_viewpager.setOnPageChangeListener(this);
//下面三個回調方法 分別是在ViewPager進行滑動的時候調用
@Override
public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
//首先設置當前的Item爲正常狀態
View preView=mClassContainer.getChildAt(mCurClassIndex);
((TextView)(preView.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
((ImageView)(preView.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
mCurClassIndex=position;
//設置當前爲選中狀態
View currentItem=mClassContainer.getChildAt(mCurClassIndex);
((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_blue);
((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorSelected);
//這邊移動的距離 是經過計算粗略得出來的
mScrollX=currentItem.getLeft()-300;
Log.d("zttjiangqq","mScrollX:" + mScrollX);
mScrollBar.post(new Runnable() {
@Override
public void run() {
mScrollBar.scrollTo(mScrollX,0);
}
});
}
@Override
public void onPageScrollStateChanged(int state) {
}
上面onPageSelected()方法中我們首先設置原先的Item的顏色爲正常未選中狀態,然後設置當前的位置選中以及字體顏色改變,最後讓HorizontalScrollView平移到合適的位置即可:
3.3.以上我們的核心代碼已經講解完成了,下面我們看一下運行效果:
3.4.爲了大家方便閱讀代碼,我這邊把InfoFragment的全部代碼貼出來:
public class InfoFragment extends Fragment implements ViewPager.OnPageChangeListener{
private View mView;
ViewPager info_viewpager;
private List<Fragment> fragments;
private CNKFixedPagerAdapter mPagerAdater;
private String[] titles=new String[]{"全部","氪TV","O2O","新硬件","Fun!!","企業服務","Fit&Health","在線教育","互聯網金融","大公司","專欄","新產品"};
/**
* 當前選擇的分類
*/
private int mCurClassIndex=0;
/**
* 選擇的分類字體顏色
*/
private int mColorSelected;
/**
* 非選擇的分類字體顏色
*/
private int mColorUnSelected;
/**
* 水平滾動的Tab容器
*/
private HorizontalScrollView mScrollBar;
/**
* 分類導航的容器
*/
private ViewGroup mClassContainer;
/**
* 水平滾動X
*/
private int mScrollX=0;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(mView==null){
mView=inflater.inflate(R.layout.info_fragment_layout,container,false);
initViews();
initValidata();
}
return mView;
}
/**
* 初始化佈局控件
*/
private void initViews(){
info_viewpager=(ViewPager)mView.findViewById(R.id.info_viewpager);
mScrollBar=(HorizontalScrollView)mView.findViewById(R.id.horizontal_info);
mClassContainer=(ViewGroup)mView.findViewById(R.id.linearlayout_container);
}
private void initValidata(){
mColorSelected=FDApplication.getInstance().getResources().getColor(R.color.color_selected);
mColorUnSelected=FDApplication.getInstance().getResources().getColor(R.color.color_unselected);
//添加Tab標籤
addScrollView(titles);
mScrollBar.post(new Runnable() {
@Override
public void run() {
mScrollBar.scrollTo(mScrollX,0);
}
});
fragments=new ArrayList<>();
for(int i=0;i<12;i++){
OneFragment oneFragment=new OneFragment();
Bundle bundle=new Bundle();
bundle.putString("extra",titles[i]);
oneFragment.setArguments(bundle);
fragments.add(oneFragment);
}
mPagerAdater=new CNKFixedPagerAdapter(getChildFragmentManager());
mPagerAdater.setTitles(titles);
mPagerAdater.setFragments(fragments);
info_viewpager.setAdapter(mPagerAdater);
info_viewpager.setOnPageChangeListener(this);
}
/**
* 動態添加頂部Tab滑動的標籤
* @param titles
*/
private void addScrollView(String[]titles){
LayoutInflater mLayoutInflater=LayoutInflater.from(FDApplication.getInstance());
final int count=titles.length;
for(int i=0;i<count;i++){
final String title=titles[i];
final View view=mLayoutInflater.inflate(R.layout.horizontal_item_layout,null);
final LinearLayout linearLayout=(LinearLayout)view.findViewById(R.id.horizontal_linearlayout_type);
final ImageView img_type=(ImageView)view.findViewById(R.id.horizontal_img_type);
final TextView type_name=(TextView)view.findViewById(R.id.horizontal_tv_type);
type_name.setText(title);
if(i==mCurClassIndex){
//已經選中
type_name.setTextColor(mColorSelected);
img_type.setImageResource(R.drawable.bottom_line_blue);
}else {
//未選中
type_name.setTextColor(mColorUnSelected);
img_type.setImageResource(R.drawable.bottom_line_gray);
}
final int index=i;
//點擊頂部Tab標籤,動態設置下面的ViewPager頁面
view.setOnClickListener(newView.OnClickListener() {
@Override
public void onClick(View v) {
//首先設置當前的Item爲正常狀態
View currentItem=mClassContainer.getChildAt(mCurClassIndex);
((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
mCurClassIndex=index;
//設置點擊狀態
img_type.setImageResource(R.drawable.bottom_line_blue);
type_name.setTextColor(mColorSelected);
//跳轉到指定的ViewPager
info_viewpager.setCurrentItem(mCurClassIndex);
}
});
mClassContainer.addView(view);
}
}
//下面三個回調方法 分別是在ViewPager進行滑動的時候調用
@Override
public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
//首先設置當前的Item爲正常狀態
View preView=mClassContainer.getChildAt(mCurClassIndex);
((TextView)(preView.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
((ImageView)(preView.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
mCurClassIndex=position;
//設置當前爲選中狀態
View currentItem=mClassContainer.getChildAt(mCurClassIndex);
((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_blue);
((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorSelected);
//這邊移動的距離 是經過計算粗略得出來的
mScrollX=currentItem.getLeft()-300;
Log.d("zttjiangqq","mScrollX:" + mScrollX);
mScrollBar.post(new Runnable() {
@Override
public void run() {
mScrollBar.scrollTo(mScrollX,0);
}
});
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
(四).最後總結
今天我們通過Fragment+ViewPager+FragmentStatePagerAdapter+HorizontalScrollView實現了仿照網易新聞客戶端(或者36Kr)首頁的頁面滑動和頂部Tab效果。
本次實例代碼因爲比較多,代碼全貼比較浪費篇幅,重點在於講解思路了。不過實例註釋過的全部代碼已經上傳到Github項目中了。同時歡迎大家去Github站點進行clone或者fork瀏覽整個開源快速開發框架項目~
https://github.com/jiangqqlmj/FastDev4Android
尊重原創,轉載請註明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵權必究!
關注我的訂閱號,每天分享移動開發技術(Android/IOS),項目管理以及博客文章!
關注我的微博,可以獲得更多精彩內容