專案中遇到了ViewPager+Fragment,特此記錄一下踩過的坑,
預加載的一些問題
之前都不知道ViewPager有一個特殊的功能,預加載,會預加載臨近的界面,讓滑動更加流暢,ViewPager還提供一個方法來設定預加載的界面數,
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();
}
}
通過原始碼發現當你設定的值小于默認的值的時候,會自動設定成默認的值,
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
由于默認值為1,想要關閉預加載功能是不能通過這個方法實作的,這是我踩過的坑之一,
需求是需要改變界面的背景顏色,遇到一個問題就是當前的界面的顏色改變了,但是相鄰的界面背景顏色沒有改變,因為預加載過了,相鄰界面的UI不會改變,
但是有人就會說到adapter不是有notifyDataSetChanged()方法嗎?直接呼叫這個方法更新一下不就可以嗎?我曾經也是這么認為的,踩過的坑之一,直接呼叫notifyDataSetChanged()是不會更新的,這和recycleView不同,
至于為什么不會更新,就來跟著原始碼一起看看,
public void notifyDataSetChanged() {
synchronized (this) {
if (mViewPagerObserver != null) {
mViewPagerObserver.onChanged();
}
}
mObservable.notifyChanged();
}
有個同步鎖的方法和一個notifyChanged()方法,不管加鎖的方法,進入到notifyChanged()方法去看看,
/**
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*/
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
mObservers這個變數里肯定存放了ViewPager的資料資訊,看官方的注釋資訊,翻譯過來就是當資料集的內容更改時呼叫,下次查詢時,將獲得新資料,這里依然不知道資料是怎么改變的,接著進入onChanged()方法看看,
public abstract class DataSetObserver {
public void onChanged() {
// Do nothing
}
public void onInvalidated() {
// Do nothing
}
}
直接一個抽象類,那只能找它的子類了,
private class PagerObserver extends DataSetObserver {
PagerObserver() {
}
@Override
public void onChanged() {
dataSetChanged();
}
@Override
public void onInvalidated() {
dataSetChanged();
}
}
再進入dataSetChanged()方法里看看,
void dataSetChanged() {
// This method only gets called if our observer is attached, so mAdapter is non-null.
final int adapterCount = mAdapter.getCount();
mExpectedAdapterCount = adapterCount;
boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
&& mItems.size() < adapterCount;
int newCurrItem = mCurItem;
boolean isUpdating = false;
for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
final int newPos = mAdapter.getItemPosition(ii.object);
if (newPos == PagerAdapter.POSITION_UNCHANGED) {
continue;
}
if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
if (!isUpdating) {
mAdapter.startUpdate(this);
isUpdating = true;
}
mAdapter.destroyItem(this, ii.position, ii.object);
needPopulate = true;
if (mCurItem == ii.position) {
// Keep the current item in the valid range
newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
needPopulate = true;
}
continue;
}
if (ii.position != newPos) {
if (ii.position == mCurItem) {
// Our current item changed position. Follow it.
newCurrItem = newPos;
}
ii.position = newPos;
needPopulate = true;
}
}
if (isUpdating) {
mAdapter.finishUpdate(this);
}
Collections.sort(mItems, COMPARATOR);
if (needPopulate) {
// Reset our known page widths; populate will recompute them.
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.isDecor) {
lp.widthFactor = 0.f;
}
}
setCurrentItemInternal(newCurrItem, false, true);
requestLayout();
}
}
只關注中間那個for回圈,看到兩個if判斷,
if (newPos == PagerAdapter.POSITION_UNCHANGED) {
continue;
}
if (newPos == PagerAdapter.POSITION_NONE) {
...
continue;
}
看來找到了不會更新的原因了,newPos通過getItemPosition()方法賦值,而getItemPosition()默認回傳POSITION_UNCHANGED,所以在判斷中就不會往下執行別的代碼邏輯了,
/**
* Called when the host view is attempting to determine if an item's position
* has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given
* item has not changed or {@link #POSITION_NONE} if the item is no longer present
* in the adapter.
*/
public int getItemPosition(@NonNull Object object) {
return POSITION_UNCHANGED;
}
既然發現了問題的原因,就可以想到解決的辦法了,那就是重寫getItemPosition()方法,重寫之前先了解這個方法的作用,
看到官方的注釋,翻譯為在宿主視圖試圖確定專案的位置是否已更改時呼叫,如果給定專案的位置未更改,則回傳{@link #POSITION_UNCHANGED};如果配接器中不再存在該專案,則回傳{@link #POSITION_NONE},
那么直接重寫讓getItemPosition()回傳POSITION_NONE,再呼叫notifyDataSetChanged()方法,至此我的問題解決了,雖然效率低下,哈哈哈哈,
懶加載的一些問題
實作懶加載就是需要重寫setUserVisibleHint(boolean isVisibleToUser)方法,當界面對用戶可見時,再從網路加載資料,展示到界面上,
實作的方式都差不多一樣的,我這里就不在重復了,網上搜搜很多博客和帖子,
至于懶加載需要注意的就是setUserVisibleHint(boolean isVisibleToUser)會呼叫很多次,一般都是在界面創建前呼叫,就是在onViewCreated()之前執行,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/261007.html
標籤:其他
上一篇:Git常用忽略檔案
下一篇:實作進度條自增長及漸變樣式
