android轮播图变白,Android轮播图效果的各种实现

前言

很多APP的首页通常会有一个带有动画切换的各种轮播图效果,刚好新项目中也要实现轮播图的效果,于是便研究了Android平台下各种轮播效果,网上也有很多实现轮播相关的方案,但是质量参差不齐,为此踩了不少的坑。下面就来关于轮播图实现方面的一些学习心得,希望对大家有所帮助。

**(一)使用ViewPager实现轮播图切换效果 **

4052fcae8b9c

Gif_20170430_114547.gif

**(1)ViewPager切换动画实现原理 **

使用View.PageTransformer实现动画切换效果,ViewPager的动画切换效果都是重写PageTransformer这个类来实现的。PageTransformer方法中就一个transformPage方法,里面有两个参数,viewPager的滑动时的子View,这个很好理解,最难理解的是这个position参数。切换时的动画实现主要靠position来显示。下面来介绍其中含义。

public interface PageTransformer {

void transformPage(View page, float position);

}

ViewPager左右切换时,position的值范围说明:

**(-oo,-1) 相对于左边第一页,其左边的所有页面 **

*** [-1, 0 ) 相对于当前选中页,其左边的第一页**

*** [0, 1 ) 相对于当前选中页,其右边第一页 **

[1,+oo) 相对于右边第一页,其右边的所有页面

下面举例说明:

**当前ViewPage选中的页为,其右边的页面为B,现在向左滑动A,慢慢由页面A切换到页面B **

**页面A的position值是由 0 慢慢减小到 -1 [0,-1] **

**页面B的position值是由 1 慢慢减小到 0 [1,0] **

**此时页面B为ViewPage当前选中的页面 **

**再向右滑动页面B,慢慢由页面B切换到页面A **

**页面A的position值由 -1 慢慢增加到 0 [-1,0] **

页面B的position值由 0 慢慢增加到 1 [ 0,1]

理解了transformPage方法中position的含义,那么ViewPager动画切换效果的实现就很好理解了。下面是项目中我实现的一个ViewPager切换效果,如果所示:

代码:

public class ZoomPageTransformer implements ViewPager.PageTransformer {

private static final float MAX_SCALE = 1.0f;

private static final float MIN_SCALE = 0.85f;//0.85f

private static final float MIN_ALPHA = 0.3f;

private static final String TAG = "PageTransformer";

@Override

public void transformPage(View view, float position) {

//setScaleY只支持api11以上

if (position < -1) {

view.setScaleX(MIN_SCALE);

view.setScaleY(MIN_SCALE);

view.setAlpha(MIN_ALPHA);//左边的左边的Page

} else if (position <= 1) {

float scaleFactor = MIN_SCALE + (1 - Math.abs(position)) * (MAX_SCALE - MIN_SCALE);

if (position > 0) {

view.setTranslationX(-scaleFactor);

} else if (position < 0) {

view.setTranslationX(scaleFactor);

}

view.setScaleY(scaleFactor);

view.setScaleX(scaleFactor);

// float alpha = 1f - Math.abs(position) * (1 - );

float alpha = MIN_ALPHA + (1 - MIN_ALPHA) * (1 - Math.abs(position));

view.setAlpha(alpha);

Log.i(TAG,"position = " + position + " alpha = " + alpha);

} else { // (1,+Infinity]

view.setScaleX(MIN_SCALE);

view.setScaleY(MIN_SCALE);

view.setAlpha(MIN_ALPHA);

}

}

}

当position的范围在(-oo,-1) 和[1,+oo)时,view的比例缩小MIN_SCALE(0.85f),透明度缩小到 MIN_ALPHA(0.3f)

当position的范围在 [-1,1]的时候,View的scale值和position绝对值成反比

float scaleFactor = MIN_SCALE + (1 - Math.abs(position)) * (MAX_SCALE - MIN_SCALE);

if (position > 0) {

view.setTranslationX(-scaleFactor);

} else if (position < 0) {

view.setTranslationX(scaleFactor);

}

view.setScaleY(scaleFactor);

view.setScaleX(scaleFactor);

当position的值等于-1或者1的时候,此时View就是左边第一页和右边第一页,此时scale值就是MIN_SCALE(0.85f)

当position的值范围在[-1,0]时,scale的值慢慢由MIN_SCALE变大到MAX_SCALE,也就是说ViewPager右滑动时,左边第一个View是慢慢放大的,直到其放大到MAX_SCALE。

当position的值范围在(0,1]时,scale的值由MAX_SCALE变小到MIN_SCALE,也就是说ViewPager右滑动时,右边第一个View是慢慢缩小的,直到其比例缩放到MIN_SCALE。

透明度变化也是同样一个原理。

**(2)让ViewPager显示多页 **

一般情况下,ViewPager只能显示一页,那如何让其显示多个子页面呢?那就不得不说setClipChildren(false)这个方法了。

默认情况下为setClipChildren(true),如果子View的布局范围超过了父View,那么它的边界将会被裁减掉,也就是说超过父View的部分是看不到的。

当setClipChildren(false)的情况下,子View的布局范围超过了父View部分将不会被裁减掉 ,而将会以动画的形式显示出来。

android:id="@+id/pager_container"

android:layout_width="match_parent"

android:layout_height="220dp"

android:clipChildren="false"

android:background="?attr/colorPrimary">

android:id="@+id/overlap_pager"

android:layout_width="300dp"

android:layout_height="200dp"

android:layout_gravity="center" />

Layout.xml

android:layout_width="match_parent"

android:layout_height="@dimen/recommend_banner_height"

android:clipChildren="false">

android:id="@+id/fragment_recommend_viewPager"

android:layout_width="@dimen/recommend_banner_image_width"

android:layout_height="@dimen/recommend_banner_image_height"

android:layout_centerHorizontal="true"

android:clipChildren="false" />

android:id="@+id/fragment_recommend_banner_indicator"

android:layout_alignParentBottom="true"

android:layout_width="match_parent"

android:layout_height="25dp"

android:gravity="center_horizontal"

android:orientation="horizontal" />

JAVA代码

/**

* Created by liuyu on 2017/4/17.

* 推荐页轮播图控件

*/

public class BannerView extends RelativeLayout {

private static final String TAG = "BannerView";

private LinearLayout mBannerIndicator;

private ViewPager mViewPager;

private long VIEWPAGER_SWITCH_DURING = 8000;//轮播时间

private int MSG_START_SCROLL = 100;//消息的名称

private int mPointRadius;

private int mPointTotalCount;//小圆点真正个数,有可能接口返回数据记录 < DEFAULT_POINT_COUNT

private Drawable mNormalColor, mSelectedColor;

private BannerAdapter mBannerAdapter;

private int mPointMarginTop;

public BannerView(Context context) {

super(context);

initBanner();

}

public BannerView(Context context, AttributeSet attrs) {

super(context, attrs);

initBanner();

}

private void initBanner() {

this.setClipChildren(false);

mPointRadius = getContext().getResources().getDimensionPixelSize(R.dimen.recommend_banner_point_radius);

mPointMarginTop = getContext().getResources().getDimensionPixelSize(R.dimen.recommend_banner_point_margin_top);

mNormalColor = getResources().getDrawable(R.drawable.bg_shape_recommend_banner_point_normal);

mSelectedColor = getResources().getDrawable(R.drawable.bg_shape_recommend_banner_point_selected);

}

public BannerView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

initBanner();

}

private Handler mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

if (msg.what == MSG_START_SCROLL) {

if (mHandler != null) {

mHandler.removeMessages(MSG_START_SCROLL);

mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);

mHandler.sendEmptyMessageDelayed(MSG_START_SCROLL, VIEWPAGER_SWITCH_DURING);

}

}

}

};

@Override

protected void onFinishInflate() {

super.onFinishInflate();

mBannerIndicator = (LinearLayout) findViewById(R.id.fragment_recommend_banner_indicator);

mViewPager = (ViewPager) findViewById(R.id.fragment_recommend_viewPager);

}

/**

* 添加小圆点

*/

private void addDots(int count) {

mPointTotalCount = count;

// mBannerIndicator.removeAllViews();

Logger.i(TAG, "addDots count = " + count);

//加点

for (int i = 0; i < count; i++) {

ImageView pointView = new ImageView(getContext());

pointView.setImageDrawable(getResources().getDrawable(R.drawable.bg_shape_recommend_banner_point_normal));

//点的大小

LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(mPointRadius * 2, mPointRadius * 2);

//点的间隔

layoutParams.leftMargin = mPointRadius * 2;

//居中显示

layoutParams.topMargin = mPointMarginTop;

Logger.i(TAG, "addDots index = " + i);

pointView.setLayoutParams(layoutParams);

//把点添加到容器中

mBannerIndicator.addView(pointView);

}

}

/**

* @param dataList

*/

public void show(List dataList) {

Logger.i(TAG, "start show banner");

if (dataList == null || dataList.size() == 0) {

return;

}

/* if (mViewPager.getChildCount() > 0 && mBannerIndicator.getChildCount() > 0) {

mBannerAdapter.notifyDataSetChanged();

}*/

//重置数据

if (mViewPager.getChildCount() > 0) {

mViewPager.removeAllViews();

}

if (mBannerIndicator.getChildCount() > 0) {

mBannerIndicator.removeAllViews();

}

addDots(dataList.size());

/**** 重要部分 ******/

//clipChild用来定义他的子控件是否要在他应有的边界内进行绘制。 默认情况下,clipChild被设置为true。 也就是不允许进行扩展绘制。

mViewPager.setClipChildren(false);

//父容器一定要设置这个,否则看不出效果

//mViewPager.setPageMargin(getResources().getDimensionPixelOffset(R.dimen.recommend_banner_image_space));

mViewPager.setOffscreenPageLimit(5);

final List imageList = new ArrayList<>();

//add image url

mBannerAdapter = new BannerAdapter(getContext(), dataList);

mBannerAdapter.setImageList(imageList);

mViewPager.setAdapter(mBannerAdapter);

int currentItem = Integer.MAX_VALUE / 2;

mViewPager.setCurrentItem(currentItem);

changeIndicatorStatus(currentItem);

//设置ViewPager切换效果,即实现画廊效果

mViewPager.setPageTransformer(true, new ZoomPageTransformer());

//将容器的触摸事件反馈给ViewPager

this.setOnTouchListener(new OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

// dispatch the events to the ViewPager, to solve the problem that we can swipe only the middle view.

return mViewPager.dispatchTouchEvent(event);

}

});

//图片数量大于1的时候,才进行自动轮播

if (dataList.size() > 1) {

startAutoScroll();

}

mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

@Override

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override

public void onPageSelected(int position) {

changeIndicatorStatus(position);

}

@Override

public void onPageScrollStateChanged(int state) {

}

});

}

/**

* 开始自动切换

*/

public void startAutoScroll() {

if (mHandler != null) {

mHandler.removeMessages(MSG_START_SCROLL);

mHandler.sendEmptyMessageDelayed(MSG_START_SCROLL, VIEWPAGER_SWITCH_DURING);

}

}

/**

* 停止自动切换

*/

public void stopScroll() {

mHandler.removeMessages(MSG_START_SCROLL);

}

public void changeIndicatorStatus(int position) {

if (position == 0) {

return;

}

int realPos = position % mPointTotalCount;

for (int i = 0; i < mPointTotalCount; i++) {

if (i == realPos) {

((ImageView) mBannerIndicator.getChildAt(i)).setImageDrawable(mSelectedColor);

} else {

((ImageView) mBannerIndicator.getChildAt(i)).setImageDrawable(mNormalColor);

}

}

}

@Override

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

clear();

}

/**

* 数据清理

*/

public void clear() {

if (mHandler != null) {

mHandler.removeCallbacksAndMessages(null);

}

}

}

(二)使用FancyCoverFlow实现轮播图切换效果

(1) FancyCoverFlow的使用

Java代码

this.fancyCoverFlow.setUnselectedAlpha(0.0f);

// 未选中的饱和度

this.fancyCoverFlow.setUnselectedSaturation(0.0f);

// 未选中的比例

this.fancyCoverFlow.setUnselectedScale(0.8f);

// child间距

this.fancyCoverFlow.setSpacing(-60);

// 旋转度数

this.fancyCoverFlow.setMaxRotation(0);

// 非选中的重心偏移,负的向上

this.fancyCoverFlow.setScaleDownGravity(-1f);

// 作用距离

this.fancyCoverFlow.setActionDistance(FancyCoverFlow.ACTION_DISTANCE_AUTO);

XML布局文件

android:layout_width="match_parent"

android:layout_height="match_parent"

fcf:maxRotation="45"

fcf:unselectedAlpha="0.3"

fcf:unselectedSaturation="0.0"

fcf:unselectedScale="0.4" />

(2) 实现无限循环效果

FancyCoverFlow是通过继承Galley实现的,那么我们可以利用Galley的setSelection()方法实现一些特殊的效果,例如打造无限循环的录播图。

FancyCoverFlowSampleAdapter.java

public class FancyCoverFlowSampleAdapter extends FancyCoverFlowAdapter {

// =============================================================================

// Private members

// =============================================================================

private int[] images = {

R.drawable.image1, R.drawable.image2, R.drawable.image3,

R.drawable.image6, R.drawable.image5, R.drawable.image4

};

// =============================================================================

// Supertype overrides

// =============================================================================

@Override

public Integer getItem(int i) {

return images[ i];

}

@Override

public long getItemId(int position) {

return position;

}

@Override

public int getCount() {

return Integer.MAX_VALUE;

}

@Override

public View getCoverFlowItem(int i, View reuseableView, ViewGroup viewGroup) {

ImageView imageView = null;

if (reuseableView != null) {

imageView = (ImageView) reuseableView;

} else {

imageView = new ImageView(viewGroup.getContext());

imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

imageView.setLayoutParams(new FancyCoverFlow.LayoutParams(300, 400));

}

imageView.setImageResource(this.getItem(i % images.length));

return imageView;

}

}

MainActivity.java

this.fancyCoverFlow = (FancyCoverFlow) this.findViewById(R.id.fancyCoverFlow);

this.fancyCoverFlow.setAdapter(new FancyCoverFlowSampleAdapter());

this.fancyCoverFlow.setUnselectedAlpha(1.0f);

this.fancyCoverFlow.setUnselectedSaturation(0.0f);

this.fancyCoverFlow.setUnselectedScale(0.6f);

this.fancyCoverFlow.setSpacing(-20);

this.fancyCoverFlow.setMaxRotation(40);

this.fancyCoverFlow.setScaleDownGravity(0.2f);

this.fancyCoverFlow.setActionDistance(FancyCoverFlow.ACTION_DISTANCE_AUTO);

this.fancyCoverFlow.setSelection(Integer.MAX_VALUE/2);

效果如下:

4052fcae8b9c

Gif_20170514_171512.gif

(三)使用RecyclerCoverFlow实现轮折叠的轮播图效果

效果如下:

4052fcae8b9c

Gif_20170514_172656.gif

** (1) RecyclerCoverFlow的使用**

xml布局

android:id="@+id/list"

android:layout_width="match_parent"

android:layout_height="match_parent">

Activity中引入

mList = (RecyclerCoverFlow) findViewById(R.id.list);

// mList.setFlatFlow(true); //平面滚动

mList.setAdapter(new Adapter(this));

mList.setOnItemSelectedListener(new CoverFlowLayoutManger.OnSelected() {

@Override

public void onItemSelected(int position) {

((TextView)findViewById(R.id.index)).setText((position+1)+"/"+mList.getLayoutManager().getItemCount());

}

});

(2) 实现原理

RecyclerCoverFlow是通过集成RecyclerView实现的,由于默认情况下,ViewGroup中的子view绘制顺序,index越大,其绘制顺序会越靠后,所以后面的子View会遮住前面的子view,导致居中显示的子View右边重叠部分会被靠后的子View遮住。

@Override

protected int getChildDrawingOrder(int childCount, int i) {

int center = getCoverFlowLayout().getCenterPosition()

- getCoverFlowLayout().getFirstVisiblePosition(); //计算正在显示的所有Item的中间位置

if (center < 0) center = 0;

else if (center > childCount) center = childCount;

int order;

if (i == center) {

order = childCount - 1;

} else if (i > center) {

order = center + childCount - 1 - i;

} else {

order = i;

}

return order;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值