美文网首页自定义Android开发感悟程序员
[Android] Gallery选中变大效果的实现

[Android] Gallery选中变大效果的实现

作者: wuzhen | 来源:发表于2016-04-15 21:00 被阅读5570次

    需求:横向滚动的列表,操作类似 Gallery,选中的 Item 居中且放大显示,UI 效果如下图:

    UI效果示例.jpg

    实现方案:

    从 API 16 开始,因为效率问题系统控件 Gallery 已被废弃,官方建议使用 HorizontalScrollView 实现,但是根据效果图使用 HorizontalScrollView 实现工作量较大,所以引入了第三方控件:EcoGallery

    1. 重写 EcoGallerygetChildStaticTransformation(View, Transformation) 方法:

    // View mSelectedChild:记录选中的 Item 的 View
    // float mSelectedScale:选中的 Item 放大的倍数
    @Override
    protected boolean getChildStaticTransformation(View child, 
            Transformation t) {
        if (mSelectedScale != 1.f) {
            t.clear();
            t.setTransformationType(Transformation.TYPE_MATRIX);
    
            // 判断需要变换的 child 是否为选中的 View
            if (child == mSelectedChild) {
                // 根据效果图,child 以 (x轴中心、y轴最底部) 为中心点进行缩放
                // 设置缩放的中心点横坐标为 child 的 x 轴中心位置
                float pivotX = child.getWidth() / 2.f;
    
                // 设置缩放的中心点纵坐标为 child 的 y 轴最底部
                float pivotY = child.getHeight() - child.getPaddingBottom();
                Matrix matrix = t.getMatrix();
                matrix.postScale(mSelectedScale, mSelectedScale,
                        pivotX, pivotY);
            }
        }
        return true;
    }
    

    该实现方案有两个问题:

    1. 如果不指定默认选中的 Item,进入页面后第0个 Item 会处于选中状态,之后滚动 Gallery 选中其他的 Item 后,第0个 Item 不会变小,一直显示成放大后的效果,直到再次选中第0个 Item 后才能恢复正常,打印 Log 发现进入页面后从第0个滚动到其他 Item 时 getChildStaticTransformation() 方法中的 child 没有第0个 Item,具体原因未知。
    2. Item 选中后会瞬间变大、变小,体验上不是很好。

    因为上面的两个问题,于是放弃了该实现方案。

    之后搜索了一些资料,发现了这篇文章:
    关于getChildStaticTransformation在android4.1失效问题解决方案

    通过在画 child 之前对 Canvas 进行必要的变换,如上 Transformation 变换类似,重载 drawChild 方法

    根据效果图,具体的实现方案思路为:因为 Gallery 选中的 Item 居中显示,所以在绘制 Item 时先计算 Item 和 Gallery 中心点的距离。如果距离=0,此时 Item 应该放大显示,且放大的比例为最大值;列表滚动后,当前放大显示的 Item 远离中心点,放大的比例逐渐变小,而此时列表另一端的 Item 正在接近中心点,显示的比例逐渐变大;当 Item 与中心点的距离超过 Item 的宽度时不再进行放大显示。

    // View mSelectedChild:记录选中的 Item 的 View
    // float mSelectedScale:选中的 Item 放大的倍数
    @Override
    protected boolean drawChild(Canvas canvas, View child,
            long drawingTime) {
    
        if (mSelectedScale != 1.f) {
            // 获取 Item 的 View 的宽度和高度
            int childWidth = child.getWidth();
            int childHeight = child.getHeight();
    
            // 获取 Gallery 中心点坐标的 x 值
            final int center = getCenterOfGallery();
    
            // 获取 Item 中心点坐标的 x 值
            final int childCenter = child.getLeft() + childWidth / 2;
    
            // 计算 child 中心点和 Gallery 中心点的距离
            final int offsetCenter = Math.abs(center - childCenter);
            final float offsetScale = (childWidth - offsetCenter)
                    * 1.f / childWidth;
    
            // 如果 child 和中心点的距离小于 child 的宽度,此时需要放大显示
            if (offsetCenter < childWidth) {
                // 设置缩放的中心点坐标
                child.setPivotX(childWidth / 2.f);
                child.setPivotY(childHeight - child.getPaddingBottom());
    
                // 根据和中心点的距离计算缩放比例
                float scale = 1 + (mSelectedScale - 1) * offsetScale;
                child.setScaleX(scale);
                child.setScaleY(scale);
            }
            // 如果 child 和中心点的距离不小于 child 的宽度,不需要放大显示
            else {
                child.setScaleX(1.f);
                child.setScaleY(1.f);
            }
        }
        return super.drawChild(canvas, child, drawingTime);
    }
    

    最终实现的效果如下图:

    Gallery最终效果图

    参考

    1. GitHub: EcoGallery
    2. 关于getChildStaticTransformation在android4.1失效问题解决方案

    源码

    GitHub: EcoGalleryDemo


    开源

    目前该控件已开源:GitHub: android-wgallery


    最近发现一个比较好的实现方式:使用 ViewPager + ViewPager.PageTransformer,父控件设置 android:clipChildren="false",具体的代码待整理完成后开放出来。

    具体实现方式参考:

    GitHub: android-xgallery

    相关文章

      网友评论

      • 心一如既往的空荡:在fragment中,每当fragment show的时候就调用一次,该如何处理啊
      • iblade_wang:GitHub: android-xgallery这个无法使用,Demo的Apk安装解析失败,求指津!
      • 未央_984a:为什么在fragment中加载不出图片,在activity中可以
        wuzhen:怎么加载的图片
      • SheepYang:楼主,手指松开后回滚的速度能设置吗???
      • imyyq_star:没有通过代码设置当前页吗?我用setSelection(int, boolean)方法,可是感觉太突兀了。还有一个,可不可以加入我滚动到某一页后稳定下来后的回调?
        DINGCHAO_:时间过去这么久了,给后来者一个提示吧,设置gallery.setCallbackDuringFling(false);就可以让itemSelected执行你最后选中的那个了,另外,感谢楼主提供干货
        待定者:在recyclerview的item里面使用这个控件,如果你的item中有图片是从网络请求来的 那么的图片会无法显示出来,并且滑动的时候会卡顿,且来回跳动。图片闪动
        imyyq_star:回调的已经解决了,原来有ItemSelected接口
      • f49611ea1632:我试了下 发现了些问题 因为EcoGallery 会频繁的调用mAdapter.getView方法,在某些情况下 会造成死循环的调用mAdapter.getView方法,如果你的item中有图片是从网络请求来的 那么的图片会无法显示出来,并且滑动的时候会卡顿,且来回跳动。 :joy:
        f49611ea1632:@wuzhen 具体什么原因我不知道 但是我发现 只要是在Android5.0以上的手机 基本必现,你只要在Adapter里面对imageView进行网络请求图片就会出现这种情况
        f49611ea1632:@wuzhen 就是mAdapter.getView一只的回调0,1,2然后又是0,1,2,不过demo中确实不会,具体问题在哪我还没找到 :joy:
        wuzhen:@f49611ea1632 这个还没遇到过,什么情况下会造成死循环呢
      • hackest:居然没人star 这不科学
      • 3452e7a96dbd:😂😂😂
      • 3452e7a96dbd:坐等源码😜😜😜
        wuzhen:@佳佳DR :smile: :smile: 源码地址:https://github.com/wuzhendev/samples/tree/master/EcoGalleryDemo

      本文标题:[Android] Gallery选中变大效果的实现

      本文链接:https://www.haomeiwen.com/subject/biqglttx.html