一刃麸、ViewPager2介紹
1 簡(jiǎn)介
ViewPager2是Google 在 androidx 組件包里增加的一個(gè)組件帚桩,目前已經(jīng)到了1.0.0-beta02版本。
谷歌為什么要出這個(gè)組件呢锌奴?官方是這么說的:
ViewPager2 replaces ViewPager, addressing most of its predecessor’s pain-points,
including right-to-left layout support, vertical orientation, modifiable Fragment collections, etc.
2 具體改動(dòng):
New features:
支持豎向滾動(dòng)
完整支持notifyDataSetChanged
能夠關(guān)閉用戶輸入 (setUserInputEnabled, isUserInputEnabled)
API changes:
FragmentStateAdapter 替代 FragmentStatePagerAdapter
RecyclerView.Adapter 替代 PagerAdapter
registerOnPageChangeCallback 替代 addPageChangeListener
3 附上官方鏈接:
官方文檔
https://developer.android.google.cn/jetpack/androidx/releases/viewpager2#1.0.0-alpha01
官方Demo
https://github.com/googlesamples/android-viewpager2
二趁蕊、ViewPager2的使用
1. 準(zhǔn)備工作
- AndroidX適配參考文檔:
https://developer.android.com/jetpack/androidx/migrate
http://www.reibang.com/p/41de8689615d - 修改gradle.properties
android.useAndroidX=true
android.enableJetifier=true
android.useAndroidX=true 表示當(dāng)前項(xiàng)目啟用 AndroidX
android.enableJetifier=true 表示將依賴包也遷移到AndroidX 伊者。如果取值為 false ,表示不遷移依賴包到AndroidX,但在使用依賴包中的內(nèi)容時(shí)可能會(huì)出現(xiàn)問題胁赢,當(dāng)然了企蹭,如果你的項(xiàng)目中沒有使用任何三方依賴,那么智末,此項(xiàng)可以設(shè)置為 false
- 依賴庫(kù)
implementation 'androidx.appcompat:appcompat:1.1.0-alpha05'
implementation 'com.android.support:design:28.0.0'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
2. xml文件
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" />
3. 常用Api
- void setOrientation(int orientation)
- void setUserInputEnabled(boolean enabled)
- int getCurrentItem()
- void setCurremt(int item)
- void addItemDecoration(RecyclerView.ItemDecoration decor)
- void addItemDecoration(RecyclerView.ItemDecoration decor, int index)
- void beginFakeDrag()
- endFakeDrag()
- getAdapter()
- setOffscreenPageLimit(int limit)
- setPageTransformer(ViewPager2.PageTransformer transformer)
- registerOnPageChangeCallback(OnPageChangeCallback).
- unregisterOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback)
4.ViewPager2的Demo
-
ViewPager2 with Views
viewPager2 = findViewById(R.id.viewpager2);
viewPager2.setAdapter(new ViewPagerAdapter());
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.CardViewHolder> {
...
@NonNull
@Override
public ViewPagerAdapter.CardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new CardViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewPagerAdapter.CardViewHolder holder, int position) {
holder.textView.setText(mDatas.get(position));
}
@Override
public int getItemCount() {
return mDatas.size();
}
public static class CardViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public CardViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv_content);
}
}
}
是不是很簡(jiǎn)單谅摄?adapter和使用RecyclerView是一樣的,這個(gè)大家都很熟悉了吧系馆?
-
ViewPager2 with Fragments
viewPager.setAdapter(new ViewPagerFragmentStateAdapter(),colors);
public class ViewPagerFragmentStateAdapter extends FragmentStateAdapter {
@NonNull
@Override
public Fragment createFragment(int position) {
return PageFragment.newInstance(colors, position);
}
@Override
public int getItemCount() {
return colors.size();
}
}
ViewPager2和Fragment結(jié)合使用送漠,需要使用FragmentStateAdapter。FragmentStateAdapter繼承RecyclerView.Adapter它呀,有興趣的可以去看看源碼螺男。
-
ViewPager2 with TabLayout
mViewPager2.setAdapter(adapter);
new TabLayoutMediator(mTabLayout, mViewPager2, (tab, position) -> tab.setText(titles.get(position))).attach();
// 滑動(dòng)監(jiān)聽
mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
}
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
androidx中,TabLayout沒有setupWithViewPager(ViewPager2 viewPager2)方法纵穿,而是用TabLayoutMediator將TabLayout和ViewPager2結(jié)合下隧。
- 幾個(gè)api的使用示例和效果
- void setOffscreenPageLimit(boolean enable)
- void setUserInputEnabled(boolean enable)
- void beginFakeDrag()
- void notifyDataSetChanged();
Demo: ViewMutableActivity.java
public class ViewMutableActivity extends AppCompatActivity implements View.OnClickListener {
...
private void initViews() {
...
landscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
adapter = new MuTableViewPagerAdapter(this, model);
mViewPager2.setAdapter(adapter);
}
private void setListener() {
...
CheckBox checkBox = findViewById(R.id.disable_user_input_checkbox);
checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
mViewPager2.setUserInputEnabled(false);
} else {
mViewPager2.setUserInputEnabled(true);
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonUpdate:
model.update(mViewPager2.getCurrentItem(), "update content");
adapter.notifyItemChanged(mViewPager2.getCurrentItem());
break;
case R.id.buttonAddBefore:
int oldPosition = mViewPager2.getCurrentItem();
String content = model.getData(oldPosition);
model.add(oldPosition, "is new data");
adapter.notifyDataSetChanged();
if (model.contains(content)) {
int newPositin = model.getPosition(content);
mViewPager2.setCurrentItem(newPositin,false);
}
break;
case R.id.buttonAddAfter:
int oldPosition1 = mViewPager2.getCurrentItem();
String content1 = model.getData(oldPosition1);
model.add(oldPosition1 + 1, "is new data");
adapter.notifyDataSetChanged();
if (model.contains(content1)) {
int newPositin = model.getPosition(content1);
mViewPager2.setCurrentItem(newPositin,false);
}
break;
case R.id.buttonRemove:
if(!TextUtils.isEmpty(editText.getText().toString())){
int oldPosition2 = Integer.parseInt(editText.getText().toString());
if(oldPosition2 < model.getSize()){
model.removeData(oldPosition2);
adapter.notifyDataSetChanged();
}
}
break;
case R.id.tv_vertical:
mViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
break;
case R.id.tv_horizontal:
mViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
break;
case R.id.tv_scroll:
mViewPager2.setUserInputEnabled(true);
break;
case R.id.tv_unscroll:
mViewPager2.setUserInputEnabled(false);
break;
}
}
private final float getValue(MotionEvent event) {
return this.landscape ? event.getY() : event.getX();
}
private boolean handleOnTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastValue = getValue(event);
mViewPager2.beginFakeDrag();
break;
case MotionEvent.ACTION_MOVE:
float value = getValue(event);
float delta = value - lastValue;
mViewPager2.fakeDragBy(delta);
lastValue = value;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mViewPager2.endFakeDrag();
break;
}
return true;
}
}
三谓媒、ViewPager2到底好在哪里
1淆院、使用更加方便
通過ViewPager2的介紹可以看出,ViewPager2實(shí)現(xiàn)滑動(dòng)方向的切換句惯,禁止滑動(dòng)這些都有API土辩,開發(fā)者可以很方便的根據(jù)需求進(jìn)行修改。
而ViewPager則需要根據(jù)不同的情況抢野,重寫方法拷淘。
比如禁止滑動(dòng):
public class ScrollViewPager extends ViewPager {
...
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (isScroll) {
return super.onInterceptTouchEvent(event);
} else {
return false;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isScroll) {
return super.onTouchEvent(event);
} else {
return true;
}
}
2、性能上提升
- ViewPager2實(shí)現(xiàn)了懶加載和View復(fù)用指孤。
ViewPager2
/**
The given value must either be larger than 0, or {@code #OFFSCREEN_PAGE_LIMIT_DEFAULT(-1)}.
*/
public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = -1;
public void setOffscreenPageLimit(@OffscreenPageLimit int limit) {
if (limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) {
throw new IllegalArgumentException(
"Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number > 0");
}
mOffscreenPageLimit = limit;
// Trigger layout so prefetch happens through getExtraLayoutSize()
mRecyclerView.requestLayout();
}
ViewPager
public static final int DEFAULT_OFFSCREEN_PAGES = 1;
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
從源碼中可以看出启涯,ViewPager2的limit必須大于0或者是-1贬堵,而ViewPager的limit最小是1。VIewPager2可以不預(yù)加載结洼,通過Fragment的生命周期可以驗(yàn)證黎做。
- 刷新
ViewPager2支持局部刷新
notifyDataSetChanged();
notifyItemChanged(int position)
...
ViewPager 只能全局刷新
notifyDataSetChanged();
四、使用過程中的坑
- 官方ViewPager2 with TabLayout示例代碼閃退松忍,幾個(gè)意思蒸殿?
Caused by: java.lang.ClassCastException: Bootstrap method returned null
at com.google.android.material.tabs.TabLayout$TabView.addOnLayoutChangeListener(TabLayout.java:2592)
at com.google.android.material.tabs.TabLayout$TabView.update(TabLayout.java:2508)
at com.google.android.material.tabs.TabLayout$TabView.setTab(TabLayout.java:2437)
at com.google.android.material.tabs.TabLayout.createTabView(TabLayout.java:1501)
at com.google.android.material.tabs.TabLayout.newTab(TabLayout.java:855)
at com.google.android.material.tabs.TabLayoutMediator.populateTabsFromPagerAdapter(TabLayoutMediator.java:142)
at com.google.android.material.tabs.TabLayoutMediator.attach(TabLayoutMediator.java:118)
at com.example.myviewpager2.CardViewTabLayoutActivity.onCreate(CardViewTabLayoutActivity.kt:37)
at android.app.Activity.performCreate(Activity.java:7441)
at android.app.Activity.performCreate(Activity.java:7431)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1286)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3343)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3548)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:86)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2155)
at android.os.Handler.dispatchMessage(Handler.java:109)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:7539)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
解決:
'com.google.android.material:material:1.1.0-alpha05' 替代
'com.google.android.material:material:1.1.0-alpha08'
- 如果在已創(chuàng)建的項(xiàng)目使用androidX,先看看需要注意的點(diǎn)
- 這里推薦一個(gè)android.support向androidX遷移的文章
http://www.reibang.com/p/41de8689615d