溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android仿頭條、微信大圖預(yù)覽視圖的方法詳解

發(fā)布時間:2020-10-03 19:22:04 來源:腳本之家 閱讀:170 作者:cedear 欄目:移動開發(fā)

圖片大圖預(yù)覽

在我現(xiàn)在的項目當(dāng)中,也存在大圖預(yù)覽的功能,但其實現(xiàn)過于繁重,采用一個Activity實現(xiàn),并且在圖片展示的過程中會產(chǎn)生卡頓感,整體感覺很是不好,正巧項目也在重構(gòu)過程中,所以決定將這一功能寫成一個成型的控件。

話不多說,先上圖看下效果。

Android仿頭條、微信大圖預(yù)覽視圖的方法詳解

整體實現(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());
  //將被縮放的圖片放在一個單獨的ImageView上進(jìn)行單獨的動畫處理。
  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í)價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI