專案地址:github.com/razerdp/Fri… (能弱弱的求個star或者fork麼QAQ)
【Warning】:
本篇完整的從思考->尋找->編寫程式碼->最終完成
來闡述我如何實現本篇預覽圖功能
本篇篇幅較長,請帶上一定的耐心
本篇圖片較多,流量黨請注意
本篇比較抽象,我會盡量形象的闡述
本篇預覽圖:
前言
正如上一篇文章所說,一個app動人之處在於細節的研磨和富有動感的互動。
在微信的朋友圈,我們點選圖片可以感覺到像預覽圖那樣的效果:點選某張圖片,然後它會放大到全屏,再點選,則會縮小到原來的那個地方
這種互動看起來非常贊,最起碼看得順眼。
然而很多時候互動動作設計的時候看起來確實很棒,但對於我等程式設計師來說,設計棒,設計酷往往會讓我們擺出一張苦逼臉
—— 臣妾做不到啊,陛下。
但迫於Money的壓力下,我們往往不得不硬著頭皮上。
正如今天這個效果,確實一開始是沒有任何頭緒,在思考實現的過程中,我曾經想過如下幾種方法:
- 直接重寫一個ImageView,利用martix來放大圖片
- 點選的時候,通過windowmanager動態新增一個imageview,然後讓這個imageview實現動畫
- 弄兩個view,一個隱藏的,一個是listview中的,然後點選的時候把隱藏的顯示,並進行動畫。
- 甚至想過,直接上activityoptions....使用activity的轉場動畫
但實際上,以上的方法貌似都可以,但實際上真要我去幹了,就猶豫了,且不說執行效率,但起碼可以預測到程式碼量。。。
然而,一次神奇的發現,讓我解決了這個問題,準確的說,是谷歌早就解決了這個問題。
羽翼君探索篇
發現
對於面向搜尋引擎程式設計的我們,其實一直都習慣於有問題找度娘,或者找谷歌。
鑑於度娘找到的技術文章基本都是你抄我,我抄你,於是我只好到谷歌以**"android scale a view to full screen"**來找答案,奈何找來找去都是關於如何讓imageview的圖片填充整個螢幕的。
於是換個思路,除了scale,我們不是經常還能接觸到"zoom"這個關鍵詞麼,於是就繼續谷歌**"android zoom a view to full screen"**
結果第一個結果就是Android開發者文件的train專案:
點進去一看,瞬間滿滿的幸福感,原來頭疼了好久的問題,人家谷歌早就給出了答案
而且,不得不說的是,這個專案僅僅是在Android的培訓專案,相當於打遊戲第一關的新手教程那樣吧,具體地址可以點這裡(http://developer.android.com/intl/zh-cn/training/index.html )
事實上,在完成了這篇文章的效果後,我到官方培訓這裡看了幾次,於是決定,我必須要把這裡所有東西弄明白。
這裡真的要給谷歌一萬個贊。
難點
在得到官方培訓這個超級大外掛後,最難的地方其實已經沒有什麼障礙了,剩下的就是該如何適配到我們的專案中。
從我們日常使用朋友圈的經驗看,關於圖片點選放大會涉及到這麼幾個難點:
-
朋友圈的圖片是1~9張,那麼該如何確保ViewPager可以載入相等數量的圖片
-
點選圖片是否應該跳轉到新的Activity
-
假如我點選第一張圖片,在ViewPager滑倒第三張圖片,那麼點選圖片退出時該如何確保View縮小後的位置與第三張圖片一致而非縮小到第一張圖片的位置。
-
ViewPager瀏覽的時候圖片放大和縮小如何實現
在官方的demo中,僅僅只有一張圖片的瀏覽,也就是說僅僅是展示了一張圖片縮放到全屏的方法,所以我們只有去完全的理解demo,才能繼續我們的工程。
不過在真正實現之前,上面的問題我們其實可以回答幾個:
-
針對第一個問題,我們可以在點選圖片的時候,把當前圖片所在的Item的圖片地址陣列傳到adapter裡面然後通知更新
-
在我們使用朋友圈的時候,可以感覺點選圖片放大這個過程非常的快,而如果重新開啟一個Activity,則需要經過那麼多的onCreate等生命期方法,那麼肯定不會有這麼靈敏的反應,所以很明顯,這個ViewPager其實是包含在朋友圈所在的視窗,只是平時隱藏起來而已。
-
針對第三個問題在稍後的闡述中回答。
-
第四個問題,我覺得想都不用想,直接上PhotoView這個庫。
原理篇
官方Demo詳解
事實上,官方的demo中的註釋是十分的清楚的,官方的Demo最主要依靠的是兩個東西:
- getGlobalVisibleRect
- ObjectAnimator
我們都知道,一個View是可以通過getLocationOnScreen或者**getLocationInWindow **得到相對於整個螢幕/相對於父控制元件的xy位置資訊。
而getGlobalVisibleRect/getLocalVisibleRect跟上面的這個其實差不多,不過不同的是,它得到的不是xy位置資訊,而是得到指定View在螢幕中展示的矩形資訊
簡單的描述就是前者得到view的原點資訊,後者得到view的2D形狀資訊。
官方的demo則是通過這個得到兩個view的Rect:
- 點選的view的rect(startRect)
- 最終放大後的view的rect(endRect)
得到兩個view的矩形後,就可以得到雙方的縮放比。
通過這個比例,可以做的事情就很多了,官方的demo則是通過這個比例,來計算出以下的引數:
- 小圖的x位置和大圖的x位置,得到放大後View水平方向上應該偏移多少。
- 因為僅僅是view的縮放是不夠的,因為需要保證放大後的View處於螢幕中央,而不是說偏左偏右。
- 同理得到垂直方向的偏移。
- 得到水平方向的縮放比,也就是上面所說的縮放比。
- 同理得到垂直方向的縮放比。
得到這些引數後,就通過ObjectAnimator,操作的物件是隱藏在螢幕中的最終展示的View,通過監聽它的數值變化,從而不斷的更新展示的View的屬性,給人造成原來的view放大的錯覺。
或許文字說的有點枯燥,所以,直接上AE,弄出一個動圖,相信大家一看就明白:
在圖層的結構上如下動圖:
在點選的之後,會發生如下動作:
總結起來就是:
- 點選時,得到被點選的view的rect,和最終效果的rect
- 通過兩個rect計算縮放比率
- 動畫開始之前將最終展示的view設定為可見
- 由於ObjectAnimator開始的值是被點選的view的rect值,所以最終展示的view會從被點選的view的大小開始播放
- 隨著動畫進行,不斷的改變自己的x,y,scaleX,scaleY
- 從而達到讓人感覺view從小變大的錯覺
程式碼篇
Step 1- 佈局/MVP的方法新增
呼呼,又是AE,又是穹妹(手動斜眼)的,終於可以開始弄我們的程式碼了。
從上面我們知道,要實現這種偽放大效果,最重要的是得到開始和結束兩個view的rect,而我們由於是使用viewpager,所以我們穿值傳遞的是一個陣列,這個陣列就是當前Item所擁有的imageview的rect陣列。
因此回到我們的Activity,由於我們採用MVP模式,所以在View層增加一個方法:
public interface DynamicView {
...之前的方法不變
// 瀏覽圖片
void showPhoto(@NonNull ArrayList<String> photoAddress, @NonNull ArrayList<Rect> originViewBounds, int
curSelectedPos);
}
複製程式碼
同樣在P層也增加這個方法,這裡就不貼上來了。
接下啦到我們朋友圈的佈局中,新增一個viewpager。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/photo_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:visibility="invisible">
<razerdp.friendcircle.widget.HackyViewPager
android:id="@+id/photo_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
複製程式碼
值得注意的是,因為微信朋友圈的大圖瀏覽是有背景(黑色)的,所以我們外層用一個佈局包裹。
另外由於我們需要使用PhotoView,所以我們的ViewPager將會使用PhotoView作者給出的解決方法:
HackyViewPager程式碼如下(因為有LICENSE,所以就完整貼出):
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* Found at http://stackoverflow.com/questions/7814017/is-it-possible-to-disable-scrolling-on-a-viewpager.
* Convenient way to temporarily disable ViewPager navigation while interacting with ImageView.
*
* Julia Zudikova
*/
/**
* Hacky fix for Issue #4 and
* http://code.google.com/p/android/issues/detail?id=18990
* <p/>
* ScaleGestureDetector seems to mess up the touch events, which means that
* ViewGroups which make use of onInterceptTouchEvent throw a lot of
* IllegalArgumentException: pointerIndex out of range.
* <p/>
* There's not much I can do in my code for now, but we can mask the result by
* just catching the problem and ignoring it.
*
* @author Chris Banes
*/
public class HackyViewPager extends ViewPager {
private boolean isLocked;
public HackyViewPager(Context context) {
super(context);
isLocked = false;
}
public HackyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
isLocked = false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isLocked) {
try {
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
}
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return !isLocked && super.onTouchEvent(event);
}
public void toggleLock() {
isLocked = !isLocked;
}
public void setLocked(boolean isLocked) {
this.isLocked = isLocked;
}
public boolean isLocked() {
return isLocked;
}
}
複製程式碼
在佈局弄好後,我們將它include到我們的朋友圈activity,於是目前的層次如下:
我們的viewpager在listview的上方
Step 2 - ViewPager的adapter
adapter很明顯,就是為了實現我們的所有方法的,在adapter的設計中,我們需要知道幾個地方:
-
ViewPager的adapter如果直接呼叫adapter.notifydatasetchanged是未必能重新整理的,這個跟getItemPosition方法有關,所以如果想adapter重新整理,就需要覆寫這個。
- 本例的adapter重新整理不使用notifydatasetchanged,而是直接setAdapter,viewpager的setAdapter方法會觸發destroyItem,所以我們直接使用setAdapter
-
adapter中,我們只管檢視的渲染,不管事件的處理,事件的處理我們通過介面拋到外部處理。
因此我們的adapter將會這麼設計:
/**
* Created by 大燈泡 on 2016/4/12.
* 圖片瀏覽視窗的adapter
*/
public class PhotoBoswerPagerAdapter extends PagerAdapter {
private static final String TAG = "PhotoBoswerPagerAdapter";
//=============================================================datas
private ArrayList<String> photoAddress;
private ArrayList<Rect> originViewBounds;
//=============================================================bounds
private Context mContext;
private LayoutInflater mLayoutInflater;
public PhotoBoswerPagerAdapter(Context context) {
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
photoAddress = new ArrayList<>();
originViewBounds = new ArrayList<>();
}
public void resetDatas(@NonNull ArrayList<String> newAddress, @NonNull ArrayList<Rect> newOriginViewBounds)
throws IllegalArgumentException {
if (newAddress.size() != newOriginViewBounds.size() || newAddress.size() <= 0 ||
newOriginViewBounds.size() <= 0) {
throw new IllegalArgumentException("圖片地址和圖片的位置快取不對等或某一個為空");
}
photoAddress.clear();
originViewBounds.clear();
photoAddress.addAll(newAddress);
originViewBounds.addAll(newOriginViewBounds);
}
@Override
public int getCount() {
return photoAddress.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
return null;
}
int[] pos = new int[1];
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
//=============================================================點選消失的interface
private OnPhotoViewClickListener mOnPhotoViewClickListener;
public OnPhotoViewClickListener getOnPhotoViewClickListener() {
return mOnPhotoViewClickListener;
}
public void setOnPhotoViewClickListener(OnPhotoViewClickListener onPhotoViewClickListener) {
mOnPhotoViewClickListener = onPhotoViewClickListener;
}
public interface OnPhotoViewClickListener {
void onPhotoViewClick(View view, Rect originBound, int curPos);
}
}
複製程式碼
在adapter中,我們存放著這些引數:
- photoAddress:存放圖片地址的陣列
- originViewBounds:點選時所處item的所有imageview的rect
然後還有我們內部定義的介面:OnPhotoViewClickListener,這個介面在點選Viewpager裡面的PhotoView時會觸發。
Step 3 - PhotoPagerManager
在adapter初步結構設計後,我們暫時先不管,接下來我們需要處理的就是縮放動畫和點選的事件處理。
由於我們的Activity作為MVP的View,程式碼量已經比較多了,所以我們將動畫的實現和點選事件的處理封裝到另一個類裡,委託它進行操作。
在設計這個類之前,我們需要確定一下需要的委託管理的東西:
- adapter:負責處理adapter內部PhotoView點選時回撥的動作
- pager:需要pager的setAdapter來進行重新整理以及setCurrentItem來定位到我們點選的圖片位於圖片序列的位置
- container:就是佈局裡包裹著ViewPager的RelativeLayout,我們通過它做一個點選消逝時的透明度漸變動畫,同時endRect也是依靠它來得到
由此,我們初步設計以下結構:
/**
* Created by 大燈泡 on 2016/4/12.
* 相簿展示的管理類
*/
public class PhotoPagerManager implements PhotoBoswerPagerAdapter.OnPhotoViewClickListener {
private Context mContext;
private PhotoBoswerPagerAdapter adapter;
private HackyViewPager pager;
private Rect finalBounds;
private Point globalOffset;
private View container;
//私有構造器
private PhotoPagerManager(Context context, HackyViewPager pager, View container) {
if (container != null) {
finalBounds = new Rect();
globalOffset = new Point();
this.mContext = context;
this.container = container;
this.pager = pager;
adapter = new PhotoBoswerPagerAdapter(context);
adapter.setOnPhotoViewClickListener(this);
}
else {
throw new IllegalArgumentException("PhotoPagerManager >>> container不能為空哦");
}
}
//靜態工廠
public static PhotoPagerManager create(Context context, HackyViewPager pager, View container) {
return new PhotoPagerManager(context, pager, container);
}
//共有呼叫方法,傳入圖片地址和view的可見矩形陣列
public void showPhoto(
@NonNull ArrayList<String> photoAddress, @NonNull ArrayList<Rect> originViewBounds, int curSelectedPos) {
}
//當前正在進行的動畫,如果動畫沒展示完,就將其取消以執行下一個動畫
private AnimatorSet curAnimator;
//私有showPhoto處理
private void showPhotoPager(@NonNull ArrayList<Rect> originViewBounds, int curSelectedPos) {
}
//pager的PhotoView點選回撥,用於執行消失時的縮小動畫
@Override
public void onPhotoViewClick(View view, Rect originBound, int curPos) {
}
//計算縮放比率
private float calculateRatio(Rect startBounds, Rect finalBounds) {
}
//銷燬
public void destroy() {
adapter.destroy();
mContext = null;
adapter = null;
pager = null;
finalBounds = null;
globalOffset = null;
container = null;
}
}
複製程式碼
可以看得出,我們的重頭戲全在showPhoto裡面
在私有構造器裡面我們將需要的成員進行賦值,同時adapter需要實現我們在第二步定義的介面。
接下來我們補充共有的showPhoto方法:
public void showPhoto(
@NonNull ArrayList<String> photoAddress, @NonNull ArrayList<Rect> originViewBounds, int curSelectedPos) {
adapter.resetDatas(photoAddress, originViewBounds);
pager.setAdapter(adapter);
pager.setCurrentItem(curSelectedPos);
pager.setLocked(photoAddress.size() == 1);
container.getGlobalVisibleRect(finalBounds, globalOffset);
showPhotoPager(originViewBounds, curSelectedPos);
}
複製程式碼
每次呼叫show方法我們都需要重新整理adapter的資料,然後使用setAdapter來進行重新整理。
接下來判斷傳進來的圖片是否只有一張,如果只有一張,就不允許viewpager滑動,setLocked方法是PhotoView作者給出的解決方案帶有的,其原理是在Viewpager的onInterceptTouchEvent裡通過locked來決定是否攔截事件。
container.getGlobalVisibleRect(finalBounds, globalOffset);
這個在上面的解釋裡已經有,這裡只是直接copy官方demo程式碼而已。
最後呼叫私有方法:showPhotoPager
private void showPhotoPager(@NonNull ArrayList<Rect> originViewBounds, int curSelectedPos) {
Rect startBounds = originViewBounds.get(curSelectedPos);
startBounds.offset(-globalOffset.x, -globalOffset.y);
finalBounds.offset(-globalOffset.x, -globalOffset.y);
float ratio = calculateRatio(startBounds, finalBounds);
pager.setPivotX(0);
pager.setPivotY(0);
container.setVisibility(View.VISIBLE);
container.setAlpha(1.0f);
final AnimatorSet set = new AnimatorSet();
set.play(ObjectAnimator.ofFloat(pager, View.X, startBounds.left, finalBounds.left))
.with(ObjectAnimator.ofFloat(pager, View.Y, startBounds.top, finalBounds.top))
.with(ObjectAnimator.ofFloat(pager, View.SCALE_X, ratio, 1f))
.with(ObjectAnimator.ofFloat(pager, View.SCALE_Y, ratio, 1f));
set.setDuration(300);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
curAnimator = set;
}
@Override
public void onAnimationEnd(Animator animation) {
curAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
curAnimator = null;
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
set.start();
}
複製程式碼
這裡跟官方的程式碼基本一致,因為官方程式碼有註釋,所以這裡就不詳細闡述了。
不過值得留意的是,在動畫執行之前必須要將container的alpha設回1,因為我們在退出動畫裡將它設定為0的。
同理,在PhotoView點選回撥裡,我們也寫出差不多的程式碼:
@Override
public void onPhotoViewClick(View view, Rect originBound, int curPos) {
//如果展開動畫沒有展示完全就關閉,那麼就停止展開動畫進而執行退出動畫
if (curAnimator != null) {
curAnimator.cancel();
}
container.getGlobalVisibleRect(finalBounds, globalOffset);
originBound.offset(-globalOffset.x, -globalOffset.y);
finalBounds.offset(-globalOffset.x, -globalOffset.y);
float ratio = calculateRatio(originBound, finalBounds);
pager.setPivotX(0);
pager.setPivotY(0);
final AnimatorSet set = new AnimatorSet();
set.play(ObjectAnimator.ofFloat(pager, View.X, originBound.left))
.with(ObjectAnimator.ofFloat(pager, View.Y, originBound.top))
.with(ObjectAnimator.ofFloat(pager, View.SCALE_X, 1f, ratio))
.with(ObjectAnimator.ofFloat(pager, View.SCALE_Y, 1f, ratio))
.with(ObjectAnimator.ofFloat(container, View.ALPHA, 1.0f, 0.0f));
set.setDuration(300);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
curAnimator = set;
}
@Override
public void onAnimationEnd(Animator animation) {
curAnimator = null;
container.clearAnimation();
container.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationCancel(Animator animation) {
curAnimator = null;
container.clearAnimation();
container.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
set.start();
}
複製程式碼
在退出的動畫裡,我們需要將SCALE_X和SCALE_Y的動畫起始值和目標值替換
- 在放大動畫裡,我們是從小->大,即計算出來的比率->1f
- 在縮小動畫則相反,從大到小
最後補全,哦,不,是copy官方的計算比率的方法:
private float calculateRatio(Rect startBounds, Rect finalBounds) {
float ratio;
if ((float) finalBounds.width() / finalBounds.height() > (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
ratio = (float) startBounds.height() / finalBounds.height();
float startWidth = ratio * finalBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
}
else {
// Extend start bounds vertically
ratio = (float) startBounds.width() / finalBounds.width();
float startHeight = ratio * finalBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
}
return ratio;
}
複製程式碼
官方的計算方法是這樣的:
- 比較最終view的寬高比和起始view的寬高比
- 無論是那種,都需要計算出差值,這個差值用來定位最終view動畫播放時的起始位置,讓其保證跟起始view一致
在這個類完成後,我們在Activity裡僅僅需要兩句話呼叫:
/**
* Created by 大燈泡 on 2016/2/25.
* 朋友圈demo視窗
*/
public class FriendCircleDemoActivity extends FriendCircleBaseActivity
implements DynamicView, View.OnClickListener, OnSoftKeyboardChangeListener {
... 成員變數略
//圖片瀏覽的pager manager
private PhotoPagerManager mPhotoPagerManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
...略
initView();
...略
}
private void initView() {
...各種findViewById略
//初始化我們的manager
mPhotoPagerManager = PhotoPagerManager.create(this, (HackyViewPager) findViewById(R.id.photo_pager),
findViewById(R.id.photo_container));
}
...其他方法略
@Override
public void showPhoto(
@NonNull ArrayList<String> photoAddress, @NonNull ArrayList<Rect> originViewBounds, int curSelectedPos) {
//事件委託給manager
mPhotoPagerManager.showPhoto(photoAddress, originViewBounds, curSelectedPos);
}
}
複製程式碼
Step 4 - adapter程式碼補全
實現完manager後,我們就補全我們的adapter程式碼
在adapter裡面,我們主要關注兩個方法:
- instantiateItem:初始化view的時候回撥
- setPrimaryItem:滑動時回撥當前展示著的view
其他方法都是常規方法,就不展示了
初始化的時候,我們的程式碼非常簡單,new一個,add,完。。。
@Override
public Object instantiateItem(ViewGroup container, int position) {
PhotoView photoView=new PhotoView(mContext);
Glide.with(mContext).load(photoAddress.get(position)).into(photoView);
container.addView(photoView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
return photoView;
}
複製程式碼
在setPrimaryItem中,我們為photoView設定回撥:
int currentPos;
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
currentPos=position;
if (object instanceof PhotoView) {
PhotoView photoView = (PhotoView) object;
if (photoView.getOnViewTapListener() == null) {
photoView.setOnViewTapListener(new PhotoViewAttacher.OnViewTapListener() {
@Override
public void onViewTap(View view, float x, float y) {
if (mOnPhotoViewClickListener != null) {
mOnPhotoViewClickListener.onPhotoViewClick(view, originViewBounds.get(currentPos), currentPos);
}
}
});
}
}
}
複製程式碼
在這裡,我們留意到在回撥裡我們傳入的rect就是外部傳進來起始View的rect組,這裡就回答了我們疑點中的第三個問題:
點選某張圖片,滑動到其他圖片時,退出的縮小動畫如何縮小到對應的起始View中
我們的解決方法就是,把那個View的rect扔給我們的manager讓他計算,就好了。
Step 5 - Item裡使用
在目前的專案裡,事實上也是在微信朋友圈裡,圖片永遠都是0~9,在我們的專案中,因為ListView的Adapter高度抽象化,所以我們可以很輕鬆的在ViewHolder裡處理
在ItemWithImg.java中,我們針對GridView的onItemClick進行處理:
public class ItemWithImg extends BaseItemDelegate implements AdapterView.OnItemClickListener {
private static final String TAG = "ItemWithImg";
private NoScrollGridView mNoScrollGridView;
private GridViewAdapter mGridViewAdapter;
private ArrayList<String> mUrls = new ArrayList<>();
private ArrayList<Rect> mRects = new ArrayList<>();
...略
@Override
protected void bindData(int position, @NonNull View v, @NonNull MomentsInfo data, int dynamicType) {
if (data.content.imgurl == null || data.content.imgurl.size() == 0 || mNoScrollGridView == null) return;
mUrls.clear();
mUrls.addAll(data.content.imgurl);
...資料繫結
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final int childCount = parent.getChildCount();
mRects.clear();
try {
if (childCount >= 0) {
for (int i = 0; i < childCount; i++) {
View v = parent.getChildAt(i);
Rect bound = new Rect();
v.getGlobalVisibleRect(bound);
mRects.add(bound);
}
}
} catch (NullPointerException e) {
Log.e(TAG, "view可能為空哦");
}
getPresenter().shoPhoto(mUrls, mRects, position);
}
}
複製程式碼
這裡我們需要留意兩個地方:
- 在bindData裡面,因為這個是一個抽象化的ViewHolder介面,所以事實上會在ListView的getView中不斷的呼叫,而我們的url的arrayList是當前類的成員變數,所以我們每次都需要將其clear掉,否則資料只會累加,這樣造成的就是圖片數量與view的rect陣列不對等。
- ItemClick裡面,我們需要對parent拿到的view進行NPE捕獲,否則掛掉就不好玩了。
到這裡,我們的工作就完成了。
問題
花了那麼多時間,終於把這個效果完成了,事實上最麻煩的東西都封到了manager裡面,理論上來說要遷移到您的專案中也是非常簡單的。
但目前來說,我們僅僅是初步實現了,其實有一些小問題還是存在的:
- ViewPager的adapter裡面的view每次都是new,感覺有點浪費
- 由於上面的那個問題,導致假如我們在大於三張圖或者分別點選不同的item時,放大動畫會看不到,必須在載入一次圖片後再次觸發才會有。
- 不知道您有沒有發現,其實我們缺少一個指示器,畢竟微信朋友圈在ViewPager下方可是有幾個小點點的
雖然問題不是很大,但我們也有修復的理由對吧。
所以,在下一篇,我們將會針對這三個問題進行處理,以及關於PhotoView在ViewPager裡面爆出的 "ImageView no longer exists. You should not use this PhotoViewAttacher any more." 錯誤從而導致PhotoView的點選事件無響應的處理方法。
敬請期待-V-