Android画廊效果-Gallery和ViewPager不同实现

之前项目开发的时候要实现一种画廊效果,分别利用Gallery或ViewPager实现了该效果:


图中圆角图片是自定义View,Gallery和ViewPager实现效果基本一致。

两种方式对比:Gallery原生支持快速滑动,实现起来稍复杂,ViewPager相比Gallery更流畅,可以设置任意页为第一条,实现相对简单。

Gallery实现画廊


自定义Gallery继承安卓Gallery控件,代码比较简单,只是坑比较多。

首先在构造函数里设置转换可用
 public GalleryView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setStaticTransformationsEnabled(true);//设置为true时,每次viewGroup(看Gallery的源码就可以看到它是从ViewGroup
 //接继承过来的)在drawChild的时候都会触发getChildStaticTransformation这个方法
	// this.setSpacing(20);
 }

获取Gallery和child的中心点,用来计算缩放比例

    /**
     * 获取Gallery的中心x
     */
    private int getCenterOfCoverflow() {
        return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();
    }
    /**
     * 获取View的中心x
     */
    private int getCenterOfView(View view) {
        return view.getLeft() + view.getWidth() / 2;
    }

当size改变的时候需要重新获取中心点

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mCoveflowCenter = getCenterOfCoverflow();   // size改变后重新获取中心
        super.onSizeChanged(w, h, oldw, oldh);
    }

设置转换可用后在drawChild的时候会调用getChildStaticTransformation方法,我们可以在getChildStaticTransformation方法里做滑动和缩放

    /**
     * 安卓15以上默认开启了硬件加速,需特殊处理
     */
    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        if (android.os.Build.VERSION.SDK_INT > 15) {
            transformView(child);
        }
        return super.drawChild(canvas, child, drawingTime);
    }
	
	    @Override    protected boolean getChildStaticTransformation(View child, Transformation trans) {        

	        if (android.os.Build.VERSION.SDK_INT < 16)            
                transformView(child);        
            return true;    }
    @SuppressLint("NewApi")
    private void transformView(View child) {
        int childCenter = getCenterOfView(child);
        float scale = (((float) (mCoveflowCenter - childCenter)) / (float) child.getWidth()) * mMaxScale;
        float sxy = 1f - Math.abs(scale);
        child.setPivotY(child.getHeight() / 2);
        if (childCenter > mCoveflowCenter) {
            child.setPivotX(0);
        } else {
            child.setPivotX(child.getWidth());
        }
        child.setScaleX(sxy);
        child.setScaleY(sxy);
    }

这里有个坑就是在系统版本15以上默认开启硬件加速,导致getChildStaticTransformation方法失效,所以需要特殊处理。transformView方法里面对child做缩放处理,获取中心点,计算缩放比例,设置锚点。

适配器关键代码,条目设置LayoutParams

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.widget_gallery_view, null ,false);
            convertView.setLayoutParams(new GalleryView.LayoutParams(getWidth(parent.getContext()),getHeight(parent.getContext())));
        }

        ImageView cardView = (ImageView) convertView.findViewById(R.id.headRIV);
        cardView.setImageResource(position % 2 == 0 ? R.mipmap.a1 : R.mipmap.head);
        return convertView;
    }
宽高是一个写死的比例,使用的时候可以做下改动
    public int getWidth(Context context) {
        if(displayMetrics == null)
        displayMetrics = context.getResources().getDisplayMetrics();

        return displayMetrics.widthPixels * 520 / 640;
    }

    public int getHeight(Context context) {
//        if(displayMetrics == null)
//            displayMetrics = context.getResources().getDisplayMetrics();

        return getWidth(context) * 800 / 520;
    }
这里必须要设置LayoutParams才能显示子View,不是很清楚为什么,有知道的不妨评论告知下。


ViewPager实现画廊效果

ViewPager实现此效果关键属性clipChildren,关于这个属性的介绍可以看 这篇文章
如果你要实现的效果不需要缩放,那么你只需要简单的设置一下属性就可以实现。

1.布局文件中ViewPager和父布局的clipChildren属性设置为false,默认为true

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent" 
    android:clipChildren="false"
    android:layout_height="match_parent" 
    android:weightSum="200"
    android:gravity="center_vertical">

    <com.itheima.galleryview.view.ViewPagerTransform
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginLeft="40dp"
        android:layout_marginRight="40dp"
        android:clipChildren="false"
        android:layout_weight="120">

    </com.itheima.galleryview.view.ViewPagerTransform>
</LinearLayout>

2.你需要给ViewPager的每个页面设置一个间距

        // 设置页面间距
        viewPager.setPageMargin(20);
只需要这两步你就可以实现画廊效果了,那么我要有缩放效果怎么实现呢?

实现切换效果ViewPager有一个方法setPageTransformer,关于PageTransformer参数的介绍可以看这里,在此效果我们准备了一个PageTransformer,可以参考谷歌官方的两个例子

    public class DepthPageTransformer implements ViewPager.PageTransformer {

        @TargetApi(Build.VERSION_CODES.HONEYCOMB)
        @SuppressLint("NewApi")
        public void transformPage(View view, float position) {

            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setScaleY(MIN_SCALE);
                view.setAlpha(MIN_ALPHA);
            } else if (position <= 0) { // [-1,0]
                // Use the default slide transition when moving to the left page
                float scaleFactor = MIN_SCALE + (1 - MIN_SCALE)* (1 - Math.abs(position));
                float alphaFactor = MIN_ALPHA + (1 - MIN_ALPHA)* (1 - Math.abs(position));
                view.setScaleY(scaleFactor);
                view.setAlpha(alphaFactor);
            } else if (position <= 1) { // (0,1]
                // Fade the page out.
                // Counteract the default slide transition

                // Scale the page down (between MIN_SCALE and 1)
                float scaleFactor = MIN_SCALE + (1 - MIN_SCALE)* (1 - Math.abs(position));
                float alphaFactor = MIN_ALPHA + (1 - MIN_ALPHA)* (1 - Math.abs(position));
                view.setScaleY(scaleFactor);
                view.setAlpha(alphaFactor);
            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setScaleY(MIN_SCALE);
                view.setAlpha(MIN_ALPHA);
            }
        }
    }

给ViewPager设置PageTransformer

this.setPageTransformer(false, new DepthPageTransformer());
至此我们实现了带缩放的画廊效果,不过到这里有一个bug,在PageTransformer 中我们能操作的child只有两个,然而我们的效果却要求我们操作三个,这里bug截图,有兴趣的可以演示一下

这里解决问题开始想到在页面切换的时候判断左滑或者右滑来获取并缩放View,但实现起来有点麻烦,后来想到OnHierarchyChangeListener,通过监听ViewPager的添加和移除child来设置,代码如下
        this.setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
            @Override
            public void onChildViewAdded(View parent, View child) {
                child.setScaleY(MIN_SCALE);
                child.setAlpha(MIN_ALPHA);
            }

            @Override
            public void onChildViewRemoved(View parent, View child) {

            }
        });
如此可以解决上面的bug,但是在滑动的时候总是有些卡顿,由于ViewPager的预加载导致下一个child显示的时候总是卡顿一下,无奈把ViewPager的预加载设置为了2
   this.setOffscreenPageLimit(2);  // 避免卡顿

有些东西抽取自定义属性会比较好,有时间再完善。

后面无意间发现设置预加载2之后,上面的监听代码注释掉一样可以实现效果,如果哪位有更好的办法,欢迎告知。

完整代码Github地址: https://github.com/liusai-90/GalleryView



  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值