首先放效果图,此次的需求是希望一共3张图片,中间突出显示,左右两侧随着滑动放大缩小,并且点击左右两侧可以切换图片.这样的效果其实是挺少见的,本人在网上找的大部分是用recyclerview实现的但是左右两侧都是被盖住的,如果加上左右间距之后会在滑动时有各种各样的问题,所以没有办法就试着自己做了一个.
说一下思路,首先是使用的viewpaegr实现的切换效果,并没有使用recyclerview,此处代码是抄袭网上郭神的代码,没有传送门.之后开始分析,首先使用viewpager实现的大部分都是轮播图,我们都知道实现这样的效果都是设置了ClipChildren属性为false使得view能够超出布局显示(这也表示了view绘制时是没有边界的,限制view显示的是Layout||viewGroup),所以在左右两侧显示的图片是无法处理事件分发的.所以无论是在viewpager外滑动还是点击都会无效,所以需要在外层布局中将左右两侧的滑动事件也分发给viewpager处理,点击事件我们自己处理.
还有一个点就是我们只需要显示3个图片,并且我希望可以自定义左右两侧和中间图片的大小,所以外层viewgroup的宽度高度都需要我们自己计算,这个计算也不是很复杂,代码注释也很详细了(自认为),就不多做赘述,代码见.
public class AnimatBanner extends ViewGroup {
private List urls = new ArrayList<>();
private ViewPager bannerPager;
private int deplayedTime = 3000;
private boolean isAutoPlay = true;
private boolean clickSwitchable = true;
private int imgWidth = dip2px(76);//图片宽度 默认为76dp
private int imgHeight = dip2px(76);//图片宽度 默认为76dp
private int pageMargin = dip2px(15);//图片之间的间距 默认为15dp
private float scalingRatio = 0.65f;
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public int dip2px(float dpValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public AnimatBanner(Context context) {
this(context, null);
}
public AnimatBanner(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AnimatBanner(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setClipChildren(false);
}
/**
* 设置是否自动轮播 在setImagesToBanner前调用
*
* @param isAutoPlay
*/
public void setAutoPlay(boolean isAutoPlay) {
this.isAutoPlay = isAutoPlay;
}
/**
* 设置居中显示的图片的大小 单位为dp 默认为76dp
*
* @param imgWidth
* @param imgHeight
*/
public void setImgSize(int imgWidth, int imgHeight) {
this.imgWidth = dip2px(imgWidth);
this.imgHeight = dip2px(imgHeight);
}
/**
* 设置图片之间的间距 单位为dp 默认为15dp
*
* @param pageMargin
*/
public void setPageMargin(int pageMargin) {
this.pageMargin = dip2px(pageMargin);
}
/**
* 设置是否可以点击切换 默认为可以切换
* 此方法可在任意时间调用
*/
public void setClickSwitchable(boolean clickSwitchable) {
this.clickSwitchable = clickSwitchable;
}
/**
* 设置缩放比例
* @param scalingRatio
*/
public void setScalingRatio(float scalingRatio){
this.scalingRatio = scalingRatio;
}
/**
* 开始轮播
*/
public void startPlay() {
if (handler != null && isAutoPlay && urls != null && urls.size() > 0) {
handler.removeMessages(1);
handler.sendEmptyMessageDelayed(1, deplayedTime);
}
}
/**
* 停止轮播
*/
public void onPause() {
if (handler != null) {
handler.removeMessages(1);
}
}
/**
* 销毁handler 防止内存泄露
*/
public void onDestroy() {
if (handler != null) {
handler.removeMessages(1);
handler = null;
}
}
/**
* 传入图片
*
* @param urls
*/
public void setImagesToBanner(final List urls) {
removeAllViews();
this.urls = urls;
bannerPager = new ViewPager(getContext());
bannerPager.setPageMargin(pageMargin);//图片之间的间距
bannerPager.setOffscreenPageLimit((urls.size() >= 3 ? 3 : urls.size()) + 3);//解决滑动很多个条目式因预加载的数量不够而显示空白的问题
bannerPager.setPageTransformer(true, new ScaleInTransformer(scalingRatio));//设置viewpager滑动样式 scalingRatio为缩放比例 根据蓝湖上标注计算得出
bannerPager.setAdapter(new BannerPagerAdapter());
bannerPager.setCurrentItem(urls.size() * 100);//无限轮播
bannerPager.setClipChildren(false);
bannerPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
startPlay();
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
addView(bannerPager);
startPlay();
}
/**
* 计算父布局宽度 使得刚好展示出左右两侧图片
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = (int) (bannerPager.getMeasuredWidth() + (imgWidth * scalingRatio) * 2 + pageMargin * 2);
setMeasuredDimension(width, imgHeight);
}
/**
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View view = getChildAt(0);//拿到唯一的一个viewpager
int left = (int) ((imgWidth * scalingRatio) + pageMargin);
int top = 0;
int right = (int) (((imgWidth * scalingRatio) + pageMargin) + view.getMeasuredWidth());
int bottom = imgHeight;
view.layout(left, top, right, bottom);//将viewpager绘制到父布局中
}
private boolean clickable = false;
private int clickPosition = -1;
//当点击的位置不在viewpager上时 事件才会返回到该布局上 再将其分发到viewpager上 嘤嘤嘤
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
clickable = true;
if (event.getX() > bannerPager.getX()) {//判断按下的位置是在viewpager左边还是右边 1:右 0:左
clickPosition = 1;
} else {
clickPosition = 0;
}
Log.e("test", clickPosition + "");
break;
case MotionEvent.ACTION_MOVE://如果移动那么点击事件不生效 clickable为false
clickable = false;
break;
case MotionEvent.ACTION_UP://没有移动 点击事件生效 根据位置切换viewpager
if (clickSwitchable && clickable && clickPosition != -1) {
if (clickPosition == 0) {//点击viewpager左侧
bannerPager.setCurrentItem(bannerPager.getCurrentItem() - 1);
} else {//点击viewpager右侧
bannerPager.setCurrentItem(bannerPager.getCurrentItem() + 1);
}
}
clickable = false;
break;
}
return bannerPager.onTouchEvent(event);//事件最终全部分发到viewpager上
}
class BannerPagerAdapter extends PagerAdapter {
@Override
public Object instantiateItem(ViewGroup container, final int position) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(imgWidth,
imgHeight);//设置图片大小也相当于设置viewpager的大小
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);//viewapger居中显示
container.setLayoutParams(layoutParams);//container为布局容器 需要手动设置container大小并将其居中
ImageView imageView = new ImageView(getContext());//创建需要加载的图片
imageView.setLayoutParams(layoutParams);//设置布局方式 可有可无
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//设置图片缩放模式
RequestOptions circleOptions = new RequestOptions()
.circleCrop();//设置圆角
Glide.with(getContext())
.load(urls.get(position % urls.size()))
.apply(circleOptions)
.into(imageView);
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object o) {
return view == o;
}
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
bannerPager.setCurrentItem(bannerPager.getCurrentItem() + 1);
}
}
};
}
ScaleInTransformer类
public class ScaleInTransformer extends BasePageTransformer {
private static final float DEFAULT_MIN_SCALE = 0.65f;
private float mMinScale = DEFAULT_MIN_SCALE;
public ScaleInTransformer() {
}
public ScaleInTransformer(float minScale) {
this(minScale, NonPageTransformer.INSTANCE);
}
public ScaleInTransformer(ViewPager.PageTransformer pageTransformer) {
this(DEFAULT_MIN_SCALE, pageTransformer);
}
public ScaleInTransformer(float minScale, ViewPager.PageTransformer pageTransformer) {
mMinScale = minScale;
mPageTransformer = pageTransformer;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void pageTransform(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
view.setPivotY(pageHeight / 2);
view.setPivotX(pageWidth / 2);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setScaleX(mMinScale);
view.setScaleY(mMinScale);
view.setPivotX(pageWidth);
} else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
if (position < 0) { //1-2:1[0,-1] ;2-1:1[-1,0]
float scaleFactor = (1 + position) * (1 - mMinScale) + mMinScale;
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
view.setPivotX(pageWidth * (DEFAULT_CENTER + (DEFAULT_CENTER * -position)));
} else { //1-2:2[1,0] ;2-1:2[0,1]
float scaleFactor = (1 - position) * (1 - mMinScale) + mMinScale;
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
view.setPivotX(pageWidth * ((1 - position) * DEFAULT_CENTER));
}
} else { // (1,+Infinity]
view.setPivotX(0);
view.setScaleX(mMinScale);
view.setScaleY(mMinScale);
}
}
}
NonPageTransformer类
public class NonPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position)
{
page.setScaleX(0.999f);//hack
}
public static final ViewPager.PageTransformer INSTANCE = new NonPageTransformer();
}
BasePageTransformer类
public abstract class BasePageTransformer implements ViewPager.PageTransformer {
protected ViewPager.PageTransformer mPageTransformer = NonPageTransformer.INSTANCE;
public static final float DEFAULT_CENTER = 0.5f;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void transformPage(View view, float position) {
if (mPageTransformer != null)
{
mPageTransformer.transformPage(view, position);
}
pageTransform(view, position);
}
protected abstract void pageTransform(View view, float position);
}
使用方法我相信大家仔细看代码之后是可以理解的,手动狗头..