Android 开发 Fresco框架点击小图显示全屏大图实现 ZoomableDraweeView

目标需求

  实现一张小图片,被点击后变成一个在整个屏幕上显示的大图片.类似于微信朋友圈的图片.

实现流程

  1.Fresco基本初始化

  2.下载并且导入ZoomableDraweeView 它是实现大图的关键view

  3.创建activity,在布局文件里加入ZoomableDraweeView设置为全屏幕占满.(如果你需要多图翻页浏览功能,请另外添加ViewPager在放入ZoomableDraweeView)

  4.在activity配置需要加载的图片

需要访问的网址:

  Fresco的github: https://github.com/facebook/fresco

  Zoomable包路径: https://github.com/facebook/fresco/tree/master/samples/zoomable

Fresco基本初始化

  这个流程我就不废话了,如果这个还没了解过它,请移步 https://www.cnblogs.com/guanxinjing/p/10364380.html

  

下载并且导入ZoomableDraweeView

  接下来是关键点Zoomable是Fresco Demo工程里自带的图片缩放库,但是它并不在依赖包里.所以你依赖了Fresco也无法找到这个View.所以我们需要手动找到这个包并且导入它.

  1.第一步找到它:

  在官方文档里只有一个小地方标注了它的存在,在这个官方文档网址:https://www.fresco-cn.org/docs/sample-code.html      当然, 也可以直接访问到指定目录看到ZoomableDraweeViewhttps://github.com/facebook/fresco/tree/master/samples/zoomable

  

  就是这个缩放库,没有任何详细解释

  2.第二步 下载整个Fresco demo工程

  在github里将这个工程下载下来,这个不需要多说啥了

  3.第三步 导入gestures包

  ZoomableDraweeView的缩放实现还需要这个demo工程里的一个手势处理包就是这个gestures包,它的路径如下图所示:

  

  找到它,将这个java文件放到你的工程里

  4.第四步 导入Zoomable

  路径如下:

  

  将这个包里的所有文件放到你的工程里,并且将一些报错依赖路径重新指向正确依赖路径

创建activity,在布局文件里加入ZoomableDraweeView设置为全屏幕占满

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".work.share.ZoomableActivity">
    
    <com.yt.kangaroo.widgets.zoomable.ZoomableDraweeView
        android:id="@+id/zoomable"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

在activity配置需要加载的图片

 @Override
    public void initView() {
        mZoomableDraweeView = (ZoomableDraweeView)findViewById(R.id.zoomable);
        mZoomableDraweeView.setIsLongpressEnabled(false);//长按
//        mZoomableDraweeView.setAllowTouchInterceptionWhileZoomed(true);//是否允许缩放时左右切换(如果设置为true,则父视图可以在视图缩放时截获触摸事件)
        mZoomableDraweeView.setTapListener(new DoubleTapGestureListener(mZoomableDraweeView));//双击击放大或缩小
        DraweeController controller = Fresco.newDraweeControllerBuilder()//创建Fresco的图片下载配置
                .setUri("https://s1.52poke.wiki/wiki/thumb/7/73/002Ivysaur.png/300px-002Ivysaur.png")
                .build();
        mZoomableDraweeView.setController(controller);//将下载配置导入

    }

 其他api


mZoomableDraweeView.setZoomingEnabled(false);//设置是否缩放

 如果你需要实现单击功能

  这个 ZoomableDraweeView已经将触摸交给双击监听处理类DoubleTapGestureListener了,所以你无法使用setOnClickListener();方法实现ZoomableDraweeView的单击监听

mZoomableDraweeView.setTapListener(new DoubleTapGestureListener(mZoomableDraweeView));//双击击放大或缩小

  这行代码就已经表示了,作者自己实现了一个DoubleTapGestureListener(双击监听类,作者主要用来双击放大缩小图片),然后设置给TapListener,所以这个时候单击已经被拦截了.所以目前只有2个方法

  1.在ZoomableDraweeView里实现onTouchEvent判断单击事件,这样处理太费时费力,且一不小心就会破坏原有的其他触摸手势功能

  2.我们也重写DoubleTapGestureListener这个类,在作者原有的DoubleTapGestureListener里面添加我们的单击事件然后接口出去.

所以我们使用第二种方法来实现单击事件,做法如下:

  在DoubleTapGestureListener里重写一下,增加重写onSingleTapConfirmed方法(要注意只有onSingleTapConfirmed方法是单击确定后的回调):

/**
 * Tap gesture listener for double tap to zoom / unzoom and double-tap-and-drag to zoom.
 *
 * @see ZoomableDraweeView#setTapListener(GestureDetector.SimpleOnGestureListener)
 */
public class DoubleTapGestureListener extends GestureDetector.SimpleOnGestureListener {
  private static final int DURATION_MS = 300;
  private static final int DOUBLE_TAP_SCROLL_THRESHOLD = 20;

  private final ZoomableDraweeView mDraweeView;
  private final PointF mDoubleTapViewPoint = new PointF();
  private final PointF mDoubleTapImagePoint = new PointF();
  private float mDoubleTapScale = 1;
  private boolean mDoubleTapScroll = false;
  private OnSingleClickListener mListener;

  public DoubleTapGestureListener(ZoomableDraweeView zoomableDraweeView) {
    mDraweeView = zoomableDraweeView;
  }

  /**
   * 重写这个方法实现单击监听
   * @param e
   * @return
   */
  @Override
  public boolean onSingleTapConfirmed(MotionEvent e) {
    if (mListener != null){
      mListener.onSingleClick();
    }
    return true;
  }


  @Override
  public boolean onDoubleTapEvent(MotionEvent e) {
    AbstractAnimatedZoomableController zc =
        (AbstractAnimatedZoomableController) mDraweeView.getZoomableController();
    PointF vp = new PointF(e.getX(), e.getY());
    PointF ip = zc.mapViewToImage(vp);
    switch (e.getActionMasked()) {
      case MotionEvent.ACTION_DOWN:
        mDoubleTapViewPoint.set(vp);
        mDoubleTapImagePoint.set(ip);
        mDoubleTapScale = zc.getScaleFactor();
        break;
      case MotionEvent.ACTION_MOVE:
        mDoubleTapScroll = mDoubleTapScroll || shouldStartDoubleTapScroll(vp);
        if (mDoubleTapScroll) {
          float scale = calcScale(vp);
          zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);
        }
        break;
      case MotionEvent.ACTION_UP:
        if (mDoubleTapScroll) {
          float scale = calcScale(vp);
          zc.zoomToPoint(scale, mDoubleTapImagePoint, mDoubleTapViewPoint);
        } else {
          final float maxScale = zc.getMaxScaleFactor();
          final float minScale = zc.getMinScaleFactor();
          if (zc.getScaleFactor() < (maxScale + minScale) / 2) {
            zc.zoomToPoint(
                maxScale,
                ip,
                vp,
                DefaultZoomableController.LIMIT_ALL,
                DURATION_MS,
                null);
          } else {
            zc.zoomToPoint(
                minScale,
                ip,
                vp,
                DefaultZoomableController.LIMIT_ALL,
                DURATION_MS,
                null);
          }
        }
        mDoubleTapScroll = false;
        break;
    }
    return true;
  }

  private boolean shouldStartDoubleTapScroll(PointF viewPoint) {
    double dist = Math.hypot(
        viewPoint.x - mDoubleTapViewPoint.x,
        viewPoint.y - mDoubleTapViewPoint.y);
    return dist > DOUBLE_TAP_SCROLL_THRESHOLD;
  }

  private float calcScale(PointF currentViewPoint) {
    float dy = (currentViewPoint.y - mDoubleTapViewPoint.y);
    float t = 1 + Math.abs(dy) * 0.001f;
    return (dy < 0) ? mDoubleTapScale / t : mDoubleTapScale * t;
  }


  public void setOnSingleClick(OnSingleClickListener listener){
    this.mListener = listener;
  }

  /**
   * 实现单击接口
   */
  public interface OnSingleClickListener{
    void onSingleClick();

  }

}

activity里实现单击回调:

DoubleTapGestureListener doubleTapGestureListener = new DoubleTapGestureListener(mZoomableDraweeView);
        doubleTapGestureListener.setOnSingleClick(new DoubleTapGestureListener.OnSingleClickListener() { //实现双击监听里的单击功能监听(双击作者这个View已经实现处理了)
            @Override
            public void onSingleClick() {
                finish();
            }
        });

 嵌套ViewPager

下面的demo不是完整的,图片计数没做,但是功能已经实现了

activity_zoomable_pager.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".work.share.ZoomableActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </androidx.viewpager.widget.ViewPager>

    <TextView
        android:id="@+id/image_count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="数量"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>


</androidx.constraintlayout.widget.ConstraintLayout>
package com.yt.kangaroo.work.share.zoomable_pager;

import android.content.Intent;
import android.graphics.drawable.Animatable;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;

import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.ControllerListener;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.request.ImageRequest;
import com.yt.kangaroo.R;
import com.yt.kangaroo.app.AppEventBusMsg;
import com.yt.kangaroo.app.BaseActivity;
import com.yt.kangaroo.net.teacherNet.TClircleListBase;
import com.yt.kangaroo.utils.L;
import com.yt.kangaroo.utils.ThumbnailUtil;
import com.yt.kangaroo.widgets.zoomable.DoubleTapGestureListener;
import com.yt.kangaroo.widgets.zoomable.ZoomableDraweeView;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.ArrayList;
import java.util.List;

/**
 *@content:左右滑页大图片显示Activity
 *@time:2019-6-21
 *@build:
 */

public class ZoomablePagerActivity extends BaseActivity {
    public static final String T_CLIRCLE_IMAGE_KEY = "TClircleImage";
    private List<TClircleListBase.FriendsPublish.Pic> mTPicList;
    private ViewPager mViewPager;
    private TextView mImageCount;
    private ZoomablePagerAdapter mAdapter;
    private long mBackDelay = 0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initListener();
        EventBus.getDefault().register(this);
        mBackDelay = System.currentTimeMillis();
    }

    @Override
    public int getLayout() {
        return R.layout.activity_zoomable_pager;
    }

    @Override
    public void initView() {
        mViewPager = findViewById(R.id.view_pager);
        mImageCount = findViewById(R.id.image_count);
        mAdapter = new ZoomablePagerAdapter();
        mViewPager.setAdapter(mAdapter);

    }

    private void initListener(){
        mAdapter.setOnSingleClick(new ZoomablePagerAdapter.OnSingleClickListener() {
            @Override
            public void onSingleClick() {
                if (System.currentTimeMillis() - mBackDelay > 1*1000){ //退出延迟,太快的返回进出Activity会导致EventBus发送数据出现问题
                    EventBus.getDefault().unregister(this);
                    finish();
                }
            }
        });
    }

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onUiThread(AppEventBusMsg msg){
        L.e("接到数据onUiThread");
        if (msg.getKey().equals(T_CLIRCLE_IMAGE_KEY)){
            L.e("接到数据T_CLIRCLE_IMAGE_KEY");
            mTPicList = msg.getObjectList();
            List<String> urlList = new ArrayList<>();
            for (TClircleListBase.FriendsPublish.Pic pic : mTPicList){
                urlList.add(pic.getPicUrl());

            }
            L.e("urlList 长度="+urlList.size());
            mAdapter.refreshData(urlList);

        }
        EventBus.getDefault().removeStickyEvent(msg);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}
zoomable_pager_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <com.yt.kangaroo.widgets.zoomable.ZoomableDraweeView
        android:id="@+id/zoomable"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@color/colorBlack1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:visibility="visible"
        android:indeterminateDrawable="@anim/ic_wait"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
package com.yt.kangaroo.work.share.zoomable_pager;

import android.graphics.drawable.Animatable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;

import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.ControllerListener;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.request.ImageRequest;
import com.yt.kangaroo.R;

import com.yt.kangaroo.utils.L;
import com.yt.kangaroo.utils.ThumbnailUtil;
import com.yt.kangaroo.widgets.zoomable.DoubleTapGestureListener;
import com.yt.kangaroo.widgets.zoomable.ZoomableDraweeView;

import java.util.ArrayList;
import java.util.List;
/**
 *@content:左右滑页大图片显示适配器
 *@time:2019-6-21
 *@build:zhouqiang
 */
public class ZoomablePagerAdapter extends PagerAdapter {
    private List<String> mImageUrlList = new ArrayList<>();
    private OnSingleClickListener mOnSingleClickListener;

    public void refreshData(List<String> imageUrl){
        L.e("触发refresh");
        mImageUrlList.clear();
        mImageUrlList.addAll(imageUrl);
        notifyDataSetChanged();

    }

    @Override
    public int getCount() {
        L.e("触发getCount="+mImageUrlList.size());
        return mImageUrlList.size();
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull final ViewGroup container, int position) {
        L.e("触发instantiateItem");
        View view = LayoutInflater.from(container.getContext()).inflate(R.layout.zoomable_pager_item, container, false);
        ZoomableDraweeView zoomableDraweeView = view.findViewById(R.id.zoomable);
        final ProgressBar progressBar = view.findViewById(R.id.progress);
        zoomableDraweeView.setIsLongpressEnabled(false);//长按
        DoubleTapGestureListener doubleTapGestureListener = new DoubleTapGestureListener(zoomableDraweeView);
        doubleTapGestureListener.setOnSingleClick(new DoubleTapGestureListener.OnSingleClickListener() { //实现双击监听里的单击功能监听(双击作者这个View已经实现处理了)
            @Override
            public void onSingleClick() {
                if (mOnSingleClickListener != null){
                    mOnSingleClickListener.onSingleClick();
                }

            }
        });
        zoomableDraweeView.setTapListener(doubleTapGestureListener);//双击击放大或缩小
        ControllerListener controllerListener = new ControllerListener() {
            @Override
            public void onSubmit(String id, Object callerContext) {
                //开始提交
                progressBar.setVisibility(View.VISIBLE);

            }

            @Override
            public void onFinalImageSet(String id, @Nullable Object imageInfo, @Nullable Animatable animatable) {
                //完成
                progressBar.setVisibility(View.GONE);

            }

            @Override
            public void onIntermediateImageSet(String id, @Nullable Object imageInfo) {
                //中间图像集

            }

            @Override
            public void onIntermediateImageFailed(String id, Throwable throwable) {
                //中间图像上失败

            }

            @Override
            public void onFailure(String id, Throwable throwable) {
                //失败
                Toast.makeText(container.getContext(),"网络异常,图片加载失败",Toast.LENGTH_LONG).show();

            }

            @Override
            public void onRelease(String id) {

                //释放

            }
        };
        DraweeController controller = Fresco.newDraweeControllerBuilder()//创建Fresco的图片下载配置
                .setControllerListener(controllerListener)
                .setLowResImageRequest(ImageRequest.fromUri(ThumbnailUtil.handlerImageUrl(mImageUrlList.get(position))))
                .setImageRequest(ImageRequest.fromUri(mImageUrlList.get(position)))
                .setOldController(zoomableDraweeView.getController())
                .build();
        zoomableDraweeView.setController(controller);//将下载配置导入
        container.addView(view);//注意别忘记添加
        return view;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView((View)object);
    }

    public void setOnSingleClick(OnSingleClickListener listener){
        mOnSingleClickListener = listener;

    }

    public interface OnSingleClickListener{
        void onSingleClick();
    }
}

  



原文地址:https://www.cnblogs.com/guanxinjing/p/10481917.html