Android仿頭條、微信大圖預(yù)覽視圖的方法詳解
圖片大圖預(yù)覽
在我現(xiàn)在的項目當(dāng)中,也存在大圖預(yù)覽的功能,但其實現(xiàn)過于繁重,采用一個Activity實現(xiàn),并且在圖片展示的過程中會產(chǎn)生卡頓感,整體感覺很是不好,正巧項目也在重構(gòu)過程中,所以決定將這一功能寫成一個成型的控件。
話不多說,先上圖看下效果。

整體實現(xiàn)思路
圖片展示:PhotoView(大圖支持雙擊放大)
圖片加載:Glide(加載網(wǎng)絡(luò)圖片、本地圖片、資源文件)
小圖變大圖時的實現(xiàn):動畫
圖片的下載:插入系統(tǒng)相冊
該控件采用自定義View的方式,通過一些基本的控件的組合,來形成一個具有大圖預(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é)束了,就是這么簡單!
具體實現(xiàn)
(1)先從showPhotoGallery(index, List, ImageView)這個方法講起
int index:我們想要展示的一個圖片列表中的第幾個
List list: 我們要傳入的要展示的圖片類型list(支持網(wǎng)絡(luò)圖片、資源圖片、本地圖片(本地圖片與網(wǎng)絡(luò)圖片其實都是一個字符串地址))
public class GalleryPhotoModel {
public Object photoSource;
public GalleryPhotoModel(@DrawableRes int drawableRes) {
this.photoSource = drawableRes;
}
public GalleryPhotoModel(String path) {
this.photoSource = path;
}
}
ImageView:即你點擊想要展示的那個圖片
(2)對傳入GalleryView的數(shù)據(jù)進(jìn)行處理
/**
* @param index 想要展示的圖片的索引值
* @param photoList 圖片集合(URL、Drawable、Bitmap)
* @param clickImageView 點擊的第一個圖片
*/
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();
//將第一個點擊的圖片參數(shù)連同整個圖片列表傳入
this.setVisibility(View.VISIBLE);
post(new Runnable() {
@Override
public void run() {
requestFocus();
}
});
setGalleryPhotoList(photoList, photoParameter);
}
通過傳遞進(jìn)來的ImageView,獲取被點擊View參數(shù),并拼裝成參數(shù)model,再進(jìn)行數(shù)據(jù)的相關(guān)處理。
(3)GalleryView的實現(xiàn)機(jī)制
該View的實現(xiàn)思路主要是:最外層是一個RelativeLayout,內(nèi)部有一個充滿父布局的ImageView和ViewPager。ImageView用來進(jìn)行圖片的動畫縮放,ViewPager用來進(jìn)行最后的圖片的展示。其實該View最主要的地方就是通過點擊ImageView到最后ViewPager的展示的動畫。接下來主要是講解一下這個地方。先看一下被點擊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());
//將被縮放的圖片放在一個單獨(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);
}
});
}
/**
* 計算放大比例,開啟放大動畫
*
* @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 圖片縮小操作
/**
* 計算縮小比例,開啟縮小動畫
*/
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 計算圖片縮放的比例
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寬高比大于圖片寬高比的時候,需要用圖片的高度除以動畫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伴隨著一個Alpha減小動畫
.with(ObjectAnimator.ofFloat(maskView, "alpha", startAlpha, endAlpha));
set.setDuration(animDuration);
if (listener != null) {
set.addListener(listener);
}
set.setInterpolator(new DecelerateInterpolator());
set.start();
}
改View的主要實現(xiàn)如上,在圖片進(jìn)行縮放的時候,要考慮的情況:短邊適配、圖片原尺寸的寬高、展示圖片的ImageView的寬高比、橫豎屏?xí)r屏幕的尺寸。在此非常感謝震哥的幫助、抱拳了!老鐵。如有更多想法的小伙伴。
請移步我的github GalleryView地址
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Android NDK開發(fā)(C語言基本數(shù)據(jù)類型)
這篇文章主要介紹了Android NDK開發(fā)中,C語言基本數(shù)據(jù)類型,主要以C語言包含的數(shù)據(jù)類型及基本類型展開相關(guān)資料,需要的朋友可以參考一下2021-12-12
Android編程之微信SDK分享功能過程步驟詳細(xì)分析
這篇文章主要介紹了Android編程之微信SDK分享功能過程步驟詳細(xì)分析,較為詳細(xì)的分析了Android微信SDK分享功能的原理、步驟與相關(guān)注意事項,需要的朋友可以參考下2015-10-10
完全解析Android多線程中線程池ThreadPool的原理和使用
本篇文章給大家通過原理和實例詳細(xì)講述了Android多線程中線程池ThreadPool的原理和使用,對此有興趣的朋友可以跟著參考學(xué)習(xí)下。2018-04-04
Android RefreshLayout實現(xiàn)下拉刷新布局
這篇文章主要為大家詳細(xì)介紹了Android RefreshLayout實現(xiàn)下拉刷新布局,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10
關(guān)于androidstuio導(dǎo)入系統(tǒng)源碼的問題
小編最近在做系統(tǒng)源碼導(dǎo)出來的小項目,在導(dǎo)入androidstudio過程中遇到過一些問題,本文以Schedule power on off為例給大家詳細(xì)介紹,需要的朋友參考下吧2021-06-06
項目發(fā)布Debug和Release版的區(qū)別詳解
這篇文章主要為大家詳細(xì)介紹了項目發(fā)布Debug和Release版的區(qū)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-10-10
android實現(xiàn)雙日期選擇控件(可隱藏日,只顯示年月)
本篇文章主要介紹了android實現(xiàn)雙日期選擇控件(可隱藏日,只顯示年月) ,非常具有實用價值,需要的朋友可以參考下。2017-01-01

