Android优秀开源Android开发Android潜修者

自己造轮子--一款实用的Android广告栏实现过程(二)

2016-01-05  本文已影响3621人  dongjunkun

这一篇是接着自己造轮子--一款实用的Android广告栏实现过程(一)写的,没看过的先看上一篇,在上一篇中我提到对轮子的要求,也分析了部分关键实现代码(轮播,修复原生切换速度过快问题,自动切换),今天分析剩余的部分,github地址BannerLayoutDemo

bannerLayoutDemo.gif

添加指示器--绘制指示器样式

github上也有各种各样很棒的指示器,可作为独立控件,这里我先简单处理直接集成到内部,后期有需求再进行重构,最简单的指示器是用两张不同状态的小图片做的,但我认为这样做相对实现是简单的,但对于修改却显得有些麻烦,适配也是问题,简单修改何必大动干戈呢?

这里借鉴了daimajia的思路,指示器是用代码绘出来的,怎么绘呢?看代码,以选中的状态为例,绘制一次便可,将drawable存起来使用:

Drawable selectedDrawable;
GradientDrawable selectedGradientDrawable = new GradientDrawable();
//设置指示器的颜色
selectedGradientDrawable.setColor(selectedIndicatorColor);
//设置指示器的形状
selectedGradientDrawable.setShape(GradientDrawable.RECTANGLE);
//设置指示器的大小
selectedGradientDrawable.setSize(selectedIndicatorWidth, selectedIndicatorHeight);
selectedLayerDrawable = new LayerDrawable(new Drawable[]{selectedGradientDrawable});
selectedDrawable = selectedLayerDrawable;

这样指示器的形状,大小,颜色可以随意换了,支持换肤也更容易

添加指示器--位置

大家都知道在xml文件中使用RelativeLayout父布局可以控制子布局的位置,用代码怎么去做呢?首先,BannerLayout是继承与RelativeLayout的,看代码

  RelativeLayout.LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  switch (indicatorPosition) {
      case centerBottom://下中
          params.addRule(RelativeLayout.CENTER_HORIZONTAL);
          params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
          break;
      case rightBottom://右下
          params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
          params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
          break;
  }
//添加指示器容器布局到BannerLayout
 addView(indicatorContainer, params);

两个属性必须分开添加不能写成

params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT|RelativeLayout.ALIGN_PARENT_BOTTOM);

如何设置指示器margin和padding这里就不再多说

切换指示器

我之前的想法是这样的,不用循环做,只需知道上一个选中的页面和即将要跳转的页面位置即可,实现思路是这样的

private void switchIndicator(int currentPosition) {
    if (oldPosition != -1){
        ((ImageView)indicatorContainer.getChildAt(oldPosition)).setImageDrawable(unSelectedDrawable);
    }
    ((ImageView)indicatorContainer.getChildAt(currentPosition)).setImageDrawable(selectedDrawable);
    oldPosition = currentPosition;
}~~~
但实际的运行效果却可能出现错乱的现象,不知是哪里出了问题,目前就采用了循环来做,后期可能会改进,这样做虽然简单,但效率始终不高

private void switchIndicator(int currentPosition) {
for (int i = 0; i < indicatorContainer.getChildCount(); i++) {
((ImageView) indicatorContainer.getChildAt(i)).setImageDrawable(i == currentPosition ? selectedDrawable : unSelectedDrawable);
}
}~~~

添加页面点击监听回调

用法就像ListView的setOnItemClickListener一样,来看看代码如何实现

private OnBannerItemClickListener onBannerItemClickListener;
//给每个页面添加点击事件
 imageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onBannerItemClickListener != null) {
                    //不直接处理点击事件,转交给onBannerItemClickListener
                    onBannerItemClickListener.onItemClick(position);
                }
            }
        });
public void setOnBannerItemClickListener(OnBannerItemClickListener onBannerItemClickListener) {
    this.onBannerItemClickListener = onBannerItemClickListener;
}
public interface OnBannerItemClickListener {
    void onItemClick(int position);
}~~~
使用

bannerLayout.setOnBannerItemClickListener(new BannerLayout.OnBannerItemClickListener() {
@Override
public void onItemClick(int position) {
//处理点击事件
}
});

####遇到的一些坑
上篇讲到了[BGABanner-Android](https://github.com/bingoogolapple/BGABanner-Android),部分思想也是参考这个库的,例如指示器位置的处理方案,但我要说的坑也在这里,低于3张图片就会直接抛异常,我试着将异常不抛出看看,一张或者两张图片的时候切换效果惨不忍睹(我猜想和ViewPager的懒加载机制有关),所以作者处理为直接抛异常,但这样不行啊,需求不可控制啊,必须解决这个问题,不然就像定时炸弹。

问题解决思路:既然轮播是伪的,图片的张数也可以是伪的,只需要给用户看起来是那样就行了,1张也可以是3x1张相同的图片,2张也可以是2x2张相同图片,3张及以上没问题就无需处理,关键代码

//添加本地图片路径
public void setViewRes(List<Integer> viewRes) {
List<View> views = new ArrayList<>();
itemCount = viewRes.size();
//主要是解决当item为小于3个的时候滑动有问题,这里将其拼凑成3个以上
if (itemCount < 1) {//当item个数0
throw new IllegalStateException("item count not equal zero");
} else if (itemCount < 2) {//当item个数为1
views.add(getImageView(viewRes.get(0), 0));
views.add(getImageView(viewRes.get(0), 0));
views.add(getImageView(viewRes.get(0), 0));
} else if (itemCount < 3) {//当item个数为2
views.add(getImageView(viewRes.get(0), 0));
views.add(getImageView(viewRes.get(1), 1));
views.add(getImageView(viewRes.get(0), 0));
views.add(getImageView(viewRes.get(1), 1));
} else {
for (int i = 0; i < viewRes.size(); i++) {
views.add(getImageView(viewRes.get(i), i));
}
}
setViews(views);
}~~~
添加网络地址和本地的思路差不多,使用的是Glide来处理加载网络图片,还有就是添加各种自定义属性。

github地址BannerLayoutDemo

完整代码实现请看源码,欢迎fork和star以及提出你宝贵的意见

上一篇下一篇

猜你喜欢

热点阅读