最近公司的项目需要实现类似淘宝、京东首页的广告banner,作为一个专业的“拷贝型”程序猿,在github上搜了一下,还是挺多的,其中还有两个比较好的,扩展性和特效都不错,看得我很是羡慕啊,有兴趣的童鞋请移步:
1.FlycoBanner_Master
2.Android-ConvenientBanner
作为一个长期“拷贝”的程序猿,偶尔还是有点小失落的,偶尔也会有大神梦,即使那么遥不可及。。。
于是乎,在一个月黑风高的夜晚,有史以来最low的广告banner诞生了。
先谈谈整体的思路吧,看到有些大神用AdapterView实现的,而我这种渣,首先想到的就是viewpager,虽然viewpager没有那么完美,不过能勉强将就也还是极好的。
当然只有viewpager是不够的,还有viewpager上面的那几个小点点,这个我就更简单粗暴的用一个Layout装载了结果imageview,然后viewpager改变item的时候,改变小点点的图片就可以啦。
还有一个问题就是循环播放啦,这个就要从viewpager的adapter入手了,上网查了很多,得到的结论就是在adapter的getCount方法上做手脚,即返回一个很大的数,使viewpager的item数量大于你看到的实际数量,而多的item中显示与前面item相同的内容,这样看起来就是循环的啦。
剩下的要解决自动循环就更简单啦,管你用timer还是handler还是thread都可以简单搞定。最后要解决的就是使这个东西用起来比较简单,所以我就简单的对它进行了一层小包装。
主要的视图类
package com.drivingassisstantHouse.library.widget.bannerview;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import com.drivingassisstantHouse.library.R;
import com.drivingassisstantHouse.library.tools.SLog;
import com.drivingassisstantHouse.library.tools.ToolUnit;
import java.lang.reflect.Field;
import java.util.ArrayList;
/**
* 包名:com.drivingassisstantHouse.library.widget.bannerview
* 描述:广告banner
* 创建者:yankebin
* 日期:2016/2/27
*/
public class BannerView extends RelativeLayout {
public static final int BANNER_TYPE_GUIDE = 1;
public static final int BANNER_TYPE_ADVERT = BANNER_TYPE_GUIDE + 1;
protected int type = BANNER_TYPE_GUIDE;
private BaseBannerViewpager viewPager;
private LinearLayout indicator;
private boolean isAutoLoop = false;
private long loopDelayTime = 4000;
private long scrollDuration=1200;
private ArrayList dots = new ArrayList<>();
public BannerView(Context context) {
super(context);
init();
}
public BannerView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.BannerView);
type = typedArray.getInt(R.styleable.BannerView_bannerType, BANNER_TYPE_GUIDE);
isAutoLoop = typedArray.getBoolean(R.styleable.BannerView_autoPlay, false);
loopDelayTime = typedArray.getInt(R.styleable.BannerView_loopTime, 4000);
scrollDuration=typedArray.getInt(R.styleable.BannerView_scrollDuration, 1200);
if (null != typedArray) {
typedArray.recycle();
}
init();
}
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility != VISIBLE) {
removeCallbacks(autoRunner);
} else {
if (isAutoLoop) {
startAutoPlay();
}
}
}
private Runnable autoRunner = new Runnable() {
@Override
public void run() {
if (!isAutoLoop) {
return;
}
int position=viewPager.getCurrentItem();
viewPager.setCurrentItem(++position, true);
postDelayed(this, loopDelayTime);
}
};
private Runnable indicatorRunner=new Runnable() {
@Override
public void run() {
int currentPosition = viewPager.getCurrentItem();
for (int i = 0; i < dots.size(); i++) {
ImageView imageView = dots.get(i);
if (currentPosition % dots.size() == i) {
imageView.setImageResource(R.drawable.banner_dot_shape_selected);
} else {
imageView.setImageResource(R.drawable.banner_dot_shape_normal);
}
}
}
};
public void setIsAutoLoop(boolean isLoop) {
this.isAutoLoop = isLoop;
}
public boolean isAutoLoop() {
return isAutoLoop;
}
public void setBannerAdapter(PagerAdapter adapter) {
viewPager.setAdapter(adapter);
}
private void init() {
viewPager = new BaseBannerViewpager(getContext());
viewPager.setLayoutParams(new LayoutParams(-1, -1));
indicator = new LinearLayout(getContext());
LayoutParams params = new LayoutParams(-2, -2);
params.addRule(ALIGN_PARENT_BOTTOM);
params.addRule(CENTER_HORIZONTAL);
params.bottomMargin = 10;
indicator.setLayoutParams(params);
indicator.setOrientation(LinearLayout.HORIZONTAL);
addView(viewPager);
addView(indicator);
initPagerListener();
}
private void initPagerListener() {
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
changeDotsColors();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void initDots(int size) {
if (size <= 0) {
throw new IllegalArgumentException("size必须大于0");
}
removeCallbacks(autoRunner);
dots.clear();
LinearLayout.LayoutParams pointViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
pointViewParams.setMargins(ToolUnit.dipTopx(8), 0,
ToolUnit.dipTopx(8), ToolUnit.dipTopx(8));
for (int i = 0; i < size; i++) {
ImageView pointView = new ImageView(getContext());
pointView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
pointView.setLayoutParams(pointViewParams);
dots.add(pointView);
indicator.addView(pointView);
}
changeDotsColors();
if (isAutoLoop) {
startAutoPlay();
}
}
private void startAutoPlay() {
removeCallbacks(autoRunner);
postDelayed(autoRunner, loopDelayTime);
}
public void changeDotsColors() {
postDelayed(indicatorRunner,scrollDuration+500);
}
private class BaseBannerViewpager extends ViewPager {
private int childId;
public class FixedSpeedScroller extends Scroller {
private int mDuration = (int)scrollDuration;
public FixedSpeedScroller(Context context) {
super(context);
}
public FixedSpeedScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
// Ignore received duration, use fixed one instead
super.startScroll(startX, startY, dx, dy, mDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
// Ignore received duration, use fixed one instead
super.startScroll(startX, startY, dx, dy, mDuration);
}
public void setmDuration(int time) {
mDuration = time;
}
public int getmDuration() {
return mDuration;
}
}
public BaseBannerViewpager(Context context) {
super(context);
resetScroller(context);
}
public BaseBannerViewpager(Context context, AttributeSet attrs) {
super(context, attrs);
resetScroller(context);
}
private void resetScroller(Context context){
try {
Field field = ViewPager.class.getDeclaredField("mScroller");
field.setAccessible(true);
FixedSpeedScroller scroller = new FixedSpeedScroller(context,
new AccelerateInterpolator());
field.set(this, scroller);
} catch (Exception e) {
SLog.e(e);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (childId > 0) {
ViewPager pager = (ViewPager) findViewById(childId);
if (pager != null) {
pager.requestDisallowInterceptTouchEvent(true);
}
}
return super.onInterceptTouchEvent(event);
}
public void setAllowChildMovement(int id) {
this.childId = id;
}
@Override
public void setAdapter(PagerAdapter adapter) {
initDots(((BaseBannerPagerAdapter)adapter).getRealCount());
super.setAdapter(adapter);
}
}
}
viewpager的adapter
package com.drivingassisstantHouse.library.widget.bannerview;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* 包名:com.simpletour.client.activity
* 描述:banner适配器
* 创建者:yankebin
* 日期:2016/2/27
*/
public abstract class BaseBannerPagerAdapter extends android.support.v4.view.PagerAdapter {
protected SparseArray views = new SparseArray<>();
private boolean isLoop;
protected ArrayList datas;
public BaseBannerPagerAdapter() {
datas = new ArrayList<>();
}
public BaseBannerPagerAdapter(ArrayList datas) {
this.datas = datas;
}
@Override
public final int getCount() {
return isLoop ? Integer.MAX_VALUE : datas.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
position = position % datas.size();
View view = views.get(position);
if (view == null) {
view = newView(position % getCount());
views.put(position, view);
}
container.addView(view);
return view;
}
public void notifyUpdateView(int position) {
View view = updateView(views.get(position), position);
views.put(position, view);
notifyDataSetChanged();
}
public View updateView(View view, int position) {
return view;
}
public abstract View newView(int position);
public final int getRealCount() {
return datas.size();
}
public void subscribe(BannerView bannerView) {
isLoop =bannerView.isAutoLoop()|| bannerView.type == BannerView.BANNER_TYPE_ADVERT;
bannerView.setBannerAdapter(this);
}
}
一些自定义属性
<declare-styleable name="BannerView">
<attr name="autoPlay" format="boolean"/>
<attr name="bannerType" format="integer">
<flag name="typeGuide" value="1"/>
<flag name="typeAdvert" value="2"/>
attr>
<attr name="loopTime" format="integer"/>
<attr name="scrollDuration" format="integer"/>
declare-styleable>
代码里调用
首先定义好xml文件
"http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.drivingassisstantHouse.library.widget.bannerview.BannerView
xmlns:banner="http://schemas.android.com/apk/res-auto"
android:layout_weight="1"
banner:autoPlay="true"
banner:loopTime="3000"
banner:bannerType="typeAdvert"
banner:scrollDuration="800"
android:id="@+id/bannerView"
android:layout_width="match_parent"
android:layout_height="0dp">
com.drivingassisstantHouse.library.widget.bannerview.BannerView>
其次实现BaseBannerPagerAdapter
private class MBannerPagerAdapter extends BaseBannerPagerAdapter {
public MBannerPagerAdapter() {
super(imageUrlList);
initImageLoader();
}
private void initImageLoader() {
}
@Override
public boolean isViewFromObject(View view, Object object) {
return null != object && object == view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(views.get(position % views.size()));
}
@Override
public View newView(final int position) {
ImageView imageView = new ImageView(GuideActivity.this);
imageView.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
ImageLoader.getInstance().displayImage(
datas.get(position),
imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(GuideActivity.this,FloatActivity.class);
intent.putExtra("uri", imageUrlList.get(position));
Intent newIntent=new Intent(GuideActivity.this,PayDemoActivity.class);
intent.putExtra("intent",newIntent);
startActivity(intent);
}
});
return imageView;
}
}
最后
new MBannerPagerAdapter().subscribe(bannerView);
这个效果和淘宝、京东的还有差距:
1.对于用户干预滑动的处理,我所能想到就是在touch事件中控制autorunner的执行,至于实现的细节,我还没有想好怎么去弄得更完美。
2.圆点指示器的实现方式,要是着色的圆点可以随viewpager相同的效果滑动而不是瞬间改变颜色就更好了。这个效果我也在viewpager的listener的onpagescrolled方法里尝试过了,但是当人为干预后,圆点就会错位,目前还没想到办法解决,希望有大神可以指点一二。