需求描述
1.单行横滑海报列表,当前海报居中放大展示。
2.点击左边海报,左边海报居中变大,点击右边海报,右边海报居中变大。
3.点击居中放大海报实现跳转。
4.海报下方标题副标题实现联动效果。
实现效果
实现思路
- ViewPager自带居中属性,实现起来比RecylerView更方便(后续会针对RecylerView实现再写一篇),通过onPageScrolled监听中的positionOffset来实现
/**
* Callback interface for responding to changing state of the selected page.
*/
public interface OnPageChangeListener {
/**
* This method will be invoked when the current page is scrolled, either as part
* of a programmatically initiated smooth scroll or a user initiated touch scroll.
*
* @param position Position index of the first page currently being displayed.
* Page position+1 will be visible if positionOffset is nonzero.
* @param positionOffset Value from [0, 1) indicating the offset from the page at position.
* @param positionOffsetPixels Value in pixels indicating the offset from position.
*/
void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
/**
* This method will be invoked when a new page becomes selected. Animation is not
* necessarily complete.
*
* @param position Position index of the new selected page.
*/
void onPageSelected(int position);
/**
* Called when the scroll state changes. Useful for discovering when the user
* begins dragging, when the pager is automatically settling to the current page,
* or when it is fully stopped/idle.
*
* @param state The new scroll state.
* @see ViewPager#SCROLL_STATE_IDLE
* @see ViewPager#SCROLL_STATE_DRAGGING
* @see ViewPager#SCROLL_STATE_SETTLING
*/
void onPageScrollStateChanged(int state);
}
positionOffset是当前页面滑动比例,如果页面向右翻动,这个值不断变大,最后在趋近1的情况后突变为0。如果页面向左翻动,这个值不断变小,最后变为0。positionOffsetPixels是当前页面滑动像素,变化情况和positionOffset一致。
- 核心思路就是改变左右两端view的scale值。
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// 虽然一屏只显示三页,但是当我们滑动的过程中需要设置四页的缩放效果,因为左边的页面被滑出去之后,页面上将显示的是后面的三页
View leftImage = mClipAdapter.activityMap.get(position - 1);
View currentImage = mClipAdapter.activityMap.get(position);
View rightImage = mClipAdapter.activityMap.get(position + 1);
View rightImage2 = mClipAdapter.activityMap.get(position + 2);
if (positionOffset >= 0) {
float scaleAlpha1 = 1 - positionOffset / 5.0f;
float scaleAlpha2 = mScale + positionOffset / 5.0f;
// 设置页面滑动过程中的缩放效果
currentImage.setAlpha(scaleAlpha1);
currentImage.setScaleX(scaleAlpha1);
currentImage.setScaleY(scaleAlpha1);
isViewInCenter(currentImage);
if (leftImage != null) {
leftImage.setAlpha(scaleAlpha2);
leftImage.setScaleX(scaleAlpha2);
leftImage.setScaleY(scaleAlpha2);
isViewInCenter(leftImage);
}
if (rightImage != null) {
rightImage.setAlpha(scaleAlpha2);
rightImage.setScaleX(scaleAlpha2);
rightImage.setScaleY(scaleAlpha2);
isViewInCenter(rightImage);
}
if (rightImage2 != null) {
rightImage2.setAlpha(scaleAlpha1);
rightImage2.setScaleX(scaleAlpha1);
rightImage2.setScaleY(scaleAlpha1);
isViewInCenter(rightImage2);
}
} else {
// 设置页面初次启动时的缩放效果
if (leftImage != null && currentImage.getAlpha() == 1) {
leftImage.setAlpha(mScale);
leftImage.setScaleX(mScale);
leftImage.setScaleY(mScale);
}
if (rightImage != null && currentImage.getAlpha() == 1) {
rightImage.setAlpha(mScale);
rightImage.setScaleX(mScale);
rightImage.setScaleY(mScale);
}
}
}
@Override
public void onPageSelected(int position) {
setTitle(position);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
float scaleAlpha1 = 1 - positionOffset / 5.0f;这里为什么是除以5.0f?是因为我们要求的scale值是0.8,明白了?哈哈
- 左右海报点击效果解决方案,直接上代码
public class SingleLineZoomViewPager extends ViewPager {
private final int VIEW_MOVE_TAG = 1;
//28:两边间隔10+10 中间间隔4+4
private int width = (UIsUtils.getMinScreen() - UIsUtils.dipToPx(28)) / 3 ;
private int height = width * 4 /3;
private int zoomWidth = width *5/4;
private int zommHeight = height * 5 /4 ;
public SingleLineZoomViewPager(Context context) {
this(context, null);
}
public SingleLineZoomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_UP) {
int[] location = new int[2];
SingleLineZoomViewPager.this.getLocationOnScreen(location);
int minX = location[0];
int minY = location[1];
//得到view的最右侧坐标
int maxX = location[0] + zoomWidth;
int maxY = location[1]+ zommHeight;
// 手指触摸的坐标
float x = ev.getRawX();
float y = ev.getRawY();
int item = getCurrentItem();
if ((x > maxX) && (y > minY && y < maxY)) {
LogInfo.log("abc","click right ---> + "+ item);
((ClipAdapter)getAdapter()).mCanClick = false;
setCurrentItem(item + 1);
} else if ((x < minX) && (y > minY && y < maxY)) {
LogInfo.log("abc","click left ---> "+ item);
((ClipAdapter)getAdapter()).mCanClick = false;
setCurrentItem(item -1);
}else{
((ClipAdapter)getAdapter()).mCanClick = true;
}
}
return super.dispatchTouchEvent(ev);
}
}
float x = ev.getRawX();
float y = ev.getRawY();要用getRawX()和getRawY()注意~
- 居中海报跳转
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mCanClick){
imageView.getTag();
UIControllerUtils.gotoActivity(mContext, bean, PlayUtils.getVideoType(mCid, bean.isPanorama()), mPageId);
}
}
});
通过mCanClick标记来确定
- 4.海报下方标题副标题实现联动效果。这里比较低效的使用一个isViewInCenter函数来判断,并实现联动
public void isViewInCenter(View view){
int[] location = new int[2];
view.getLocationOnScreen(location);
int x = location[0] + zoomWidth/2;
if((x > (center - (zoomWidth/2)) && x < (center + (zoomWidth/2)) )){
Log.e("abc", "view.getTag() "+ view.getTag());
if(view.getTag() != null){
setTitle((int)view.getTag());
}
}
}
至此,所有需要解决问题均解决。