Android仿頭條、微信大圖預(yù)覽視圖的方法詳解
圖片大圖預(yù)覽
在我現(xiàn)在的項(xiàng)目當(dāng)中,也存在大圖預(yù)覽的功能,但其實(shí)現(xiàn)過于繁重,采用一個(gè)Activity實(shí)現(xiàn),并且在圖片展示的過程中會產(chǎn)生卡頓感,整體感覺很是不好,正巧項(xiàng)目也在重構(gòu)過程中,所以決定將這一功能寫成一個(gè)成型的控件。
話不多說,先上圖看下效果。
整體實(shí)現(xiàn)思路
圖片展示:PhotoView(大圖支持雙擊放大)
圖片加載:Glide(加載網(wǎng)絡(luò)圖片、本地圖片、資源文件)
小圖變大圖時(shí)的實(shí)現(xiàn):動畫
圖片的下載:插入系統(tǒng)相冊
該控件采用自定義View的方式,通過一些基本的控件的組合,來形成一個(gè)具有大圖預(yù)覽的控件。上代碼
使用方法
(1)在布局文件中引用該view
<com.demo.gallery.view.GalleryView android:id="@+id/photo_gallery_view" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" app:animDuration="300" app:saveText="保存至相冊" app:saveTextColor="#987622"/>
(2)具體使用方法
GalleryView galleryView = findViewById(R.id.photo_gallery_view); galleryView.showPhotoGallery(index, List, ImageView);
到這里就結(jié)束了,就是這么簡單!
具體實(shí)現(xiàn)
(1)先從showPhotoGallery(index, List, ImageView)這個(gè)方法講起
int index:我們想要展示的一個(gè)圖片列表中的第幾個(gè)
List list: 我們要傳入的要展示的圖片類型list(支持網(wǎng)絡(luò)圖片、資源圖片、本地圖片(本地圖片與網(wǎng)絡(luò)圖片其實(shí)都是一個(gè)字符串地址))
public class GalleryPhotoModel { public Object photoSource; public GalleryPhotoModel(@DrawableRes int drawableRes) { this.photoSource = drawableRes; } public GalleryPhotoModel(String path) { this.photoSource = path; } }
ImageView:即你點(diǎn)擊想要展示的那個(gè)圖片
(2)對傳入GalleryView的數(shù)據(jù)進(jìn)行處理
/** * @param index 想要展示的圖片的索引值 * @param photoList 圖片集合(URL、Drawable、Bitmap) * @param clickImageView 點(diǎn)擊的第一個(gè)圖片 */ public void showPhotoGallery(int index, List<GalleryPhotoModel> photoList, ImageView clickImageView) { GalleryPhotoParameterModel photoParameter = new GalleryPhotoParameterModel(); //圖片 photoParameter.photoObj = photoList.get(index).photoSource; //圖片在list中的索引 photoParameter.index = index; int[] locationOnScreen = new int[2]; //圖片位置參數(shù) clickImageView.getLocationOnScreen(locationOnScreen); photoParameter.locOnScreen = locationOnScreen; //圖片的寬高 int width = clickImageView.getDrawable().getBounds().width(); int height = clickImageView.getDrawable().getBounds().height(); photoParameter.imageWidth = clickImageView.getWidth(); photoParameter.imageHeight = clickImageView.getHeight(); photoParameter.photoHeight = height; photoParameter.photoWidth = width; //scaleType photoParameter.scaleType = clickImageView.getScaleType(); //將第一個(gè)點(diǎn)擊的圖片參數(shù)連同整個(gè)圖片列表傳入 this.setVisibility(View.VISIBLE); post(new Runnable() { @Override public void run() { requestFocus(); } }); setGalleryPhotoList(photoList, photoParameter); }
通過傳遞進(jìn)來的ImageView,獲取被點(diǎn)擊View參數(shù),并拼裝成參數(shù)model,再進(jìn)行數(shù)據(jù)的相關(guān)處理。
(3)GalleryView的實(shí)現(xiàn)機(jī)制
該View的實(shí)現(xiàn)思路主要是:最外層是一個(gè)RelativeLayout,內(nèi)部有一個(gè)充滿父布局的ImageView和ViewPager。ImageView用來進(jìn)行圖片的動畫縮放,ViewPager用來進(jìn)行最后的圖片的展示。其實(shí)該View最主要的地方就是通過點(diǎn)擊ImageView到最后ViewPager的展示的動畫。接下來主要是講解一下這個(gè)地方。先看一下被點(diǎn)擊ImageView的參數(shù)Model。GalleryPhotoParameterModel
public class GalleryPhotoParameterModel { //索引 public int index; // 圖片的類型 public Object photoObj; // 在屏幕上的位置 public int[] locOnScreen = new int[]{-1, -1}; // 圖片的寬 public int photoWidth = 0; // 圖片的高 public int photoHeight = 0; // ImageView的寬 public int imageWidth = 0; // ImageView的高 public int imageHeight = 0; // ImageView的縮放類型 public ImageView.ScaleType scaleType; }
3.1圖片放大操作
private void handleZoomAnimation() { // 屏幕的寬高 this.mScreenRect = GalleryScreenUtil.getDisplayPixes(getContext()); //將被縮放的圖片放在一個(gè)單獨(dú)的ImageView上進(jìn)行單獨(dú)的動畫處理。 Glide.with(getContext()).load(firstClickItemParameterModel.photoObj).into(mScaleImageView); //開啟動畫 mScaleImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //開始放大操作 calculateScaleAndStartZoomInAnim(firstClickItemParameterModel); // mScaleImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); }
/** * 計(jì)算放大比例,開啟放大動畫 * * @param photoData */ private void calculateScaleAndStartZoomInAnim(final GalleryPhotoParameterModel photoData) { mScaleImageView.setVisibility(View.VISIBLE); // 放大動畫參數(shù) int translationX = (photoData.locOnScreen[0] + photoData.imageWidth / 2) - (int) (mScreenRect.width() / 2); int translationY = (photoData.locOnScreen[1] + photoData.imageHeight / 2) - (int) ((mScreenRect.height() + GalleryScreenUtil.getStatusBarHeight(getContext())) / 2); float scale = getImageViewScale(photoData); // 開啟放大動畫 executeZoom(mScaleImageView, translationX, translationY, scale, true, new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { showOtherViews(); tvPhotoSize.setText(String.format("%d/%d", viewPager.getCurrentItem() + 1, photoList.size())); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); }
3.2 圖片縮小操作
/** * 計(jì)算縮小比例,開啟縮小動畫 */ private void calculateScaleAndStartZoomOutAnim() { hiedOtherViews(); // 縮小動畫參數(shù) int translationX = (firstClickItemParameterModel.locOnScreen[0] + firstClickItemParameterModel.imageWidth / 2) - (int) (mScreenRect.width() / 2); int translationY = (firstClickItemParameterModel.locOnScreen[1] + firstClickItemParameterModel.imageHeight / 2) - (int) ((mScreenRect.height() + GalleryScreenUtil.getStatusBarHeight(getContext())) / 2); float scale = getImageViewScale(firstClickItemParameterModel); // 開啟縮小動畫 executeZoom(mScaleImageView, translationX, translationY, scale, false, new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { mScaleImageView.setImageDrawable(null); mScaleImageView.setVisibility(GONE); setVisibility(GONE); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }); }
3.3 計(jì)算圖片縮放的比例
private float getImageViewScale(GalleryPhotoParameterModel photoData) { float scale; float scaleX = photoData.imageWidth / mScreenRect.width(); float scaleY = photoData.photoHeight * 1.0f / mScaleImageView.getHeight(); // 橫向圖片 if (photoData.photoWidth > photoData.photoHeight) { // 圖片的寬高比 float photoScale = photoData.photoWidth * 1.0f / photoData.photoHeight; // 執(zhí)行動畫的ImageView寬高比 float animationImageScale = mScaleImageView.getWidth() * 1.0f / mScaleImageView.getHeight(); if (animationImageScale > photoScale) { // 動畫ImageView寬高比大于圖片寬高比的時(shí)候,需要用圖片的高度除以動畫ImageView高度的比例尺 scale = scaleY; } else { scale = scaleX; } } // 正方形圖片 else if (photoData.photoWidth == photoData.photoHeight) { if (mScaleImageView.getWidth() > mScaleImageView.getHeight()) { scale = scaleY; } else { scale = scaleX; } } // 縱向圖片 else { scale = scaleY; } return scale; }
3.4 執(zhí)行動畫的縮放
/** * 執(zhí)行縮放動畫 * @param scaleImageView * @param translationX * @param translationY * @param scale * @param isEnlarge */ private void executeZoom(final ImageView scaleImageView, int translationX, int translationY, float scale, boolean isEnlarge, Animator.AnimatorListener listener) { float startTranslationX, startTranslationY, endTranslationX, endTranslationY; float startScale, endScale, startAlpha, endAlpha; // 放大 if (isEnlarge) { startTranslationX = translationX; endTranslationX = 0; startTranslationY = translationY; endTranslationY = 0; startScale = scale; endScale = 1; startAlpha = 0f; endAlpha = 0.75f; } // 縮小 else { startTranslationX = 0; endTranslationX = translationX; startTranslationY = 0; endTranslationY = translationY; startScale = 1; endScale = scale; startAlpha = 0.75f; endAlpha = 0f; } //-------縮小動畫-------- AnimatorSet set = new AnimatorSet(); set.play( ObjectAnimator.ofFloat(scaleImageView, "translationX", startTranslationX, endTranslationX)) .with(ObjectAnimator.ofFloat(scaleImageView, "translationY", startTranslationY, endTranslationY)) .with(ObjectAnimator.ofFloat(scaleImageView, "scaleX", startScale, endScale)) .with(ObjectAnimator.ofFloat(scaleImageView, "scaleY", startScale, endScale)) // ---Alpha動畫--- // mMaskView伴隨著一個(gè)Alpha減小動畫 .with(ObjectAnimator.ofFloat(maskView, "alpha", startAlpha, endAlpha)); set.setDuration(animDuration); if (listener != null) { set.addListener(listener); } set.setInterpolator(new DecelerateInterpolator()); set.start(); }
改View的主要實(shí)現(xiàn)如上,在圖片進(jìn)行縮放的時(shí)候,要考慮的情況:短邊適配、圖片原尺寸的寬高、展示圖片的ImageView的寬高比、橫豎屏?xí)r屏幕的尺寸。在此非常感謝震哥的幫助、抱拳了!老鐵。如有更多想法的小伙伴。
請移步我的github GalleryView地址
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
- android 大圖片拖拽并縮放實(shí)現(xiàn)原理
- Android 實(shí)現(xiàn)WebView點(diǎn)擊圖片查看大圖列表及圖片保存功能
- Android中超大圖片無法顯示的問題解決
- Android高效加載大圖、多圖解決方案 有效避免程序OOM
- Android 實(shí)現(xiàn)加載大圖片的方法
- Android編程實(shí)現(xiàn)大圖滾動顯示的方法
- Android 加載大圖、多圖和LruCache緩存詳細(xì)介紹
- Android實(shí)現(xiàn)網(wǎng)絡(luò)加載圖片點(diǎn)擊大圖后瀏覽可縮放
- Android實(shí)現(xiàn)大圖滾動顯示效果
相關(guān)文章
Kotlin標(biāo)準(zhǔn)庫函數(shù)使用分析及介紹
Kotlin提供了一個(gè)系統(tǒng)庫,是Java庫的增強(qiáng)。其中有很多函數(shù)在適配了Java的類型和方法同時(shí)使用Kotlin的語法。其中一些底層的函數(shù) 是使用比較廣泛的2022-09-09android 調(diào)用JNI SO動態(tài)庫的方法
android 調(diào)用JNI 分為靜態(tài)調(diào)用與動態(tài)調(diào)用,接下來通過本文給大家介紹android 調(diào)用JNI SO動態(tài)庫的方法,感興趣的朋友一起看看吧2021-11-11Android 圖像處理(類型轉(zhuǎn)換,比例縮放,倒影,圓角)的小例子
Android 圖像處理(類型轉(zhuǎn)換,比例縮放,倒影,圓角)的小例子,需要的朋友可以參考一下2013-05-05Android 使用PopupWindow實(shí)現(xiàn)彈出更多的菜單實(shí)例詳解
最近想要做一個(gè)彈出更多的菜單,而原生的彈出菜單卻不是我們想要的效果,所以必然要自定義菜單。接下來通過本文給大家介紹android 使用popupwindow實(shí)現(xiàn)彈出更多的菜單實(shí)例詳解,需要的朋友可以參考下2017-04-04Android簡單實(shí)現(xiàn)屏幕下方Tab菜單的方法
這篇文章主要介紹了Android簡單實(shí)現(xiàn)屏幕下方Tab菜單的方法,簡單分析了Android實(shí)現(xiàn)tab菜單所涉及的界面布局及功能相關(guān)操作技巧,需要的朋友可以參考下2016-08-08Android編程之手機(jī)壁紙WallPaper設(shè)置方法示例
這篇文章主要介紹了Android編程之手機(jī)壁紙WallPaper設(shè)置方法,結(jié)合實(shí)例形式分析了Android手機(jī)壁紙WallPaper的相關(guān)設(shè)置與使用技巧,需要的朋友可以參考下2017-08-08android基礎(chǔ)總結(jié)篇之一:Activity生命周期
本篇文章主要介紹了android基礎(chǔ)總結(jié)篇之一:Activity生命周期,想要學(xué)習(xí)的可以了解一下。2016-11-11Android簡單實(shí)現(xiàn)菜單拖拽排序的功能
這篇文章主要介紹了Android簡單實(shí)現(xiàn)菜單拖拽排序的功能,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)價(jià)值,需要的朋友可以參考一下2022-07-07Android使用surfaceView自定義抽獎大轉(zhuǎn)盤
這篇文章主要為大家詳細(xì)介紹了Android使用surfaceView自定義抽獎大轉(zhuǎn)盤,熟練掌握SurfaceVie實(shí)現(xiàn)抽獎大轉(zhuǎn)盤,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android編程實(shí)現(xiàn)圖片平鋪的方法分析
這篇文章主要介紹了Android編程實(shí)現(xiàn)圖片平鋪的方法,結(jié)合具體實(shí)例形式總結(jié)分析了Android實(shí)現(xiàn)圖片平鋪效果的三種常用操作技巧,需要的朋友可以參考下2017-06-06