TabLayout+ViewPager+Fragmet+RecyclerView結合的小demo

前兩天想熟悉一下tablayout,因此就弄了個特別簡單的tablayout+viewpager+fragmet+recyclerview結合的小demo.雖然特別簡單,但仍是遇到了很多問題。java

tablayout顯示標題,viewpager實現滑動,fragment裝載數據,recyclerview顯示數據。android

  • step1:引入依賴

implementation 'com.android.support:appcompat-v7:xxx' implementation 'com.android.support:design:xxx' implementation 'com.android.support:recyclerview-v7:xxx'bash

  • step2:XML中引入tabLayout
<android.support.design.widget.TabLayout
複製代碼

java文件中:網絡

tabLayout = (TabLayout) findViewById(R.id.tab);
tabLayout.setupWithViewPager(viewPager);
複製代碼

在這裏要記得設一下tabLayout的mode,有兩個值,分別是 TabLayout.MODE_FIXED :當Tab較少,且佔滿整個屏幕時可使用這種模式。 TabLayout.MODE_SCROLLABLE :當Tab數量較多,屏幕寬度不夠時使用該模式,整個TabLayout是能夠左右滑動的。app

tabLayout.setTabMode(TabLayout.MODE_FIXED);
複製代碼
  • step3:再來是viewPager

xml引入ide

<android.support.v4.view.ViewPager
複製代碼

java文件中函數

viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentList));
複製代碼
  • step4:Fragment
public class MyFragment extends Fragment {
複製代碼

繼承了fragmentPagerAdapter的類不須要重寫那麼多方法,必須有的是getItem(int)和getCount()。不過我還加了這兩個方法。佈局

@Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { }
    
 @Override
    public CharSequence getPageTitle(int position) {
        stringList.add("碎片1");
        stringList.add("碎片2");
        stringList.add("碎片3");
        return stringList.get(position);
    }
複製代碼

解釋一下,第一個方法重載方法是爲了防止viewpager在滑動切換的時候,裏面的fragment被銷燬,致使數據須要從新加載。若是此時viewpager裏面的數據是從網絡獲取的,那麼每一輪滑動,都要從新進行網絡獲取,很是影響性能。性能

查找資料後經試驗,直接在destroyItem裏面將其super.destroyItem()刪除掉。這樣就不會從新加載數據了。固然還有其餘方式處理這種狀況,若是有小夥伴試驗成功的,更好的方法歡迎下方留言指教。學習

第二個重載方法,是爲了顯示tabLayout的標題,標題這裏出了個大坑,網上不少例子都是直接在主activity那裏addTab,setText,可是很容易會遇到標題沒法顯示的問題。解決辦法就是在執行了tablayout.setUpWithViewPager以後再設置經過tablayout.getTabAt(position).setText(String)去設置。

然而,知道了tabLayout怎麼工做以後相信你們會更願意選擇直接在fragment裏面利用getPageTitle來操做。

查看源碼能夠發現,這裏竟然把全部添加的tab給remove掉了,因此在這個方法以前添加的tab和標題會都沒有效果。另外在for裏面咱們能夠觀察到,其實tabLayout會根據adapter的數量添加tab和標題.因此咱們能夠不用在主acivity添加tab,標題設置直接在fragment裏面利用getPageTitle就行了。

void populateFromPagerAdapter() {
        this.removeAllTabs();
        if (this.pagerAdapter != null) {
            int adapterCount = this.pagerAdapter.getCount();

            int curItem;
            for(curItem = 0; curItem < adapterCount; ++curItem) {
                this.addTab(this.newTab().setText(this.pagerAdapter.getPageTitle(curItem)), false);
            }

            if (this.viewPager != null && adapterCount > 0) {
                curItem = this.viewPager.getCurrentItem();
                if (curItem != this.getSelectedTabPosition() && curItem < this.getTabCount()) {
                    this.selectTab(this.getTabAt(curItem));
                }
            }
        }

    }
複製代碼

注:不過有的時候tablayout不跟viewPager和fragment結合在一塊兒,那麼這個時候就只能在主activity裏面將添加tab和標題了。得注意,添加標題在tablayout.setUpWithViewPager()方法後面添加。

viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentList));
複製代碼

fragmentList就是你事先要準備好的數據源

List<Fragment> fragmentList = new ArrayList<>();
fragmentList.add(new MyFragment());
        fragmentList.add(new MyFragment());
        fragmentList.add(new MyFragment());

複製代碼

內個MyFragment這些是本身自定義的fragment類,包括相關的xml。fragment類裏面作的主要是加載xml視圖。xml裏面的東西就是你要顯示的東西啦,那在這裏就是recyclerview。 貼fragment類,太長,弄成兩張。

public class MyFragment extends Fragment {
    @Nullable
    List<String> data_top = new ArrayList<>();
    List<String> data_bottom = new ArrayList<>();
    List<String> data_new = new ArrayList<>();
    RecyclerView recyclerView;
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View fragment1 = inflater.inflate(R.layout.fragment_first, container, false);
        recyclerView = (RecyclerView) fragment1.findViewById(R.id.recycler_first);
        initRecycler();
        return fragment1;
    }
    public void initRecycler() {
        data_top.add("Layout Manager:Item的佈局");
        data_top.add("爲Item提供數據");
        data_top.add("Item Decoration:Item之間的Divider");
        data_top.add("Item Animator:添加、刪除Item動畫");
        data_bottom.add("學習雷鋒好榜樣");
        data_bottom.add("學習雷鋒好榜樣");
        data_bottom.add("學習雷鋒好榜樣");
        data_bottom.add("學習雷鋒好榜樣");
        data_new.add("立場堅決鬥志強");
        data_new.add("立場堅決鬥志強");
        data_new.add("立場堅決鬥志強");
        data_new.add("立場堅決鬥志強");
        MainRecyclerAdapter mainRecyclerAdapter = new MainRecyclerAdapter(data_top, data_bottom, data_new, getContext());
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(mainRecyclerAdapter);
        mainRecyclerAdapter.notifyDataSetChanged();
    }
}

複製代碼

欸又看到新朋友了哦,來來來RecyclerView,介紹一下你是幹嗎的~

  • step5:RecyclerView

若是你是想在fragment裏面顯示簡單的數據,那麼好辦,但若是你是想經過RecyclerView顯示你的數據呢,那可就沒那麼簡單了。 RecyclerView呢,它本身也須要有一個數據適配器。 好咯那就給他造一個咯。插一個完整的文件代碼。

public class MainRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    Context context;

    //設置不一樣類型adapter的區分enum
    public static enum TYPE_ENUM {
        IMAGE_ENUM,
        TEXT_ENUM
    }

    public static class TextHolder extends RecyclerView.ViewHolder {
        TextView text_top, text_bottom;
        ImageView icon;

        public TextHolder(@NonNull View itemView) {
            super(itemView);
            text_top = (TextView) itemView.findViewById(R.id.tv_top);
            text_bottom = (TextView) itemView.findViewById(R.id.tv_bottom);
            icon = (ImageView) itemView.findViewById(R.id.iv_icon);
        }
    }

    public static class ImageHolder extends RecyclerView.ViewHolder {
        TextView textTop;
        EditText textBottom;
        ImageView editIcon;

        public ImageHolder(@NonNull View itemView) {
            super(itemView);
            textTop = (TextView) itemView.findViewById(R.id.tv_top);
            textBottom = (EditText) itemView.findViewById(R.id.tv_bottom);
            editIcon = (ImageView) itemView.findViewById(R.id.iv_image_left);
        }
    }

    List<String> mDatas_top = new ArrayList<>();
    List<String> mData_bottom = new ArrayList<>();
    List<String> mData_new = new ArrayList<>();

    public MainRecyclerAdapter(List<String> mDatas_top, List<String> mData_bottom, List<String> mData_new, Context context) {
        this.mDatas_top = mDatas_top;
        this.mData_bottom = mData_bottom;
        this.mData_new = mData_new;
        this.context = context;
    }

    //判斷是哪一類型的adapter
    @Override
    public int getItemViewType(int position) {
        return position % 2 == 0 ? TYPE_ENUM.IMAGE_ENUM.ordinal() : TYPE_ENUM.TEXT_ENUM.ordinal();
    }

    //getItemViewType的返回值就是該方法的int i,因此經過i判斷是哪一類型的adapter
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        if (i == TYPE_ENUM.IMAGE_ENUM.ordinal()) {
            return new ImageHolder(LayoutInflater.from(context).inflate(R.layout.recycleritemnew, viewGroup, false));
        } else {
            return new TextHolder(LayoutInflater.from(context).inflate(R.layout.recycler_item, viewGroup, false));
        }
    }

    //根據不一樣類型的holder進行不一樣操做
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int i) {
        if (holder instanceof ImageHolder) {
            //((ImageHolder) holder).textTop.setText(mData_new.get(i));
            ((ImageHolder) holder).textBottom.setText("haoya");
            ((ImageHolder) holder).editIcon.setBackgroundResource(R.mipmap.ic_launcher_round);
        } else {
            ((TextHolder) holder).text_top.setText(mDatas_top.get(i));
            ((TextHolder) holder).text_bottom.setText(mData_bottom.get(i));
            ((TextHolder) holder).icon.setBackgroundResource(R.mipmap.ic_launcher);
            //holder.itemView.setBackgroundResource(R.mipmap.ic_launcher);
        }
    }

    @Override
    public int getItemCount() {
        return mDatas_top.size();
    }
}
複製代碼

代碼有點長。先不說不一樣類型的holder,先說單一類型的,用RecyclerView要怎麼實現。 RecyclerView的實現相對於咱們熟悉的listview來講確實複雜了一點點的。好嘞~如今詳細說說各部分。

(1)先寫一下列表每一行想要有什麼佈局,個人是上字下字右圖,圖是固定的。這個的話就不貼圖了,看你本身須要。

(2)通常嘛,要在java文件裏將xml裏面的東西加載出來,那麼RecyclerView裏誰完成了這個任務呢?,沒錯就是他啦~建立holder的視圖。

@Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        if (i == TYPE_ENUM.IMAGE_ENUM.ordinal()) {
            return new ImageHolder(LayoutInflater.from(context).inflate(R.layout.recycleritemnew, viewGroup, false));
        } else {
            return new TextHolder(LayoutInflater.from(context).inflate(R.layout.recycler_item, viewGroup, false));
        }
    }
複製代碼

(3)那麼建立好了,就得綁定是吧,嗯嗯,這裏

@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int i) {
        if (holder instanceof ImageHolder) {
            //((ImageHolder) holder).textTop.setText(mData_new.get(i));
            ((ImageHolder) holder).textBottom.setText("haoya");
            ((ImageHolder) holder).editIcon.setBackgroundResource(R.mipmap.ic_launcher_round);
        } else {
            ((TextHolder) holder).text_top.setText(mDatas_top.get(i));
            ((TextHolder) holder).text_bottom.setText(mData_bottom.get(i));
            ((TextHolder) holder).icon.setBackgroundResource(R.mipmap.ic_launcher);
            //holder.itemView.setBackgroundResource(R.mipmap.ic_launcher);
        }
    }
複製代碼

根據你xml裏面的控件,進行設置。一種類型的話,就直接這樣就行了 代碼爲舉例,具體你懂的

holder.itemView.setBackgroundResource(R.mipmap.ic_launcher);
複製代碼

還有什麼呢看一下,噢~綁定視圖的時候數據從哪來,是的,從構造函數來。就是說將準備好的數據這樣 MainRecyclerAdapter mainRecyclerAdapter = new MainRecyclerAdapter(data_top, data_bottom, data_new, getContext()); 再這樣,

List<String> mDatas_top = new ArrayList<>();
    List<String> mData_bottom = new ArrayList<>();
    List<String> mData_new = new ArrayList<>();

    public MainRecyclerAdapter(List<String> mDatas_top, List<String> mData_bottom, List<String> mData_new, Context context) {
        this.mDatas_top = mDatas_top;
        this.mData_bottom = mData_bottom;
        this.mData_new = mData_new;
        this.context = context;
    }
複製代碼

最後這樣,

((TextHolder) holder).text_top.setText(mDatas_top.get(i));
            ((TextHolder) holder).text_bottom.setText(mData_bottom.get(i));
            ((TextHolder) holder).icon.setBackgroundResource(R.mipmap.ic_launcher);
            
複製代碼

哇文章這麼長啦。應該完了吧。欸不對好像還少了點什麼嗯嗯,咱可不能遺忘了他的存在,他但是頗有存在感的呢~ 它!是繼承了viewholder的一個住在adapter類裏面的類,做用是將xml的控件findviewbyid。

public static class TextHolder extends RecyclerView.ViewHolder {
        TextView text_top, text_bottom;
        ImageView icon;

        public TextHolder(@NonNull View itemView) {
            super(itemView);
            text_top = (TextView) itemView.findViewById(R.id.tv_top);
            text_bottom = (TextView) itemView.findViewById(R.id.tv_bottom);
            icon = (ImageView) itemView.findViewById(R.id.iv_icon);
        }
    }
複製代碼

有一個點,這裏的話,若是是一個holder而已,畫框部分能夠寫成MainRecyclerAdapter.VH--VH是你的內部類名。

public class MainRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
複製代碼

我我的對這個adapter的理解,首先他繼承了RecyclerView.adapter,實現了幾個必要的方法,加了個內部類

holder的話,我以爲是一個兜,兜住了視圖的控件。 oncreateviewholder的話,那麼就是建立出這個holder。 onbindviewholder的話,就是將控件須要的數據源給到對應的控件。

有可能理解得不許確,但願你們不吝賜教。

好了呢,單類型的adapter就這樣啦。單類型的意思就是說RecyclerView從頭至尾,控件的位置類型都是一致的,同種風格。可是有的時候,也會遇到須要不一樣類型的需求。這個時候就要出動另外一個父類方法getItemViewType(int position),在這裏面根據position設置這一行須要的是什麼類型的adapter,詳細見上邊的完整代碼。相關地方有註解。相信各位能夠看懂~

完結!

做者介紹

  • 楊曉華:廣州蘆葦科技 APP 團隊 Android 開發實習生

內推信息

  • 咱們正在招募小夥伴,有興趣的小夥伴能夠把簡歷發到 app@talkmoney.cn,備註:來自掘金社區
  • 詳情能夠戳這裏--> 廣州蘆葦信息科技
相關文章
相關標籤/搜索