您的位置:首页 > 移动开发 > Android开发

android 中图片的轮播ConvenientBanner

2016-07-10 23:56 477 查看
花了一天的时间来研究这个ConvenientBanner图片轮播的源码,现在来写下自己对该控件的心得。(本人是小白,学习android三个多月,很多东西都不知道,所以本文只是自己对该源码的一些心得。)

首先,需要去下载该控件的源码。读源码这是一个必须要经历的过程。这里有源码下载的地址https://github.com/saiwu-bigkoo/Android-ConvenientBanner

读源码,首先是将该程序运行起来,看看效果,看别人的程序,也是需要将程序跑起来,这样给人直观的感受,自己也知道自己将要去做什么。

接着去开始读源码。开始从控件开始,逐渐向下,但是后面的讲解时从后面开始。看别人的程序时,首先从整体出发,首先去看看gradle中所引用的jar,然后是熟悉那些自己不知道的,暂时不需要去非常熟悉,了解大概就可以了。然后看androidManifest.xml中有哪些信息,然后从主入口中进入,接下来就是从方法中跳来跳去,需要去记住这些方法的执行顺序,现在暂时不去考虑具体的内容,整体把握该程序。

ConvenientBanner控件中

从这写包名中理解了大概,适配器,监听事件和ViewPager。然后再从ConVenientBanner文件入手,逐渐深入。第一遍没有看懂不要紧,我是看第二遍才看懂的。看完源码后,就会恍然大悟,哦,原来是这样。首先我们使用过ViewPager,图片就可以进行滑动了,但是是需要我们手动的滑动,图片才能转到下一页,如何使它自动的完成呢,让它在一个线程中运行,每次都让其页面加1,当到了最后一个页面后,在回到第一个控件。在将线程延时执行。那么就实现了图片的轮播效果。该控件就是这样实现的。

那么现在思路就清晰了,该控件中修改了一下ViewPager,同时ViewPager需要一个适配器,那么也要对PageAdapter实现。我们对ViewPager和PageAdapter都和熟悉,PageAdapter就实现它的四个方法,ViewPager设置适配器。ConVenientBanner就是一个组合控件了,该类中一个核心的代码就是开启线程。这样一来,整个控件就简化成几个核心的方法了。

那么先从PageAdapter开始(读源码的时候是从ConVenientBanner向下走的,进过我们分析以后,就知道,ConVenientBanner就组合了ViewPager,和一些对外id方法,那么现在就从后向前推),PageAdapter有几个核心的方法。其中有四个方法大家很熟悉,getCount(),isViewFromObject(),instantiateItem(),destroyItem()方法的含义就不多说了。
@Override
public int getCount() {
return canLoop ? getRealCount()*MULTIPLE_COUNT : getRealCount();
}

@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
int realPostion = toRealPosition(position);
View view = getView(realPostion, null, container);

container.addView(view);
return view;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
}


getCount()中方法返回值和以前不一样,是因为需要设置轮播效果。重点需要讲instantiateItem()。
public View getView(int position, View view, ViewGroup container) {
Holder holder = null;
if (view == null) {
holder = (Holder) mHolder.createViewHolder();
view = holder.createHolderView(container.getContext());
view.setTag(R.id.cb_item_tag, holder);
} else {
holder = (Holder) view.getTag(R.id.cb_item_tag);
}

if (mDatas != null && !mDatas.isEmpty()) {
holder.updateView(container.getContext(), position, mDatas.get(position));
}
return view;
}
首先,以前我们是知道需要设置的值,但是,现在是可以对该控件的值进行设置,我们不知道值从哪里来,怎么写呢。如何设置VIew。既然是从外面设置值,那么我就去提供接口给别人去实现,然后再这里去调用它,获取我想要获取的值。这里就类似方法的回调,别人去实现方法,我就直接操作接口返回来的值。大家可以去理解一下方法的回调,很重要,很多时候需要去运用到它,比如Fragment向Activity中传值时。这里就简单说一下回调,就是去定义一个接口,在接口中写需要别人实现的方法,自己直接去调用这些接口。别人必须去实现这些方法。
View createHolderView(Context context);
void updateView(Context context,int postion,T data);这就是别人需要实现的两个方法,一个是创建一个VIew,一个是去更新这个VIew(更新它的内容,设置图片)。此时就可以获取到所需要的View了。
还有一个方法,该方法是重新设置一下ViewPager当前的position。

//最后在更新的后期会调用finishUpdate。当finishUpdate返回时
// instantiateItem返回的对象应该添加到父ViewGroup destroyItem返回的对象应该被ViewGroup删除。
@Override
public void finishUpdate(ViewGroup container) {
int position = mViewPager.getCurrentItem();
if (position == 0) {
position = mViewPager.getFirstItem();
} else if (position == getCount() - 1) {
position = mViewPager.getLastItem();
}
try {
mViewPager.setCurrentItem(position, false);
} catch (Exception e) {
e.printStackTrace();
}
}

接下来分析ViewPager,该方法中有两个核心的方法,OnPageChangeListener(),页面翻转的监听事件,还有一个就是事件的拦截。
private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
private float mPreviousPosition = -1;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mOuterPageChangeListener != null){
if (position != mAdapter.getRealCount()-1){
//不是最后一页
mOuterPageChangeListener.onPageScrolled(position,positionOffset,positionOffsetPixels);
}else{
if (positionOffset > 0.5){//翻页
mOuterPageChangeListener.onPageScrolled(0,0,0);
}else{//不翻页,但是将位置重新调整
mOuterPageChangeListener.onPageScrolled(position,0,0);
}
}
}
}

@Override
public void onPageSelected(int position) {
//转化成真实的postion
int realPostion = mAdapter.toRealPosition(position);
if (mPreviousPosition != realPostion) {
mPreviousPosition = realPostion;
if (mOuterPageChangeListener != null) {
mOuterPageChangeListener.onPageSelected(realPostion);
}
}
}

@Override
public void onPageScrollStateChanged(int state) {
if (mOuterPageChangeListener != null){
mOuterPageChangeListener.onPageScrollStateChanged(state);
}
}
};

重点是onPageScrolled,页面进行滑动的时候,会出现几种情况,第一种就是当前页不是最后一页,那么什么都不去操作了,当前页是最后一页,那么是不是翻页的,如果是翻页,就需要将页码设置为0,如果不是,就需要将滑动距离变回原来的值。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isScroller){
return super.onInterceptTouchEvent(ev);
}else{
return false;
}
}
private float oldX = 0, newX = 0;
private static final float sens = 5;
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isScroller){
if (onItemClickListener != null){
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
oldX = getX();
break;
case MotionEvent.ACTION_UP:
newX = getX();
if (Math.abs(oldX-newX) < sens){
onItemClickListener.onItemClick((getRealItem()));
}
oldX = 0;
newX = 0;
break;
}
}
return super.onTouchEvent(ev);
}else{
return false;
}
}
这里的监听事件,就是对滑动距离小于sens时判定为点击事件(需要理解事件的分发机制,大家可以去看看别人写的事件分发)。
到现在为止,ViewPager已经分析完了,接下来就是ConvenientBanner类了,这个类也是很好理解的,创建一个xml文件,在xml文件中引用写好的ViewPager控件,然后再添加一个LinearLayout,它是添加小原点的。init()初始化操作,找打该view和其中的控件。创建一个线程,

static class AdSwitchTask implements Runnable {

private final WeakReference<ConvenientBanner> reference;

AdSwitchTask(ConvenientBanner convenientBanner) {
this.reference = new WeakReference<ConvenientBanner>(convenientBanner);
}

@Override
public void run() {
ConvenientBanner convenientBanner = reference.get();

if(convenientBanner != null){
if (convenientBanner.mViewPager != null && convenientBanner.turning) {
int page = convenientBanner.mViewPager.getCurrentItem() + 1;
convenientBanner.mViewPager.setCurrentItem(page);
convenientBanner.postDelayed(convenientBanner.adSwitchTask, convenientBanner.autoTurningTime);
}
}
}
}
WeakReference是一个弱引用,是将<span style="font-family: Arial, Helvetica, sans-serif;">convenientBanner增强一下,不要让其很快的被垃圾回收掉。run()方法中就是设置ViewPager当前页面(页码加1),然后延时去运行这个线程。</span>
<span style="font-family:Arial, Helvetica, sans-serif;">最主要,最核心的就是这么些方法,理解了之后,其他的就是一些向外公布的方法,比如设置是否轮播,设置是否滚动。大家去看源码。</span>
<span style="font-family:Arial, Helvetica, sans-serif;">
</span>
<span style="font-family:Arial, Helvetica, sans-serif;">这里只是提供我去理解该源码的一个过程,和自己看源码的一些心得,如果有什么不懂的,可以加我的qq:601016205。一起交流,一起学习。</span>
<span style="font-family:Arial, Helvetica, sans-serif;">我是一个小白,一个向往明天回更好的小白。</span>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ConvenientBanner