ImageView and FrameLayout with gestures control and position animation

Overview

GestureViews

Maven Build Size

ImageView and FrameLayout with gestures control and position animation.

Main goal of this library is to make images viewing process as smooth as possible and to make it easier for developers to integrate it into their apps.

Features

  • Gestures support: pan, zoom, quick scale, fling, double tap, rotation.
  • Seamless integration with ViewPager (panning smoothly turns into ViewPager flipping and vise versa).
  • View position animation ("opening" animation). Useful to animate into full image view mode.
  • Advanced animation from RecyclerView (or ListView) into ViewPager.
  • Exit full image mode by scroll and scale gestures.
  • Rounded images with animations support.
  • Image cropping (supports rotation).
  • Lots of settings.
  • Gestures listener: down (touch), up (touch), single tap, double tap, long press.
  • Custom state animation (animating position, zoom, rotation).
  • Supports both ImageView and FrameLayout out of the box, also supports custom views.

Sample app

Get it on Google Play

Demo video

YouTube

Demo video

Usage

Add dependency to your build.gradle file:

implementation 'com.alexvasilkov:gesture-views:2.7.1'

Usage wiki

Javadoc

Sample app sources

License

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Memory leak in ViewPositionHolder

    Memory leak in ViewPositionHolder

    It seem there is a memory leak when using GestureViews.

    ViewPositionAnimatorcontains a fromPosHolder and a toPosHolder. But toPosHolder.clear() is never called. As such, view.getViewTreeObserver().removeOnPreDrawListener is never called, and the OnPreDrawListener keep a reference to the GestureView.

    bug 
    opened by mr-thierry 13
  • Use requestDisallowInterceptTouchEvent() to properly coordinate touch events

    Use requestDisallowInterceptTouchEvent() to properly coordinate touch events

    Hey alex, thanks for this fastastic library. I noticed that neither GestureImageView/FrameView call requestDisallowInterceptTouchEvent() when they start consuming events. This is bad because they currently don't work well with nested Views that support gestures.

    Since I'm using this library in one of my projects, I'd be happy to send a PR if you can guide me :)

    enhancement 
    opened by saket 13
  •   surfaceView can't  implement gestureView

    surfaceView can't implement gestureView

    how can i make surfaceView have the same function?

         I have write a MapView with surfaceView ,then i wish it can be operated as well as GestureImageView,But it can't work. I hope to get your suggestion ,the following is my code:

    package com.jms.cleanse.widget;

    import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.RectF; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView;

    import com.alexvasilkov.gestures.GestureController; import com.alexvasilkov.gestures.State; import com.alexvasilkov.gestures.views.interfaces.GestureView; import com.jms.cleanse.widget.mapview.POIConfig; import com.jms.cleanse.widget.mapview.ScaleUtils; import com.jms.cleanse.widget.mapview.TestPOI;

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

    /**

    • Created by zhoukan on 2018/3/28.
    • @desc: 绘制POI / 绘制路径 / */

    public class JMMapView extends SurfaceView implements SurfaceHolder.Callback, Runnable,GestureView {

    private static final String TAG = "JMMapView";
    private final GestureController controller;
    
    private Paint mPaint;
    private Canvas canvas;
    
    private SurfaceHolder holder;
    private Thread t;
    private boolean isDrawing = false;
    private List<TestPOI> testPOIS;
    private LinkedList<Path> paths;
    private LinkedList<RectF> points;
    
    /************ 地图坐标系相关的配置 *************/
    private int coodinateX;
    private int coodinateY;
    private Bitmap cleanseRes;
    private Bitmap unCleanseRes;
    private Bitmap pathStartRes;
    private Bitmap pathEndRes;
    private int colorCleanse;
    private int colorUnCleanse;
    // 地图
    private Bitmap map;
    
    private Resources resources;
    
    /* 事件 */
    private OnClickListener onClickListener;
    
    @Override
    public GestureController getController() {
        return controller;
    }
    
    public interface OnClickListener {
        void onPointClick(TestPOI poi, int pos);
    
        void onPathClick(int pos);
    }
    
    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
    
    public JMMapView(@NonNull Context context) {
        this(context, null);
    }
    
    public JMMapView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    @SuppressLint("ResourceType")
    public JMMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    
        this.setFocusable(true);
        this.setZOrderOnTop(true);
    
        holder = getHolder();
        holder.addCallback(this);
        holder.setFormat(PixelFormat.TRANSPARENT);
    

    // this.setSurfaceTextureListener(this);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(5);
        mPaint.setStyle(Paint.Style.STROKE);
    
        testPOIS = new ArrayList<>();
        paths = new LinkedList<>();
        points = new LinkedList<>();
    
        resources = getResources();
        cleanseRes = BitmapFactory.decodeStream(resources.openRawResource(POIConfig.cleanseRes));
        unCleanseRes = BitmapFactory.decodeStream(resources.openRawResource(POIConfig.unCleanseRes));
        pathStartRes = BitmapFactory.decodeStream(resources.openRawResource(POIConfig.pathStartRes));
        pathEndRes = BitmapFactory.decodeStream(resources.openRawResource(POIConfig.pathEndRes));
    
        colorCleanse = ContextCompat.getColor(this.getContext(), POIConfig.pathColorCleanse);
        colorUnCleanse = ContextCompat.getColor(this.getContext(), POIConfig.pathColorUnCleanse);
    
        /* make jmmapview support gesture */
        controller = new GestureController(this);
        controller.getSettings().setOverzoomFactor(1f).setPanEnabled(false);
        controller.getSettings().initFromAttributes(context, attrs);
        controller.getSettings().setZoomEnabled(true);
        controller.getSettings().setRotationEnabled(true);
        controller.addOnStateChangeListener(new GestureController.OnStateChangeListener() {
            @Override
            public void onStateChanged(State state) {
                applyState(state);
            }
    
            @Override
            public void onStateReset(State oldState, State newState) {
                applyState(newState);
            }
        });
    
    }
    
    /**
     *
     * @param state
     */
    private void applyState(State state) {
    
    }
    
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        isDrawing = true;
        t = new Thread(this);
        t.start();
    }
    
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }
    
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isDrawing = false;
    }
    
    @Override
    public void run() {
        if (isDrawing) {
            drawMap();
        }
    }
    
    /**
     * 绘制地图
     */
    private void drawMap() {
    
        try {
            if (null != holder) {
                canvas = holder.lockCanvas();
                canvas.save();
                if (!canvasChange()) {
                    return;
                }
                canvas.drawColor(Color.WHITE);
    
                drawBitMap();
                drawPath();
                drawPoi();
                canvas.restore();
            }
        } catch (Exception e) {
            e.getStackTrace();
        } finally {
    
            if (null != canvas) {
                holder.unlockCanvasAndPost(canvas);
            }
        }
    }
    
    /**
     * @aim: 进行画布操作,将绘制起点设置为bitmap的左上角,坐标系转换到bitmap的像素坐标系上,方便作图
     */
    private boolean canvasChange() {
        if (map != null) {
            // 缩放倍数
            float ratio = ScaleUtils.getScaleRatio(ScaleUtils.ScaleType.CENTER_INSIDE, map, getWidth(), getHeight());
            // 宽的差值
            float dw = getWidth() - map.getWidth() * ratio;
            // 高的差值
            float dh = getHeight() - map.getHeight() * ratio;
            canvas.scale(ratio, ratio);
            // 横屏
            if (getWidth() > getHeight()) {
                canvas.translate(dw / (2 * ratio), 0);
                // 竖屏
            } else {
                canvas.translate(0, dh / (2 * ratio));
            }
            // 计算坐标系的最大值
            coodinateX = map.getWidth();
            coodinateY = map.getHeight();
            Log.d(TAG, "canvasChange: " + coodinateX + ",y" + coodinateY);
        } else {
            return false;
        }
        return true;
    }
    
    
    /**
     * 绘制地图,在绘制之前需要确认bitmap缩放的方式并返回得到的bitmap.
     */
    private void drawBitMap() {
        if (map != null) {
            canvas.drawBitmap(map, 0, 0, mPaint);
        }
    }
    
    /**
     * 绘制POI点 测试点数据 (-2.5,4.4)
     */
    private void drawPoi() {
        TestPOI lastPoi = null;
        for (int i = 0; i < testPOIS.size(); i++) {
            TestPOI poiPoint = testPOIS.get(i);
            if (poiPoint.isCleanse()) {
                canvas.drawBitmap(cleanseRes, null, points.get(i), mPaint);
            } else if (lastPoi != null && lastPoi.isCleanse()) {
                canvas.drawBitmap(cleanseRes, null, points.get(i), mPaint);
            } else {
                canvas.drawBitmap(unCleanseRes, null, points.get(i), mPaint);
            }
            lastPoi = poiPoint;
    
            /* 绘制起点和终点 */
            if (i == 0) {
    

    // canvas.drawBitmap(pathStartRes,null,getBitmapRectF(poiPoint.getAx(),poiPoint.getAy(),pathStartRes),mPaint); canvas.drawBitmap(pathStartRes, (float) (poiPoint.getAx() - pathStartRes.getWidth() / 2), (float) (poiPoint.getAy() - pathStartRes.getHeight()), mPaint); // canvas.drawRect(getBitmapRectF(poiPoint.getAx(),poiPoint.getAy(),pathEndRes),mPaint); } else if (i > 0 && i == testPOIS.size() - 1) { // canvas.drawBitmap(pathStartRes,null,getBitmapRectF(poiPoint.getAx(),poiPoint.getAy(),pathStartRes),mPaint); // mPaint.setColor(Color.BLUE); // canvas.drawRect(getBitmapRectF(poiPoint.getAx(),poiPoint.getAy(),pathEndRes),mPaint); canvas.drawBitmap(pathEndRes, (float) (poiPoint.getAx() - pathEndRes.getWidth() / 2), (float) (poiPoint.getAy() - pathEndRes.getHeight()), mPaint); } }

    }
    
    /**
     * 获取Bitmap矩形区域
     *
     * @param ax
     * @param ay
     * @param bm
     */
    private RectF getBitmapRectF(double ax, double ay, Bitmap bm) {
    
        int w = bm.getWidth();
        int h = bm.getHeight();
    
        RectF rectF = new RectF();
        rectF.left = (float) (ax - w / 2);
        rectF.top = (float) (ay - h);
        rectF.right = (float) (ay + w / 2);
        rectF.bottom = (float) ay;
    
        return rectF;
    }
    
    @SuppressLint("ResourceType")
    private void drawPath() {
    
        getPathList(this.testPOIS);
        for (int i = 0; i < testPOIS.size(); i++) {
            TestPOI poiPoint = testPOIS.get(i);
            // 最后一个点 直接跳出循环
            if (i == testPOIS.size() - 1) {
                break;
            }
            if (poiPoint.isCleanse()) {
                mPaint.setColor(colorCleanse);
            } else {
                mPaint.setColor(colorUnCleanse);
            }
            canvas.drawPath(paths.get(i), mPaint);
        }
    
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int w = 0;
        int h = 0;
        if (null != map) {
            w = map.getWidth();
            h = map.getHeight();
        }
        setMeasuredDimension(getDefaultSize(w, widthMeasureSpec), getDefaultSize(h, heightMeasureSpec));
    }
    
    /**
     * 设置地图
     */
    public void setMap(Bitmap map) {
        this.map = map;
        invalidate();
    }
    
    public void setTestPOIS(List<TestPOI> testPOIS) {
        if (testPOIS != null && testPOIS.size() > 0) {
            this.testPOIS.clear();
            this.testPOIS.addAll(testPOIS);
            invalidate();
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    
        float x = event.getX();
        float y = event.getY();
    
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 点被点击
                Log.d(TAG, "onTouchEvent: 我被点了" + x+",y"+y);
                for (int i = 0; i <points.size() ; i++) {
                    if (onClickListener!=null){
                        if (points.get(i).contains(x,y)){
                            onClickListener.onPointClick(testPOIS.get(i),i);
                            Log.d(TAG, "onTouchEvent: " + i+"我被点了");
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            case MotionEvent.ACTION_MOVE:
                break;
        }
    
        return controller.onTouch(this,event);
    }
    
    /**
     * 根据poi点得到对应的路径组合
     *
     * @param testPOIS
     */
    private void getPathList(List<TestPOI> testPOIS) {
    
        // 记录上一个点的坐标
        double[] lastPoi = null;
        for (int i = 0; i < testPOIS.size(); i++) {
            TestPOI poiPoint = testPOIS.get(i);
            double[] currentPoi = poiPoint.getAndroidCoordinate(coodinateX, coodinateY);
            Log.d(TAG, "getPathList: x:" + currentPoi[0] + ",y:" + currentPoi[1]);
            if (lastPoi != null) {
                Path p = new Path();
                p.moveTo((float) lastPoi[0], (float) lastPoi[1]);
                p.lineTo((float) currentPoi[0], (float) currentPoi[1]);
                // 添加路径
                paths.add(p);
            }
            lastPoi = currentPoi;
            // 添加矩形区域
            points.add(poiPoint.getRect(currentPoi[0], currentPoi[1], coodinateX, coodinateY, cleanseRes.getWidth() / 2));
    
        }
    }
    

    }

    question 
    opened by ZhouKanZ 11
  • How to prevent final image size blinking briefly on enter animation

    How to prevent final image size blinking briefly on enter animation

    I'm trying to prevent the image from blinking briefly in its final size and position right before the enter a animation runs in the Image Animation (cross-activity) example.

    For this scenario I need the image to not be invisible, so it starts visible, but when I do that this happens every single time:

    I click on the image image

    It shows like this instantly for what feels like 1 animation frame image

    And quickly switches to the actual animation/transition (imagine this growing from the original image to the final size in the new activity) image

    So for a very fast 1 frame kinda moment the image is shown in its final size and position right before it starts animating from the clicked area.

    I tried setting the setState manually into the gesture view even before the image is set in it, also after. I also tried to set the enter with the initial x, y and zoom and false for animation, just to make sure the image is starting from the expected position and size. I even confirmed the states and fromPos were being stored correctly in the getPositionAnimator(), but no difference.

    Isn't there any way to ensure that the very first time the GestureView display the image is at the specified position + zoom we expect or define?

    question 
    opened by ParticleCore 10
  • Bounds not adjusted when rotating device

    Bounds not adjusted when rotating device

    Dear Alex, rotating the device will not re-adjust a photo to fit the screen. Instead you have to zoom out to make it fit.

    image.getController().getSettings().setFillViewport(true); image.getController().getSettings().setOverzoomFactor(10f); image.getController().getSettings().setDoubleTapZoom(2f); image.getController().getSettings().setMaxZoom(20f); image.getController().getSettings().setFitMethod(Fit.INSIDE);

    Manifest android:configChanges="orientation|keyboardHidden|screenSize|locale"

    question 
    opened by Farasy 10
  • Erratic behavior when animating/setting padding in GestureImageView

    Erratic behavior when animating/setting padding in GestureImageView

    I was trying to come up with a way to vertically offset the image inside a GestureImageView when I noticed an erratic pattern that is not consistent.

    Initially I tried to play with .setViewPort(), but that only controls the size of the image, not the position. Then I checked padding and it works, to a certain degree.

    If I set the padding manually in the xml layout file the image is exactly positioned as I expect it to be, but because there is UI that overlaps the view and that UI can be toggled with sliding animation I tried to animate the padding as well.

    The top and left padding animations work perfectly, but for some reason the bottom and right do not work the same way, it feels like they are being completely ignored.

    This video shows a basic example of what I am describing, please ignore the jerkiness/jumps because those are due to the fullscreen entering/exiting and are irrelevant to this example.

    gif5

    The top and left padding animation are working as expected, however the right and bottom padding values are not. Worse, they seem to be "taken into account" when the values are set back to normal.

    This feels like the viewport position is being correctly updated, but the resizing/scale of it is not.

    To replicate this problem replace the following code in this location: https://github.com/alexvasilkov/GestureViews/blob/master/sample/src/main/java/com/alexvasilkov/gestures/sample/demo/adapter/PhotoPagerAdapter.java#L66-L78

    With this

        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup container) {
            final ViewHolder holder = new ViewHolder(container);
    
            holder.image.setOnClickListener(view -> {
                onImageClick();
    
                // here we are simply animating all paddings of the view at the same
                // time to 200px or 0px depending on if there is a padding already set
                boolean show = holder.image.getPaddingTop() == 0;
                ValueAnimator.AnimatorUpdateListener updateListener2 = animation -> {
                    holder.image.setPadding(
                        (int) animation.getAnimatedValue(),
                        (int) animation.getAnimatedValue(),
                        (int) animation.getAnimatedValue(),
                        (int) animation.getAnimatedValue()
                    );
                };
                ValueAnimator valueAnimator = ObjectAnimator.ofInt(show ? 0 : 200, show ? 200 : 0);
                valueAnimator.setDuration(1000);
                valueAnimator.addUpdateListener(updateListener2);
                valueAnimator.start();
                // end of code modification, everything else is the original untouched code
    
            });
    
            settingsController.apply(holder.image);
    
            holder.image.getController().enableScrollInViewPager(viewPager);
            holder.image.getPositionAnimator().addPositionUpdateListener((position, isLeaving) ->
                    holder.progress.setVisibility(position == 1f ? View.VISIBLE : View.INVISIBLE));
            return holder;
        }
    

    Can we get this fixed somehow?


    Note: I tried to play with changing the UI heights/real positions (not translations, but actual view repositioning) with the GestureView constrained between them, but this interferes with the gesture behavior since the height adjustments are interpreted as gestures as well if the user is touching the view during the UI transitions. I also tried to just leave the view constrained with no views changing dimensions, but when the enter/leave animation is automatically clipped at the edges of the view (as if the view limits were the device screen limits).

    The only working solution that has almost worked is to play with the padding and I cannot think of anything else to try.

    question 
    opened by ParticleCore 9
  • Animations usage tutorial

    Animations usage tutorial

    is there any simple tutorial to show how to use animations? i have used GestureViews to create a comic book and want to zoom to different parts of the page. any help will be regarded.

    question 
    opened by arnoldozippiex1 9
  • Return to default rotation after rotation

    Return to default rotation after rotation

    How can i return view to default rotation state with animation, after rotating with fingers? Now when i rotate view with fingers it stays in rotated state.

    Like in the video: https://youtu.be/Jh-OJjtHJmQ

    enhancement 
    opened by alashow 9
  • Open basic animation

    Open basic animation

    how implement it? i dont get it.

    i use:

                mImageView.getPositionAnimator().enter(viewAnim, true);
    

    and i get:

    java.lang.NullPointerException: Attempt to invoke virtual method 'void com.alexvasilkov.gestures.GestureController.resetState()' on a null object reference

    bug 
    opened by fedeamura 9
  • Zoom image smaller than viewport and move it within viewport

    Zoom image smaller than viewport and move it within viewport

    the Api does the zoom function amazingly.

    Is it possible to go the opposite direction and have a function to shrink the images (make the image smaller than the image view)?

    Currently, the image snaps back to the size of the image view when trying to shrink the image.

    question 
    opened by angelkoh 8
  • Can't pan to full child view height when zoomed in

    Can't pan to full child view height when zoomed in

    I have a layout structure as such:

    <ConstraintLayout/>
        <ScrollView/>
            <GestureFrameLayout/>
                <ConstraintLayout/>   <-- has an image as background around 2500px
    

    When I try to zoom in after moving down the scrollview (let's say to the bottom of the child element of scrollview), then when I try to pan upwards to the top of the image that is the background of my ConstraintLayout (whilst still zoomed in), I am restricted to only going upwards to a fraction of the height.

    Here is an image trying to explain the issue:

    zoom

    question 
    opened by bizzyman 7
  • hello,how to keep the entered list not hidden

    hello,how to keep the entered list not hidden

    exm: GestureTransitions.from(mViewBinding.rvRelation, listTracker).into(mViewBinding.vpRelation, pagerTracker);

    Hello, may I ask how to keep the entered list rvRelation not hidden or in the state of display? Now I have a requirement that the list rvRelation remains displayed, how should I set it? thank you

    opened by i91h1r 0
  • EditText not editable in GestureFrameLayout

    EditText not editable in GestureFrameLayout

    Hello, i have complex view. its like a book with images and text. build with GestureFrameLayout and ViewPager in it. I am using GestureFrameLayout to have posibility zoom in/out to/from elements. With images it works ok. but when zooming to EditText keyboard not showing and EditText not changing its value when typing. if i remove zooming keyboard shows and EditText works like expected. Can you help me please ? Thanks!

    opened by Equin 2
  • You should call enter(...) before calling update(...) crash

    You should call enter(...) before calling update(...) crash

    java.lang.IllegalStateException: You should call enter(...) before calling update(...)
        at com.alexvasilkov.gestures.animation.ViewPositionAnimator.cleanBeforeUpdateInternal(ViewPositionAnimator.java:4)
        at com.alexvasilkov.gestures.transition.ViewsTransitionAnimator.onFromViewChanged(ViewsTransitionAnimator.java:4)
        at com.alexvasilkov.gestures.transition.ViewsCoordinator.setFromInternal(ViewsCoordinator.java:3)
        at com.alexvasilkov.gestures.transition.internal.FromRecyclerViewListener$1.onChildViewAttachedToWindow(FromRecyclerViewListener.java:9)
        at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:7)
        at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:7)
        at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:13)
        at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:2)
        at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:1)
        at androidx.recyclerview.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:19)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:12)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:67)
        at androidx.recyclerview.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:3)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:8)
        at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:12)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at android.view.View.measure(View.java:23466)
        at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
        at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
        at android.view.View.measure(View.java:23466)
        at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
        at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:1)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:53)
        at android.view.View.measure(View.java:23466)
        at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
        at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:21)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1565)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:847)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:726)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1565)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:847)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:726)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6834)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
        at com.android.internal.policy.DecorView.onMeasure(DecorView.java:847)
        at android.view.View.measure(View.java:23466)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2954)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1753)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2041)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1636)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7946)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1092)
        at android.view.Choreographer.doCallbacks(Choreographer.java:893)
        at android.view.Choreographer.doFrame(Choreographer.java:812)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1078)
        at android.os.Handler.handleCallback(Handler.java:907)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:216)
        at android.app.ActivityThread.main(ActivityThread.java:7625)
        at java.lang.reflect.Method.invoke(Method.java)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
    
    bug 
    opened by SaikrishnaRajaraman 3
  • Is there any sample code about cross activities from recyclerview to viewpager?

    Is there any sample code about cross activities from recyclerview to viewpager?

    Recently I try to implement a cross activities demo. But I found that the ToActivity will flash at the moment it finished(). I use a SimpleTracker to generate ViewTransitionAnimator like:

    GestureTransitions.from(fromRecyclerView, fromTracker).into(viewPager, pagerTracker)

    only different is that fromRecyclerView is in FromActivity and viewPager in ToActivity.

    I try to hide/show origin ImageView like your sample code but pageAdapter.getViewHolder always return null. I don't know how to do it.

    question 
    opened by xanahopper 7
  • Conflict with Fresco DraweeView#getController() method

    Conflict with Fresco DraweeView#getController() method

    I want create a class like this

    class ReaderCellView extends DraweeView implements GestureView{
    //...
    }
    

    but both DraweeView and GestureView has function getController() and the return type is different.

    And I think it is better to make the function of a Interface as special as we can.

    enhancement 
    opened by caojianfeng 6
Releases(v2.8.3)
  • v2.8.3(Jan 6, 2022)

  • v2.8.2(Aug 9, 2021)

  • v2.8.1(May 26, 2021)

  • v2.8.0(May 26, 2021)

    • ViewPager2 support for images transition animation (#144).
    • Default animations duration is changed from 300ms to 200ms, to make the animations feel faster.
    • Fixed recycler view item's centering when auto scrolling the list (when using recycler view to view pager transitions).
    • Making sure State never uses NaNs (to catch possible UI issues earlier).
    Source code(tar.gz)
    Source code(zip)
  • v2.7.1(Nov 24, 2020)

  • v2.7.0(Nov 24, 2020)

    • Fixed bounds clipping animation (bug introduced after fix for #151)
    • Removed all deprecated API.
    • Organizing examples into groups.
    • Sample app fixes and major UI facelift.
    Source code(tar.gz)
    Source code(zip)
  • v2.6.0(Feb 4, 2020)

    • Migrating to AndroidX
    • Fixed bounds clipping logic when destination view is not fullscreen (#151)
    • Better support for nested GestureFrameLayout scrolling
    Source code(tar.gz)
    Source code(zip)
  • v2.5.2(May 5, 2018)

  • v2.5.1(Apr 15, 2018)

    • Added min zoom level setting (along with new Fit.NONE option)
    • Added bounds restrictions type setting, one of: NORMAL, INSIDE, OUTSIDE, PIVOT or NONE
    • Added setting to toggle fling animation
    • Fixed long click when all gestures are disabled
    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Apr 6, 2018)

  • v2.4.1(Jan 13, 2018)

    Bug fixes:

    • Do not zoom to min level if zoom gestures are disabled
    • Fixed issue in exit detector (animation state update if image is closed)
    • Ensure animation position stays within [0, 1]
    Source code(tar.gz)
    Source code(zip)
  • v2.4.0(Dec 10, 2017)

    • Min SDK set to 14
    • Distributing library as aar
    • Added XML attributes to provide settings for gesture views
    • Support for standard OnClickListener and OnLongClickListener
    • Added CropAreaView with rules support (superseding FinderView which is deprecated now)
    • Smooth image replacement (e.g. replace thumbnail with bigger image)
    • Exit gesture detection improvements
    • Improved cropping logic to prevent empty spaces
    • Fixed CircleImageView initialization
    Source code(tar.gz)
    Source code(zip)
  • v2.3.2(Sep 4, 2017)

  • v2.3.1(Jun 3, 2017)

  • v2.3.0(Apr 4, 2017)

    • Added setDoubleTapZoom(..) settings option.
    • Allowing animations from undefined 'from' position.
    • ViewsTracker replaced by FromTracker and IntoTracker to support cases where there are more than one image per list item.
    • ViewsTransitionBuilder is deprecated and replaced by GestureTransitions to provide more options and correct setup flow.
    • Automatically pre-setting image from source ImageView to target GestureImageView.
    • Fixed animation issue if item was removed from ViewPager.

    Migration notes

    ViewsTransitionBuilder, ViewsTracker and SimpleViewsTracker are deprecated. See migration wiki.

    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Dec 4, 2016)

    • Added gesture to exit full mode by scrolling up/down or zooming out.
      Added corresponding setting (enabled by default).
    • Added CircleImageView and CircleGestureImageView to show and animate rounded images.
    • Added animation duration setting, default duration is changed from 250 to 300 ms.
    • Added state source listener (whether image is idle, dragged by user or updated by animation).
    • Added debug overlay.
    • Better clipping when animating rotated images.
    • Fixed fast quick scale issue (image may be flipped).
    • Fixed scroll when zoomed out with bounds restrictions disabled.
    • Fixed wrap_content handling for GestureFrameLayout.
    Source code(tar.gz)
    Source code(zip)
Owner
Alex Vasilkov
Alex Vasilkov
Implementation of ImageView for Android that supports zooming, by various touch gestures.

PhotoView PhotoView aims to help produce an easily usable implementation of a zooming Android ImageView. [ Dependency Add this in your root build.grad

Baseflow 18.4k Dec 27, 2022
Customizable Android full screen image viewer for Fresco library supporting "pinch to zoom" and "swipe to dismiss" gestures. Made by Stfalcon

This project is no longer supported. If you're able to switch from Fresco to any other library that works with the Android's ImageView, please migrate

Stfalcon LLC 1.8k Dec 19, 2022
Android ImageView widget with zoom and pan capabilities

ImageViewTouch for Android ImageViewTouch is an android ImageView widget with zoom and pan capabilities. This is an implementation of the ImageView wi

Alessandro Crugnola 1.9k Jan 4, 2023
Crop and Rounded Corners added to an ImageView.

SuperImageView Extra features for your ImageView provided in a modularized way Documentation for v2 coming this week. CropImageView An ImageView that

César Díez Sánchez 657 Jan 5, 2023
Subclass of ImageView that 'morphs' into a circle shape and can rotates. Useful to be used as album cover in Music apps. :dvd::notes:

Music Cover View A Subclass of ImageView that 'morphs' into a circle shape and can rotates. Useful to be used as album cover in Music apps. It's used

André Mion 254 Dec 23, 2022
Create parallax and any other transformation effects on scrolling android ImageView

Android Parallax Image View Creates effect such as vertical parallax, horizontal parallax etc. on android ImageView when it's being vertically or hori

Aris 164 Dec 7, 2022
Crop and Rounded Corners added to an ImageView.

SuperImageView Extra features for your ImageView provided in a modularized way Documentation for v2 coming this week. CropImageView An ImageView that

César Díez Sánchez 658 Dec 1, 2022
A circular ImageView for Android

CircleImageView A fast circular ImageView perfect for profile images. This is based on RoundedImageView from Vince Mi which itself is based on techniq

Henning Dodenhof 13.8k Mar 29, 2021
Adds touch functionality to Android ImageView.

TouchImageView for Android Capabilities TouchImageView extends ImageView and supports all of ImageView’s functionality. In addition, TouchImageView ad

Michael Ortiz 2.4k Mar 28, 2021
Custom shaped android imageview components

Shape Image View Provides a set of custom shaped android imageview components, and a framework to define more shapes. Implements both shader and bitma

Siyamed SINIR 2.6k Mar 29, 2021
Android ImageView replacement which allows image loading from URLs or contact address book, with caching

Smart Image View for Android SmartImageView is a drop-in replacement for Android’s standard ImageView which additionally allows images to be loaded fr

James Smith 1.3k Dec 24, 2022
Implements pinch-zoom, rotate, pan as an ImageView for Android 2.1+

GestureImageView This is a simple Android View class which provides basic pinch and zoom capability for images. Can be used as a replacement for a sta

Jason 1.1k Nov 10, 2022
Android ImageView that handles animated GIF images

GifImageView Android ImageView that handles Animated GIF images Usage In your build.gradle file: dependencies { compile 'com.felipecsl:gifimageview:

Felipe Lima 1.1k Mar 9, 2021
Android ImageView that supports different radii on each corner.

SelectableRoundedImageView Note that this project is no longer maintained. Android ImageView that supports different radii on each corner. It also sup

Joonho Kim 1.1k Mar 17, 2021
ImageView with a tag on android

SimpleTagImageView ImageView with a tag in android. So it's a ImageView. Demo ####Warning:When you set the round radius,the simpletagimageview scale t

null 944 Nov 10, 2022
Custom ImageView for moving image around the screen (Android)

MovingImageView Create a custom ImageView for moving image around the screen. Usage To use MovingImageView, add the module into your project and start

Albert Grobas 819 Nov 18, 2022
Custom ImageView for android with polygon shape (Android)

PolygonImageView Create a custom ImageView with polygonal forms. Usage To use PolygonImageView, add the module into your project and start to build xm

Albert Grobas 531 Dec 25, 2022
BadgedImageview allow you show a badge into a Imageview.

BadgedImageview BadgedImageview allow you show a badge into a Imageview. I just extracted the widgets from Plaid app developed by Nick Butcher(https:/

Yesid 435 Apr 4, 2022