Android画廊效果
1.概述
最近项目中需要类似上图这种可滚动、点击选中居中的画廊效果,于是萌生了一些想法.最开始想到的这种横向滚动的效果可以用HorizontalScrollView来实现(需要考虑item复用);当然也可以使ViewPager,这需要解决一屏多显和点击事件的问题;再有,就是通过RecyclerView线性横向布局来实现,但是需要处理滚动选中.暂时,就这三种方式了,无一例外,它们都需要选中后居中放大,更甚者,需要添加图片的倒影效果.
2.ViewPager和RecyclerView实现
2.1 ViewPager实现方式
原理就一个属性android:clipChildren="false"
,该属性的意思就是在子View进行绘制时不要去裁切它们的显示范围;在布局文件中
<RelativeLayout android:layout_width="match_parent" android:layout_height="160dp" android:clipChildren="false" android:layout_centerInParent="true" android:background="#aadc71ff" > <android.support.v4.view.ViewPager android:id="@+id/id_viewpager" android:layout_width="match_parent" android:layout_marginLeft="60dp" android:layout_marginRight="60dp" android:clipChildren="false" android:layout_height="120dp" android:layout_gravity="center" /> </RelativeLayout>
给ViewPager及其父
控件都设置了android:clipChildren="false"
。
ViewPager的宽度是match_parent
,左后个设置了60dp
的边距,就是为了显示出左右部分的Page.
在Adapter中
//设置Page间间距
mViewPager.setPageMargin(20);
以及
//设置缓存的页面数量
mViewPager.setOffscreenPageLimit(3);
这样,一屏多显就搞定了;接下来就是设置滚动切换动画了.思路不外乎:写个类集成ViewPager.PageTransformer;重写transformPage方法.上代码:
public class ScaleAlphaPageTransformer implements ViewPager.PageTransformer{ public static final float MAX_SCALE = 1.0f; public static final float MIN_SCALE = 0.6f; public static final float MAX_ALPHA = 1.0f; public static final float MIN_ALPHA = 0.5f; private boolean alpha = true; private boolean scale = true; @Override public void transformPage(View page, float position) { if (position < -1) { position = -1; } else if (position > 1) { position = 1; } float tempScale = position < 0 ? 1 + position : 1 - position; if(scale){ float slope = (MAX_SCALE - MIN_SCALE) / 1; //一个公式 float scaleValue = MIN_SCALE + tempScale * slope; page.setScaleX(scaleValue); page.setScaleY(scaleValue); } if(alpha){ //模糊 float alope = (MAX_ALPHA - MIN_ALPHA) / 1; float alphaValue = MIN_ALPHA + tempScale * alope; page.setAlpha(alphaValue); } } /*** * 设置是否模糊和改变大小 * @param alpha * @param scale */ public void setType(boolean alpha, boolean scale){ this.alpha = alpha; this.scale = scale; } }
于是在Activity中,有
ScaleAlphaPageTransformer mScaleAlphaPageTransformer = new ScaleAlphaPageTransformer(); mScaleAlphaPageTransformer.setType(true, true); viewPager.setPageTransformer(true, mScaleAlphaPageTransformer); viewPager.setCurrentItem(0); viewPager.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int arg0) { Log.e("----------->选中", "" + arg0); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { if (vpLayout != null) { vpLayout.invalidate(); } } @Override public void onPageScrollStateChanged(int arg0) { } }); vpLayout.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { v.performClick(); return viewPager.dispatchTouchEvent(event); } });
这样的话,切换缩放的动画也解决了;
2.2 RecyclerView实现方式
淘来的一张图,效果不错,要实现这种效果,需要搞定滑动结束后某个图片居中和滚动缩放;分两步走,
①滑动结束后停留在某张图片居中
Support RecyclerView 24.2.0中增加一个非常重要的类SnapHelper
,他的作用是让RecyclerView滑动视图后使停止位置正好停在某页的正中间。使用方式很简单
重点在于new LinearSnapHelper().attachToRecyclerView(recyclerView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
new LinearSnapHelper().attachToRecyclerView(recyclerView);
这样就解决了第一个问题.
②滑动缩放
不难看出,RecyclerView的滑动缩放必须要监听RecyclerView的滑动;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// dx>0则表示右滑, dx<0表示左滑, dy<0表示上滑, dy>0表示下滑
mCurrentItemOffset += dx;
computeCurrentItemPos();
onScrolledChangedCallback();
}
});
mCurrentItemOffset为滑动总距离,Card每页滑动的距离是固定的,根据这个可以计算出当前显示的位置。缩放看onScrolledChangedCallback
这个函数,有了滑动位置就能实时计算滑动某页的百分比float percent = (float) Math.max(Math.abs(offset) * 1.0 / mOnePageWidth, 0.0001);
得到百分比, 再获取当前位置相邻的视图调用setScaleY
函数实现缩放
/**
* RecyclerView位移事件监听, view大小随位移事件变化
*/
private void onScrolledChangedCallback() {
int offset = mCurrentItemOffset - mCurrentItemPos * mOnePageWidth;
float percent = (float) Math.max(Math.abs(offset) * 1.0 / mOnePageWidth, 0.0001);
View leftView = null;
View currentView;
View rightView = null;
if (mCurrentItemPos > 0) {
leftView = mRecyclerView.getLayoutManager().findViewByPosition(mCurrentItemPos - 1);
}
currentView = mRecyclerView.getLayoutManager().findViewByPosition(mCurrentItemPos);
if (mCurrentItemPos < mRecyclerView.getAdapter().getItemCount() - 1) {
rightView = mRecyclerView.getLayoutManager().findViewByPosition(mCurrentItemPos + 1);
}
if (leftView != null) {
// y = (1 - mScale)x + mScale
leftView.setScaleY((1 - mScale) * percent + mScale);
}
if (currentView != null) {
// y = (mScale - 1)x + 1
currentView.setScaleY((mScale - 1) * percent + 1);
}
if (rightView != null) {
// y = (1 - mScale)x + mScale
rightView.setScaleY((1 - mScale) * percent + mScale);
}
}//
这样缩放也搞定了.