目标需求
实现一张小图片,被点击后变成一个在整个屏幕上显示的大图片.类似于微信朋友圈的图片.
实现流程
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 当然, 也可以直接访问到指定目录看到ZoomableDraweeView: https://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(); } }