前兩天想熟悉一下tablayout,因此就弄了個特別簡單的tablayout+viewpager+fragmet+recyclerview結合的小demo.雖然特別簡單,但仍是遇到了很多問題。java
tablayout顯示標題,viewpager實現滑動,fragment裝載數據,recyclerview顯示數據。android
implementation 'com.android.support:appcompat-v7:xxx' implementation 'com.android.support:design:xxx' implementation 'com.android.support:recyclerview-v7:xxx'bash
<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);
複製代碼
xml引入ide
<android.support.v4.view.ViewPager
複製代碼
java文件中函數
viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentList));
複製代碼
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,介紹一下你是幹嗎的~
若是你是想在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,備註:來自掘金社區
- 詳情能夠戳這裏--> 廣州蘆葦信息科技