几行代码搞定画廊效果

几行代码搞定画廊效果曲木为直终必弯,养狼当犬看家难。墨染鸬鹚黑不久,粉刷乌鸦白不坚。蜜饯黄莲终需苦,强摘瓜果不能甜。好事总得善人做,哪有凡人做神仙。当!废话不多言,上回书说道,我最近寻思干点嘛,却又无所事事,天天水群,于是心不安理不得,这天忽然看到一个画廊效果,虽然已是过时产物,但是本着劳资不会,就是比比的崇高目标,结果遭人鄙视,无人同情,令人叹惋。于是乎,奋笔疾书,瞎(说鸡不说吧,文明你我他)写,终于

大家好,又见面了,我是你们的朋友全栈君。

曲木为直终必弯,养狼当犬看家难。墨染鸬鹚黑不久,粉刷乌鸦白不坚。蜜饯黄莲终需苦,强摘瓜果不能甜。好事总得善人做,哪有凡人做神仙。
当!
废话不多言,上回书说道,我最近寻思干点嘛,却又无所事事,天天水群,于是心不安理不得,这天忽然看到一个画廊效果,虽然已是过时产物,但是本着劳资不会,就是比比的崇高目标,结果遭人鄙视,无人同情,令人叹惋。
alone
于是乎,奋笔疾书,瞎(说鸡不说吧,文明你我他)写,终于在某年某月某时某分拼凑出来,效果如下:
这就是效果咯
因为做的是本地图片加载的画廊效果,在加载网络图片时会有一定的闪屏,文末也会给大家提供解决方法,但本例写的时候开始使用的是本地图片。
首先我们拿到布局,应该想到,上面的大图应该是一个ImageView,而下面是一个可横向滑动View,因为横向滑动的View并没有多少选择,所以这里我选择了recyclerView,这里看基本的布局:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >
        <test.com.xiaomamenu.RectImageView
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:layout_height="200dp"
            android:transitionName="translate"
            android:id="@+id/second_img"
            android:layout_margin="30dp"
            android:scaleType="centerCrop"
            />
        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/second_rv"
            ></android.support.v7.widget.RecyclerView>

    </LinearLayout>

</FrameLayout>

这个RectImageView继承了ImageView,在onmeaure的时候使高度和宽度相等,这个非常简单,这里就不提了,实在不会的可以使用ImageView高度宽度相等即可。
这里应该注意一点,最外层的view必须是FrameLayout,因为我们后面会根据FrameLayout的特性进行动画的操作。
接下来,我们定义一个RecyclerView的Adapter,这里只是代码演示,所以代码比较粗糙,勿怪。

 rv.setLayoutManager(new LinearLayoutManager(SecondActivity.this, LinearLayoutManager.HORIZONTAL, false));

        rv.setAdapter(new RecyclerView.Adapter<SecondActivity.VH>() {
            @Override
            public SecondActivity.VH onCreateViewHolder(ViewGroup parent, int viewType) {
                return new SecondActivity.VH(LayoutInflater.from(SecondActivity.this).inflate(R.layout.new_item, parent, false));
            }

            @Override
            public void onBindViewHolder(final SecondActivity.VH holder, final int position) {
            //holder.img.setImageResource(imgs.get(position));
                Glide.with(SecondActivity.this).load(imgs.get(position)).dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).into(holder.img);
            }
               @Override
            public int getItemCount() {
                return imgs.size();
            }
    });

这样我们非常质朴的Adapter就写出来了,首先我们根据图片应该联想到,两张图片对换,那么我们肯定需要list 和 大图资源数据的对换,这样我们需要一个变量来解决这个问题。
接下来我们开始构造方法,首先我希望可以通过单例的形式实现点击的时候动画交互效果,这样能有效的节约资源和内存,那么首先我们先无脑new出来一个utils先。

public class AnimatorUtils<T> {
  private static AnimatorUtils utils;
    public static AnimatorUtils getInstance() {
        if (null == utils) {
            synchronized (AnimatorUtils.class) {
                if (null == utils) {
                    utils = new AnimatorUtils();
                }
            }
        }
        return utils;
    }

}

这样我们构造出了一个单例模式,然后我们开始写具体的方法,首先我们肯定需要知道和我们交互的itemView和ImageView,然后我们还需要context上下文来获取根布局以及一些其他的操作,还有List数据,当前itemView的position位置,以及大图的资源文件,因为大图的资源文件未知,所以我这里使用了泛型,我们大概就只需要这么多的参数,好了,接下来开始大段的代码,代码上都写了注释,所以应该很容易理解。

  //获取当前activity下的根布局
        ViewGroup vp= (ViewGroup) ((Activity)context).findViewById(android.R.id.content);
        ViewGroup rootView = (ViewGroup) vp.getChildAt(0);
        if (!((rootView instanceof RelativeLayout) || (rootView instanceof FrameLayout))) {
            throw new IllegalArgumentException("关掉重试,换换运气");
        }

        //获取target的宽高
        translateViewWidth = translateView.getWidth();
        translateViewHeight = translateView.getHeight();
        if (translateViewWidth <= 0 || translateViewHeight <= 0) {
            throw new IllegalArgumentException("建议你删代码跑路");
        }
        //获取itemView的宽高
        itemWidth = itemView.getWidth();
        itemHeight = itemView.getHeight();
        if (itemWidth <= 0 || itemHeight <= 0) {
            throw new IllegalArgumentException("我已经删掉了android studio");
        }
        //获取itemView所在的位置
        itemView.getLocationInWindow(itemViewLocation);
        //获取target所在位置
        translateView.getLocationInWindow(translateViewLocation);
        //获取状态栏高度
        int statusHeight = getStatusHeight();

不要在意我throw错误的那些细节,那些都只是我痛苦血泪之后的真实。
我们获取了一些必要的参数,比如当前itemView和ImageView在window中的位置,这样才能做动画的参数值,但是该怎么样做动画呢,首先属性动画虽然能改变view的属性,但是并不能改变view在当前层级的位置,所以我们并不能直接操作itemView和ImageView,这样我们就只剩下一条路了,造假!
于是乎,乎如是,一个又low又2又弱鸡的方案就诞生了。

  //第一次add进来
        if (translateViewBitmap == null) {
            translateViewBitmap = new ImageView(context);
            rootView.addView(translateViewBitmap);

            lp2 = (FrameLayout.LayoutParams) translateViewBitmap.getLayoutParams();
            if (lp2 != null) {
                lp2.width = translateViewWidth;
                lp2.height = translateViewHeight;
                lp2.setMargins(translateViewLocation[0], translateViewLocation[1] - statusHeight, 0, 0);
            } else {
                lp2 = new FrameLayout.LayoutParams(translateViewWidth, translateViewHeight);
                lp2.setMargins(translateViewLocation[0], translateViewLocation[1] - statusHeight, 0, 0);
            }
            translateViewBitmap.setLayoutParams(lp2);
        } else {
            translateViewBitmap.setVisibility(View.VISIBLE);
            translateViewBitmap.setScaleX(1);
            translateViewBitmap.setScaleY(1);
            translateViewBitmap.setTranslationX(0);
            translateViewBitmap.setTranslationY(0);
            lp2.width = translateViewWidth;
            lp2.height = translateViewHeight;
            lp2.setMargins(translateViewLocation[0], translateViewLocation[1] - statusHeight, 0, 0);
            translateViewBitmap.setLayoutParams(lp2);

        }
        translateView.destroyDrawingCache();
        translateView.setDrawingCacheEnabled(true);
        translateView.buildDrawingCache();
        targetBitmap = translateView.getDrawingCache();
        if (targetBitmap != null) {
            translateViewBitmap.setImageBitmap(targetBitmap);
        }
        //同理
        if (itemViewBitmap == null) {
            itemViewBitmap = new ImageView(context);
            rootView.addView(itemViewBitmap);
            lp = (FrameLayout.LayoutParams) itemViewBitmap.getLayoutParams();
            if (lp != null) {
                lp.width = itemWidth;
                lp.height = itemHeight;
                lp.setMargins(itemViewLocation[0], itemViewLocation[1] - statusHeight, 0, 0);
            } else {
                lp = new FrameLayout.LayoutParams(itemWidth, itemHeight);
                lp.setMargins(itemViewLocation[0], itemViewLocation[1] - statusHeight, 0, 0);
            }
            itemViewBitmap.setLayoutParams(lp);
        } else {
            //itemViewBitmap.animate().scaleY(1).scaleX(1).translationX(-itemTranslateX).translationY(-itemTranslateY);
            itemViewBitmap.setVisibility(View.VISIBLE);
            itemViewBitmap.setScaleX(1);
            itemViewBitmap.setScaleY(1);
            itemViewBitmap.setTranslationX(0);
            itemViewBitmap.setTranslationY(0);
            lp.width = itemWidth;
            lp.height = itemHeight;
            lp.setMargins(itemViewLocation[0], itemViewLocation[1] - statusHeight, 0, 0);
            itemViewBitmap.setLayoutParams(lp);
        }
        itemView.buildDrawingCache();
        itemBitmap = itemView.getDrawingCache();
        if (itemBitmap != null) {
            itemViewBitmap.setImageBitmap(itemBitmap);
        }

这里我们new出来两个imageView,但是为了防止无限次的重复add,在这里用if else进行了判断,如果是首次执行,我们设置好必要的属性,就可以了,但是以后执行,我们都需要对view进行初始化,这里也踩了很多暗坑,比如获取view的bitmap时必须先destroyDrawingCache才能正确的获取当前view的真实图层。

  translateView.destroyDrawingCache();
  translateView.setDrawingCacheEnabled(true);
  translateView.buildDrawingCache();
  targetBitmap = translateView.getDrawingCache();

我们生成了两个与itemView与ImageView一模一样的复制品,当然我们就需要把真实的两个进行绑票。

  //设置成invisible,保持位置
        itemView.setVisibility(View.INVISIBLE);
        translateView.setVisibility(View.INVISIBLE);

接下来我们就该思考如何使用动画了,这也多亏了我数学成绩多年来惊人的稳定,两年高考成绩110分的骄人成绩(咳咳,我说的是两年加起来的总分),我差点就直接放弃了。不多说了,都是泪啊。

 private AnimatorSet createAnimator() {
        AnimatorSet set = new AnimatorSet();
        //这是item-->target动画
        float itemToTargetScaleX = (float) translateViewWidth / itemWidth;
        float itemToTargetScaleY = (float) translateViewHeight / itemHeight;
        PropertyValuesHolder itemScaleXHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 1.0f, itemToTargetScaleX);
        PropertyValuesHolder itemScaleYHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.0f, itemToTargetScaleY);
        //这里位移算法  当前位置-target位置,因为view进行了scale放大,所以要加上1/2进行补偿
        itemTranslateX = -(itemViewLocation[0] - translateViewLocation[0] - (itemToTargetScaleX - 1) * itemWidth / 2);
        PropertyValuesHolder itemToTargetXHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, itemTranslateX);
        itemTranslateY = -(itemViewLocation[1] - translateViewLocation[1] - (itemToTargetScaleY - 1) * itemHeight / 2);
        PropertyValuesHolder itemToTargetYHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, itemTranslateY);
        ObjectAnimator itemAnimator = ObjectAnimator.ofPropertyValuesHolder(itemViewBitmap, itemScaleXHolder, itemScaleYHolder, itemToTargetXHolder, itemToTargetYHolder);

        //这是target-->item的动画
        float targetToItemScaleX = (float) itemWidth / translateViewWidth;
        float targetToItemScaleY = (float) itemHeight / translateViewHeight;
        PropertyValuesHolder targetScaleXHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 1.0f, targetToItemScaleX);
        PropertyValuesHolder targetScaleYHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.0f, targetToItemScaleY);
        //这里位移算法  当前位置-target位置,因为view进行了scale放大,所以要加上1/2进行补偿
        targetTranslateX = -(translateViewLocation[0] - itemViewLocation[0] - (targetToItemScaleX - 1) * translateViewWidth / 2);
        PropertyValuesHolder targetToItemXHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, targetTranslateX);
        targetTranslateY = -(translateViewLocation[1] - itemViewLocation[1] - (targetToItemScaleY - 1) * translateViewHeight / 2);
        PropertyValuesHolder targetToItemYHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, targetTranslateY);
        ObjectAnimator targetAnimator = ObjectAnimator.ofPropertyValuesHolder(translateViewBitmap, targetScaleXHolder, targetScaleYHolder, targetToItemXHolder, targetToItemYHolder);
        set.playTogether(itemAnimator, targetAnimator);
        //set.play(targetAnimator);
        set.setDuration(1000);
        set.setInterpolator(new AccelerateDecelerateInterpolator());
        return set;
    }

这里用到了属性动画,还不熟悉的各位大佬可以去鸿洋sama的博客参观浏览,而且不收费呦。
接下来就好写多了,我们在方法内,执行动画,监听动画结束即可,在动画结束时,我们需要给真实的itemView和imageView赔礼道歉,并把它们放出来并且换一身新衣服了。注意这里是本地和网络图片最大的区分点,本地图片我们加载基本是不需要时间的,而网络加载由于网络条件和加载库的原因我们并不能在动画完成后进行加载,而是应该在动画开始时,就进行设置相关的资源,所以这里就产生了分支,不过本文只提供思路,就不提供相应代码了,你来打我吧,反正我皮厚。

  set = createAnimator();
        if (set.isRunning()) {
            set.end();
        }
        set.start();
        set.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
                if (null != dataAgain) {
                    dataAgain.dataChanged();
                }
                targetBitmap = null;
                itemBitmap = null;
                itemViewBitmap.setVisibility(View.GONE);
                translateViewBitmap.setVisibility(View.GONE);
                itemView.setVisibility(View.VISIBLE);
                translateView.setVisibility(View.VISIBLE);
                list.remove(position);
                list.add(position, t);
                set.removeAllListeners();
                set = null;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);

                if (null != dataAgain) {
                    dataAgain.dataChanged();
                }

                targetBitmap = null;
                itemBitmap = null;
                itemViewBitmap.setVisibility(View.GONE);
                translateViewBitmap.setVisibility(View.GONE);
                itemView.setVisibility(View.VISIBLE);
                translateView.setVisibility(View.VISIBLE);
                list.remove(position);
                list.add(position, t);
                set.removeAllListeners();
                set = null;
            }
        });

    }

这样我们就基本写好了,但是有一些细节还值得深究,如果我们频繁的点击itemView会造成动画的错乱,那么我们在点击时,就需要屏蔽掉其他itemView的点击事件。

  final ViewGroup rv = (ViewGroup) itemView.getParent();
        if(!(rv instanceof  RecyclerView)){
            throw new IllegalArgumentException("不是RecyclerView我不接客");
        }
        if (null != rv) {
            for (int i = 0; i < rv.getChildCount(); i++) {
                View view = rv.getChildAt(i);
                if (null != view) {
                    if (i == position) {
                        view.setClickable(true);
                    } else {
                        view.setClickable(false);
                    }
                }
            }
        }

这样我们就在点击时只能让一个ItemView进行点击,当然这里还有更加节约资源的方法,这里就不做叙述了。
另外还有一个就是我们再点击时如果滑动的话,我们动画的位置是不准确的,所以我们要在点击时尽量避免RecyclerView的滑动,但是该怎么做呢,首先我想到的是自定义view,通过判断canScroll系列的return值来进行实现,但是太麻烦了,而且侵入性太大,(当然主要是太麻烦了)。
于是,一个邪恶的计划就诞生了,反射吧。
我们通过LayoutManger的源码得知,LM的是否支持滑动,是依赖于两个方法:

 /** * @return true if {@link #getOrientation()} is {@link #HORIZONTAL} */
    @Override
    public boolean canScrollHorizontally() {
        return mOrientation == HORIZONTAL;
    }

    /** * @return true if {@link #getOrientation()} is {@link #VERTICAL} */
    @Override
    public boolean canScrollVertically() {
        return mOrientation == VERTICAL;
    }

而其中VERTICAL和HORIZONTAL都是final类型,并不能更改,所以这里就修改mOrientation 的值来返回他是否可以滑动,接下来直接上代码:

public class CanScroll {

    public static void canScroll(RecyclerView recyclerView, boolean canScroll) {
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof LinearLayoutManager) {
            if (((LinearLayoutManager) manager).getOrientation() == LinearLayoutManager.VERTICAL) {
                try {
                    Class<? extends LinearLayoutManager> aClass = ((LinearLayoutManager) manager).getClass();
                    Field field = aClass.getDeclaredField("mOrientation");
                    field.setAccessible(true);
                    changeFields(field,manager,canScroll,LinearLayoutManager.VERTICAL);
                } catch (Exception e) {

                }

            }else {
                try {
                    Class<? extends LinearLayoutManager> aClass = ((LinearLayoutManager) manager).getClass();
                    Field field = aClass.getDeclaredField("mOrientation");
                    field.setAccessible(true);
                    changeFields(field,manager,canScroll,LinearLayoutManager.HORIZONTAL);
                } catch (Exception e) {

                }
            }
        }
    }

    public static void changeFields(Field field, RecyclerView.LayoutManager manager, boolean canScroll,int orientation) {
        try {
            if (canScroll) {
                field.set(manager, orientation);
            } else {
                field.set(manager, -1);
            }
        } catch (Exception e) {

        }
    }

}

我们通过反射来完成了对RecyclerView是否可以滑动。我们再点击的时候进行执行 CanScroll.canScroll((RecyclerView) rv,false);在动画完成的监听里执行 CanScroll.canScroll((RecyclerView) rv,true);这样我们基本完成了这次博客的旅程,接下来是完整代码:

public class AnimatorUtils<T> {
private ImageView itemViewBitmap, translateViewBitmap;
private int[] itemViewLocation = new int[2];
private int[] translateViewLocation = new int[2];
private FrameLayout.LayoutParams lp, lp2;
private int translateViewWidth;
private int translateViewHeight;
private int itemWidth;
private int itemHeight;
private static AnimatorSet set;
private Bitmap targetBitmap;
private Bitmap itemBitmap;
private Context context;
private static AnimatorUtils utils;
private float itemTranslateX;
private float itemTranslateY;
private float targetTranslateX;
private float targetTranslateY;
public static AnimatorUtils getInstance() {
if (null == utils) {
synchronized (AnimatorUtils.class) {
if (null == utils) {
utils = new AnimatorUtils();
}
}
}
return utils;
}
public void translate(Context context,  final View itemView, final View translateView, final List<T> list, final int position, final T t) {
this.context = context;
final ViewGroup rv = (ViewGroup) itemView.getParent();
if(!(rv instanceof  RecyclerView)){
throw new IllegalArgumentException("不是RecyclerView我不接客");
}
CanScroll.canScroll((RecyclerView) rv,false);
if (null != rv) {
for (int i = 0; i < rv.getChildCount(); i++) {
View view = rv.getChildAt(i);
if (null != view) {
if (i == position) {
view.setClickable(true);
} else {
view.setClickable(false);
}
}
}
}
if (set != null && set.isRunning()) {
return;
}
//获取当前activity下的根布局
ViewGroup vp= (ViewGroup) ((Activity)context).findViewById(android.R.id.content);
ViewGroup rootView = (ViewGroup) vp.getChildAt(0);
if (!((rootView instanceof RelativeLayout) || (rootView instanceof FrameLayout))) {
throw new IllegalArgumentException("关掉重试,换换运气");
}
//获取target的宽高
translateViewWidth = translateView.getWidth();
translateViewHeight = translateView.getHeight();
if (translateViewWidth <= 0 || translateViewHeight <= 0) {
throw new IllegalArgumentException("建议你删代码跑路");
}
//获取itemView的宽高
itemWidth = itemView.getWidth();
itemHeight = itemView.getHeight();
if (itemWidth <= 0 || itemHeight <= 0) {
throw new IllegalArgumentException("我已经删掉了android studio");
}
//获取itemView所在的位置
itemView.getLocationInWindow(itemViewLocation);
//获取target所在位置
translateView.getLocationInWindow(translateViewLocation);
//获取状态栏高度
int statusHeight = getStatusHeight();
//第一次add进来
if (translateViewBitmap == null) {
translateViewBitmap = new ImageView(context);
rootView.addView(translateViewBitmap);
lp2 = (FrameLayout.LayoutParams) translateViewBitmap.getLayoutParams();
if (lp2 != null) {
lp2.width = translateViewWidth;
lp2.height = translateViewHeight;
lp2.setMargins(translateViewLocation[0], translateViewLocation[1] - statusHeight, 0, 0);
} else {
lp2 = new FrameLayout.LayoutParams(translateViewWidth, translateViewHeight);
lp2.setMargins(translateViewLocation[0], translateViewLocation[1] - statusHeight, 0, 0);
}
translateViewBitmap.setLayoutParams(lp2);
} else {
translateViewBitmap.setVisibility(View.VISIBLE);
translateViewBitmap.setScaleX(1);
translateViewBitmap.setScaleY(1);
translateViewBitmap.setTranslationX(0);
translateViewBitmap.setTranslationY(0);
lp2.width = translateViewWidth;
lp2.height = translateViewHeight;
lp2.setMargins(translateViewLocation[0], translateViewLocation[1] - statusHeight, 0, 0);
translateViewBitmap.setLayoutParams(lp2);
}
translateView.destroyDrawingCache();
translateView.setDrawingCacheEnabled(true);
translateView.buildDrawingCache();
targetBitmap = translateView.getDrawingCache();
if (targetBitmap != null) {
translateViewBitmap.setImageBitmap(targetBitmap);
}
//同理
if (itemViewBitmap == null) {
itemViewBitmap = new ImageView(context);
rootView.addView(itemViewBitmap);
lp = (FrameLayout.LayoutParams) itemViewBitmap.getLayoutParams();
if (lp != null) {
lp.width = itemWidth;
lp.height = itemHeight;
lp.setMargins(itemViewLocation[0], itemViewLocation[1] - statusHeight, 0, 0);
} else {
lp = new FrameLayout.LayoutParams(itemWidth, itemHeight);
lp.setMargins(itemViewLocation[0], itemViewLocation[1] - statusHeight, 0, 0);
}
itemViewBitmap.setLayoutParams(lp);
} else {
//itemViewBitmap.animate().scaleY(1).scaleX(1).translationX(-itemTranslateX).translationY(-itemTranslateY);
itemViewBitmap.setVisibility(View.VISIBLE);
itemViewBitmap.setScaleX(1);
itemViewBitmap.setScaleY(1);
itemViewBitmap.setTranslationX(0);
itemViewBitmap.setTranslationY(0);
lp.width = itemWidth;
lp.height = itemHeight;
lp.setMargins(itemViewLocation[0], itemViewLocation[1] - statusHeight, 0, 0);
itemViewBitmap.setLayoutParams(lp);
}
itemView.buildDrawingCache();
itemBitmap = itemView.getDrawingCache();
if (itemBitmap != null) {
itemViewBitmap.setImageBitmap(itemBitmap);
}
//设置成invisible,保持位置
itemView.setVisibility(View.INVISIBLE);
translateView.setVisibility(View.INVISIBLE);
set = createAnimator();
if (set.isRunning()) {
set.end();
}
set.start();
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
if (null != dataAgain) {
dataAgain.dataChanged();
}
CanScroll.canScroll((RecyclerView) rv,true);
targetBitmap = null;
itemBitmap = null;
itemViewBitmap.setVisibility(View.GONE);
translateViewBitmap.setVisibility(View.GONE);
itemView.setVisibility(View.VISIBLE);
translateView.setVisibility(View.VISIBLE);
list.remove(position);
list.add(position, t);
set.removeAllListeners();
set = null;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (null != dataAgain) {
dataAgain.dataChanged();
}
CanScroll.canScroll((RecyclerView) rv,true);
targetBitmap = null;
itemBitmap = null;
itemViewBitmap.setVisibility(View.GONE);
translateViewBitmap.setVisibility(View.GONE);
itemView.setVisibility(View.VISIBLE);
translateView.setVisibility(View.VISIBLE);
list.remove(position);
list.add(position, t);
set.removeAllListeners();
set = null;
}
});
}
private AnimatorSet createAnimator() {
AnimatorSet set = new AnimatorSet();
//这是item-->target动画
float itemToTargetScaleX = (float) translateViewWidth / itemWidth;
float itemToTargetScaleY = (float) translateViewHeight / itemHeight;
PropertyValuesHolder itemScaleXHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 1.0f, itemToTargetScaleX);
PropertyValuesHolder itemScaleYHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.0f, itemToTargetScaleY);
//这里位移算法 当前位置-target位置,因为view进行了scale放大,所以要加上1/2进行补偿
itemTranslateX = -(itemViewLocation[0] - translateViewLocation[0] - (itemToTargetScaleX - 1) * itemWidth / 2);
PropertyValuesHolder itemToTargetXHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, itemTranslateX);
itemTranslateY = -(itemViewLocation[1] - translateViewLocation[1] - (itemToTargetScaleY - 1) * itemHeight / 2);
PropertyValuesHolder itemToTargetYHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, itemTranslateY);
ObjectAnimator itemAnimator = ObjectAnimator.ofPropertyValuesHolder(itemViewBitmap, itemScaleXHolder, itemScaleYHolder, itemToTargetXHolder, itemToTargetYHolder);
//这是target-->item的动画
float targetToItemScaleX = (float) itemWidth / translateViewWidth;
float targetToItemScaleY = (float) itemHeight / translateViewHeight;
PropertyValuesHolder targetScaleXHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 1.0f, targetToItemScaleX);
PropertyValuesHolder targetScaleYHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.0f, targetToItemScaleY);
//这里位移算法 当前位置-target位置,因为view进行了scale放大,所以要加上1/2进行补偿
targetTranslateX = -(translateViewLocation[0] - itemViewLocation[0] - (targetToItemScaleX - 1) * translateViewWidth / 2);
PropertyValuesHolder targetToItemXHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, targetTranslateX);
targetTranslateY = -(translateViewLocation[1] - itemViewLocation[1] - (targetToItemScaleY - 1) * translateViewHeight / 2);
PropertyValuesHolder targetToItemYHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, targetTranslateY);
ObjectAnimator targetAnimator = ObjectAnimator.ofPropertyValuesHolder(translateViewBitmap, targetScaleXHolder, targetScaleYHolder, targetToItemXHolder, targetToItemYHolder);
set.playTogether(itemAnimator, targetAnimator);
//set.play(targetAnimator);
set.setDuration(1000);
set.setInterpolator(new AccelerateDecelerateInterpolator());
return set;
}
private int getStatusHeight() {
Rect rectangle = new Rect();
Window window = ((Activity) context).getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectangle);
return rectangle.top;
}
public interface DataAgain {
void dataChanged();
}
private DataAgain dataAgain;
public void setDataAgain(DataAgain dataAgain) {
this.dataAgain = dataAgain;
}
}

CanScroll的代码:

public class CanScroll { 
 public static void canScroll(RecyclerView recyclerView, boolean canScroll) { RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if (manager instanceof LinearLayoutManager) { if (((LinearLayoutManager) manager).getOrientation() == LinearLayoutManager.VERTICAL) { try { Class<? extends LinearLayoutManager> aClass = ((LinearLayoutManager) manager).getClass(); Field field = aClass.getDeclaredField("mOrientation"); field.setAccessible(true); changeFields(field,manager,canScroll,LinearLayoutManager.VERTICAL); } catch (Exception e) { 
 } }else { try { Class<? extends LinearLayoutManager> aClass = ((LinearLayoutManager) manager).getClass(); Field field = aClass.getDeclaredField("mOrientation"); field.setAccessible(true); changeFields(field,manager,canScroll,LinearLayoutManager.HORIZONTAL); } catch (Exception e) { 
 } } } } public static void changeFields(Field field, RecyclerView.LayoutManager manager, boolean canScroll,int orientation) { try { if (canScroll) { field.set(manager, orientation); } else { field.set(manager, -1); } } catch (Exception e) { } } } 

接下来我们只需要在adapter中执行itemView的click来看看效果了,首先我们在全局进行初始化我们的utils

private AnimatorUtils utils = AnimatorUtils.getInstance();
......
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
utils.translate(SecondActivity.this, holder.itemView, sImg, imgs, position, nowImg);
utils.setDataAgain(new AnimatorUtils.DataAgain() {
@Override
public void dataChanged() {
String img = imgs.get(position);
Glide.with(SecondActivity.this).load(img).dontAnimate().dontTransform().diskCacheStrategy(DiskCacheStrategy.ALL).into(sImg);
nowImg = img;
notifyDataSetChanged();
}
});
}
});

最后的接口中,我们需要对现在大图加载的图片进行重新的设值,这样的话我们就可以在下次点击时能准确的获取当前加载图片的资源。
好了,本章完结。最后上老婆图一张:
我老婆

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/137476.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号