一、项目概述
这里,我们使用自定义组合控件实现一个自动轮播的广告条,也叫轮播图,完整版的效果图如下图所示。其实,这就是我们经常见到的滚动广告,默认情况下每隔N 秒会自动滚动,用手指左右滑动时也会切换到上一张或者下一张。当界面切换时,对应广告图片的标题也会随着改变,并且还有对应图片索引的点也会被选中变为红色。此处,实现的核心控件是ViewPager,它是Android3.0 版本加入的新控件,为了向下兼容,谷歌给我们提供了android-support-v4.jar 包。
二、轮播图UI布局
布局整体采用RelativeLayout,android.support.v4.view.ViewPager,TextView,LinearLayout 配合使用布局文件activity_main.xml 的代码如下文件所示:
轮播图的代码逻辑实现
public class BannerActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{
@Bind(R.id.viewpager)
public ViewPager mViewPager;
@Bind(R.id.tvTitle)
public TextView mTextView;
@Bind(R.id.llcontainer)
public LinearLayout mContainer;
@Bind(R.id.content)
public LinearLayout mContent;
@Bind(R.id.recyclerview)
RecyclerView mRecyclerView;
private int mPreviousPos;
private int[] imgs = new int[]{R.mipmap.a, R.mipmap.b, R.mipmap.c, R.mipmap.d, R.mipmap.e};
private String[] title;
private ArrayList transformerList = new ArrayList<>();
private RvAdapter mRvAdapter;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
int currentItem = mViewPager.getCurrentItem();
mViewPager.setCurrentItem(++currentItem);// 跳到下一个页面
//// 继续发送延时5秒的消息, 形成类似递归的效果, 使广告一直循环切换
mHandler.sendEmptyMessageDelayed(0, 5000);
}
};
private ViewPagerScroller mScroller;
private ViewPagerScroller scroller;
private ActionBar mActionBar;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
initRecyclerView();
initListener();
initTransformer();
initViewPagerScroll();
}
private void initView() {
setContentView(R.layout.activity_banner);
ButterKnife.bind(this);
title = UIUtil.getStringArray(R.array.title);
SpannableString actionBarTitle = new SpannableString("大图轮播");
actionBarTitle.setSpan(new ForegroundColorSpan(Color.WHITE), 0, actionBarTitle.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
mActionBar = getSupportActionBar();
mActionBar.setTitle(actionBarTitle);
mViewPager.setAdapter(new Adapter());
int middle = Integer.MAX_VALUE / 2;
int extra = middle % imgs.length;
int currentItem = middle - extra;
mViewPager.setCurrentItem(currentItem);
mHandler.sendEmptyMessageDelayed(0, 5000);
for (int i = 0; i < imgs.length; i++) {
ImageView img = new ImageView(this);
img.setImageResource(R.drawable.point_selector);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout
.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
if (i != 0) {
params.leftMargin = 10;
img.setEnabled(false);
}
img.setLayoutParams(params);
mContainer.addView(img);
}
mTextView.setText(title[0]);
mViewPager.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver
.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
BannerActivity.this.onPageSelected(0);
mViewPager.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}
private void initRecyclerView() {
LinearLayoutManager manager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(manager);
mRvAdapter = new RvAdapter(this, R.layout.list_item, transformerList);
mRecyclerView.setAdapter(mRvAdapter);
}
private void initListener() {
// 设置滑动监听,根据页面切换时的回调方法,实时获取当前所在页数,并更新下方对应位置的红色的点
mViewPager.addOnPageChangeListener(this);
/*new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int
positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
int pos = position % imgs.length;
mTextView.setText(title[pos]);
mContainer.getChildAt(pos).setEnabled(true);
mContainer.getChildAt(mPreviousPos).setEnabled(false);
mPreviousPos = pos;
colorChange(pos);
}
@Override
public void onPageScrollStateChanged(int state) {
}
}*/
//设置ViewPager 的触摸侦听,当用户按下时广告条停止轮播,抬起时又开始轮播
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 清除所有消息和Runnable 对象
mHandler.removeCallbacksAndMessages(null);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
// 继续轮播广告
mHandler.sendEmptyMessageDelayed(0, 5000);
break;
}
return false; // 返回false, 让viewpager 原生触摸效果正常运行
}
});
mRvAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(ViewGroup parent, View view, Object o, int position) {
String transforemerName = transformerList.get(position);
try {
Class clazz = Class.forName("com.ToxicBakery.viewpager.transforms." +
transforemerName);
ABaseTransformer transformer = (ABaseTransformer) clazz.newInstance();
mViewPager.setPageTransformer(true, transformer);
if (transforemerName.equals("StackTransformer")) {
scroller.setScrollDuration(1200);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public boolean onItemLongClick(ViewGroup parent, View view, Object o, int position) {
return false;
}
});
}
/**
* 设置ViewPager的滑动速度
*/
private void initViewPagerScroll() {
Field mScroller = null;
try {
mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
scroller = new ViewPagerScroller(mViewPager.getContext());
mScroller.set(mViewPager, scroller);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void initTransformer() {
//各种翻页效果
transformerList.add(DefaultTransformer.class.getSimpleName());
transformerList.add(AccordionTransformer.class.getSimpleName());
transformerList.add(BackgroundToForegroundTransformer.class.getSimpleName());
transformerList.add(CubeInTransformer.class.getSimpleName());
transformerList.add(CubeOutTransformer.class.getSimpleName());
transformerList.add(DepthPageTransformer.class.getSimpleName());
transformerList.add(FlipHorizontalTransformer.class.getSimpleName());
transformerList.add(FlipVerticalTransformer.class.getSimpleName());
transformerList.add(ForegroundToBackgroundTransformer.class.getSimpleName());
transformerList.add(RotateDownTransformer.class.getSimpleName());
transformerList.add(RotateUpTransformer.class.getSimpleName());
transformerList.add(StackTransformer.class.getSimpleName());
transformerList.add(ZoomInTransformer.class.getSimpleName());
transformerList.add(ZoomOutTranformer.class.getSimpleName());
mRvAdapter.notifyDataSetChanged();
}
private void colorChange(int pos) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), imgs[pos]);
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
Palette.Swatch vibrant = palette.getVibrantSwatch();
mContent.setBackgroundColor(toARGB(vibrant.getRgb()));
mTextView.setTextColor(vibrant.getBodyTextColor());
mActionBar.setBackgroundDrawable(new ColorDrawable(vibrant.getRgb()));
if (Build.VERSION.SDK_INT >=21){
Window window = getWindow();
window.setStatusBarColor(colorBurn(vibrant.getRgb()));
//window.setNavigationBarColor(Color.RED);
}
}
});
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
int pos = position % imgs.length;
mTextView.setText(title[pos]);
mContainer.getChildAt(pos).setEnabled(true);
if (pos != mPreviousPos){
mContainer.getChildAt(mPreviousPos).setEnabled(false);
}
mPreviousPos = pos;
colorChange(pos);
}
/**
* 颜色添加透明度
* @param rgb
* @return
*/
protected static int toARGB(int rgb){
int red = rgb >> 16 & 0xFF;
int green = rgb >> 8 & 0xFF;
int blue = rgb & 0xFF;
return Color.argb(180 , red , green , blue );
}
/**
* 颜色加深处理
* @param RGBValues
* @return
*/
private int colorBurn(int RGBValues) {
int alpha = RGBValues >> 24;
int red = RGBValues >> 16 & 0xFF;
int green = RGBValues >> 8 & 0xFF;
int blue = RGBValues & 0xFF;
red = (int) Math.floor(red * (1 - 0.1));
green = (int) Math.floor(green * (1 - 0.1));
blue = (int) Math.floor(blue * (1 - 0.1));
return Color.rgb(red, green, blue);
}
@Override
public void onPageScrollStateChanged(int state) {
}
private class Adapter extends PagerAdapter {
@Override
public int getCount() {
if (imgs.length != 0) {
return Integer.MAX_VALUE;
}
return 0;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
int pos = position % imgs.length;
ImageView img = new ImageView(BannerActivity.this);
img.setBackgroundResource(imgs[pos]);
container.addView(img);
return img;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
private class RvAdapter extends CommonAdapter {
public RvAdapter(Context context, int layoutId, List datas) {
super(context, layoutId, datas);
}
@Override
public void convert(ViewHolder holder, String s) {
holder.setText(R.id.tv_item, s);
}
}
}
知识点总结
1.ViewPager 的使用方式与ListView 类似,都是通过适配器来设置要显示的内容,ViewPager 使用PagerAdapter,ListView 使用BaseAdapter。PagerAdapter 中的各个方法使用见MainActivity.java 中90 到122 行。
2.动态添加图片描述和切换的点,点的个数是随着广告条设置的图片张数动态改变的,因此在MainActivity.java 中59 行,创建ViewPager 的图片内容时,同时创建点并添加到我们存放点的水平的LinearLayout 中(MainActivity.java 第68 行)。
3.广告条伪无限循环实现,viewpager 会最多预加载3 张图片,即当前界面图片、前一张和后一张的图片,当索引为0 时,不加载左边的图片,不能向左滑动,当索引为getcount -1,不加载右边的图片,不能向右滑动,我们设置了长度为Integer.MAX_VALUE,在根据position 获取值时,通过取模运算,保证数据集合脚标不越界。并且在初始时,通过mViewPager.setCurrentItem(mImageIds.length * 1000)方法,让ViewPager 显示到一个较大索引值的位置,这样就实现了伪无限循环。
4 . 代码:https://github.com/JackChen1999/Banner
开源项目
Android-ConvenientBanner
DecentBanner
AndroidImageSlider
https://github.com/JackChen1999/Banner