(一):写在前面的话
接着上一篇继续更新,上一篇文章已经把FastDev4Android项目做了大体的了解,包括项目结构已经需要进行完善的功能,那么今天我们继续完善这个项目;今天我们主要将的是实现一个首页自动无限循环组件我这边采用的是Gallery(重写)+FlowIndicator(自定义);
项目地址

(二)Gallery控件讲解
2.1:说明-实现效果如下:

Gallery为画廊控件相信大家对此非常熟悉,同时这个Gallery组件已经早就过时了,虽然官方说这个组件会造成内存过大,缓存机制不行,或者说缓存机制完全不行,不过如果作为初级阶段要实现自动轮播用这个练手还是非常方便的。虽然现在一般可以采用viewpager或者RecyleView来进行实现,后面我们还会更新viewpager和recyleview实现的图片轮播组件;
2.2:实现方式:
要实现图片的自动无限轮播,那么最重要要实现自动轮播的功能,在Gallery我们只需要实现一个定时器,每个一段时间调用Gallery的方法来切换图片如下:

接着在定时器中直接调用onkeydown让gallery来切换图片,我们来看一下onkeydown的方法:

/***Handlesleft,right,andclicking*@seeandroid.view.View#onKeyDown*/@OverridepublicbooleanonKeyDown(intkeyCode,KeyEventevent){switch(keyCode){caseKeyEvent.KEYCODE_DPAD_LEFT:if(moveDirection(-1)){playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);returntrue;}break;caseKeyEvent.KEYCODE_DPAD_RIGHT:if(moveDirection(1)){playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);returntrue;}break;caseKeyEvent.KEYCODE_DPAD_CENTER:caseKeyEvent.KEYCODE_ENTER:mReceivedInvokeKeyDown=true;//fallthroughtodefaulthandling}returnsuper.onKeyDown(keyCode,event);}12345678910111213141516171819202122232425262728

上边的源代码我们可以很清晰看到KEYCODE_DPAD_LEFT,KEYCODE_DPAD_RIGHT这两个参数,OK那么我们在调用onkeydown传入参数的时候传入这两个值就OK了。那么我们该如果设置这两个参数呢?其实很简单gallery是提供我们手指滑动来切换的,也就是Gallery实现了GestureDetector.OnGestureListener手势接口,那么我们可以重写onFing方法判断是向左还是向右滑动,代码如下:

@OverridepublicbooleanonFling(MotionEvente1,MotionEvente2,floatvelocityX,floatvelocityY){intkEvent;if(isScrollingLeft(e1,e2)){kEvent=KeyEvent.KEYCODE_DPAD_LEFT;//设置手势滑动的方法--向左}else{kEvent=KeyEvent.KEYCODE_DPAD_RIGHT;//设置手势滑动的方法--向右}onKeyDown(kEvent,null);//进行设置galler切换图片if(this.getSelectedItemPosition()==0){this.setSelection(length);}returnfalse;}123456789101112131415

下面我们来看一下AutoGallery的源代码,详细了解一下实现方法:

packagecom.chinaztt.fda.widget;importandroid.content.Context;importandroid.util.AttributeSet;importandroid.view.KeyEvent;importandroid.view.MotionEvent;importandroid.view.View;importandroid.widget.Gallery;importjava.util.Timer;importjava.util.TimerTask;/***当前类注释:重写Gallery对画廊控件进行重写,扩展成可以自动切换图片Gallery*这个图片轮播控件还是比较之前封装的,现在一般采用viewpager进行封装,后面我这边也会介绍*项目名:FastDev4Android*包名:com.chinaztt.fda.widget*作者:江清清on15/10/2308:41*邮箱:jiangqqlmj@163.com*QQ:781931404*公司:江苏中天科技软件技术有限公司*/publicclassAutoGalleryextendsGalleryimplementsView.OnTouchListener{//画廊图片的数量privateintlength;//自动切换图片的时间privatelongdelayMillis=5000;//定时器privateTimertimer=null;publicAutoGallery(Contextcontext){super(context);setOnTouchListener(this);}publicAutoGallery(Contextcontext,AttributeSetattrs){super(context,attrs);setOnTouchListener(this);}publicAutoGallery(Contextcontext,AttributeSetattrs,intdefStyle){super(context,attrs,defStyle);setOnTouchListener(this);}publicintgetLength(){returnthis.length;}publicvoidsetLength(intlength){this.length=length;}publicvoidsetDelayMillis(longdelayMillis){this.delayMillis=delayMillis;}/***重写Galler中手指滑动的手势方法*@parame1*@parame2*@paramvelocityX*@paramvelocityY*@return*/@OverridepublicbooleanonFling(MotionEvente1,MotionEvente2,floatvelocityX,floatvelocityY){intkEvent;if(isScrollingLeft(e1,e2)){kEvent=KeyEvent.KEYCODE_DPAD_LEFT;//设置手势滑动的方法--向左}else{kEvent=KeyEvent.KEYCODE_DPAD_RIGHT;//设置手势滑动的方法--向右}onKeyDown(kEvent,null);//进行设置galler切换图片if(this.getSelectedItemPosition()==0){this.setSelection(length);}returnfalse;}/***进行判断滑动方向*@parame1*@parame2*@return*/privatebooleanisScrollingLeft(MotionEvente1,MotionEvente2){returne2.getX()>e1.getX();}/***开启定时器*/publicvoidstart(){if(length>0&&timer==null){timer=newTimer();//进行每个delayMillis时间gallery切换一张图片timer.scheduleAtFixedRate(newTimerTask(){@Overridepublicvoidrun(){if(length>0){onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT,null);}}},delayMillis,delayMillis);}}/***关闭定时器*/publicvoidstop(){if(timer!=null){timer.cancel();timer=null;}}/***重写手指触摸的事件,当手指按下的时候,需要关闭gallery自动切换*当手指抬开得时候需要打开gallery自动切换功能*@paramv*@paramevent*@return*/@OverridepublicbooleanonTouch(Viewv,MotionEventevent){switch(event.getAction()){caseMotionEvent.ACTION_DOWN:stop();break;caseMotionEvent.ACTION_UP:caseMotionEvent.ACTION_CANCEL:start();break;}returnfalse;}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140

(三)FlowIndicator控件讲解
3.1:实现效果:

3.2:指示器是一个自定义的view,通过实时的绘制,首先我们需要定义圆点的attrs属性文件如下:

<!--图片轮播下面指示器自定义属性--><declare-styleablename="FlowIndicator"><attrname="point_normal_color"format="color"/><!--指示器小圆点正常颜色--><attrname="point_seleted_color"format="color"/><!--指示器小圆点选中颜色--><attrname="radius"format="dimension"/><!--指示器原点的半径--><attrname="count"format="integer"/><!--指示器原点的数量--><attrname="space"format="dimension"/><!--每个指示器原点的间隔--></declare-styleable>12345678

然后我们在FlowDicator中进行获取到相关属性,并且进行绘制ondraw即可,不过在绘制的时候需要判断以下当前是否选中,这样分别绘制选中和未选中的原点;

@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);floatwidth=(getWidth()-((radius*2*count)+(space*(count-1))))/2.f;Log.d(TAG_FLOWINDICATOR,"当前选中的为:"+this.seleted);for(inti=0;i<count;i++){if(i==seleted){paint.setStyle(Style.FILL);canvas.drawBitmap(bmp_selected,130+width+getPaddingLeft()+radius+i*(space+radius+radius),0,null);}else{paint.setStyle(Style.FILL);canvas.drawBitmap(bmp_normal,130+width+getPaddingLeft()+radius+i*(space+radius+radius),0,null);}}}1234567891011121314151617

3.3:具体实现代码,FlowIndicator.java

packagecom.chinaztt.fda.widget;importandroid.content.Context;importandroid.content.res.TypedArray;importandroid.graphics.Bitmap;importandroid.graphics.BitmapFactory;importandroid.graphics.Canvas;importandroid.graphics.Paint;importandroid.graphics.Paint.Style;importandroid.util.AttributeSet;importandroid.view.View;importcom.chinaztt.fda.ui.R;importcom.chinaztt.fda.utils.Log;/***自动播放Gallery指示器*@authorjiangqq**/publicclassFlowIndicatorextendsView{privatestaticfinalStringTAG_FLOWINDICATOR="FlowIndicator";privateintcount;privatefloatspace,radius;privatePaintpaint=newPaint(Paint.ANTI_ALIAS_FLAG);privateBitmapbmp_selected,bmp_normal;//选中privateintseleted=0;publicFlowIndicator(Contextcontext,AttributeSetattrs){super(context,attrs);TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.FlowIndicator);//小圆点数量count=a.getInteger(R.styleable.FlowIndicator_count,4);//每个小圆点间隔距离space=a.getDimension(R.styleable.FlowIndicator_space,4);//小圆点半径radius=a.getDimension(R.styleable.FlowIndicator_radius,7);//正常没有选中的图片bmp_normal=BitmapFactory.decodeResource(getResources(),R.drawable.hui);//选中的图片bmp_selected=BitmapFactory.decodeResource(getResources(),R.drawable.lan);a.recycle();}//当前选中的索引,并且重绘指示器viewpublicvoidsetSeletion(intindex){this.seleted=index;invalidate();}//设置指示器的数量publicvoidsetCount(intcount){this.count=count;invalidate();}//设置指示器下一个圆点publicvoidnext(){if(seleted<count-1)seleted++;elseseleted=0;invalidate();}//设置指示器前一个圆点publicvoidprevious(){if(seleted>0)seleted--;elseseleted=count-1;invalidate();}/***重写绘制指示器view*@paramcanvas*/@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);floatwidth=(getWidth()-((radius*2*count)+(space*(count-1))))/2.f;Log.d(TAG_FLOWINDICATOR,"当前选中的为:"+this.seleted);for(inti=0;i<count;i++){if(i==seleted){paint.setStyle(Style.FILL);canvas.drawBitmap(bmp_selected,130+width+getPaddingLeft()+radius+i*(space+radius+radius),0,null);}else{paint.setStyle(Style.FILL);canvas.drawBitmap(bmp_normal,130+width+getPaddingLeft()+radius+i*(space+radius+radius),0,null);}}}/***进行view大小的测量*@paramwidthMeasureSpec*@paramheightMeasureSpec*/@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));}privateintmeasureWidth(intmeasureSpec){intresult=0;intspecMode=MeasureSpec.getMode(measureSpec);intspecSize=MeasureSpec.getSize(measureSpec);if(specMode==MeasureSpec.EXACTLY){result=specSize;}else{result=(int)(getPaddingLeft()+getPaddingRight()+(count*2*radius)+(count-1)*radius+1);if(specMode==MeasureSpec.AT_MOST){result=Math.min(result,specSize);}}returnresult;}privateintmeasureHeight(intmeasureSpec){intresult=0;intspecMode=MeasureSpec.getMode(measureSpec);intspecSize=MeasureSpec.getSize(measureSpec);if(specMode==MeasureSpec.EXACTLY){result=specSize;}else{result=(int)(2*radius+getPaddingTop()+getPaddingBottom()+1);if(specMode==MeasureSpec.AT_MOST){result=Math.min(result,specSize);}}returnresult;}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142

(四):该组件的使用方法
我们创建布局文件使用当前组件的包名路径,然后进行初始化并且设置数据,最后绑定事件监听器即可;

packagecom.chinaztt.fda.test;importandroid.os.Bundle;importandroid.os.PersistableBundle;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.view.ViewGroup;importandroid.widget.AdapterView;importandroid.widget.BaseAdapter;importandroid.widget.ImageView;importcom.chinaztt.fda.ui.R;importcom.chinaztt.fda.ui.base.BaseActivity;importcom.chinaztt.fda.widget.AutoGallery;importcom.chinaztt.fda.widget.FlowIndicator;/***当前类注释:图片轮播封装类的简单使用*项目名:FastDev4Android*包名:com.chinaztt.fda.test*作者:江清清on15/10/2308:35*邮箱:jiangqqlmj@163.com*QQ:781931404*公司:江苏中天科技软件技术有限公司*/publicclassGalleryIndicatorActivityextendsBaseActivity{privateLayoutInflatermInflater;privateint[]mImages;privateAutoGalleryheadline_p_w_picpath_gallery;//自动图片轮播GalleryprivateFlowIndicatorgalleryFlowIndicator;//指示器控件privateintcircleSelectedPosition=0;//默认指示器的圆圈的位置为第一项privateintgallerySelectedPositon=0;//默认gallery的图片为第一张@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.gallery_indicator_layout);mInflater=getLayouInflater();mImages=newint[]{R.drawable.one,R.drawable.two,R.drawable.three,R.drawable.four};headline_p_w_picpath_gallery=(AutoGallery)this.findViewById(R.id.headline_p_w_picpath_gallery);galleryFlowIndicator=(FlowIndicator)this.findViewById(R.id.headline_circle_indicator);inttopSize=mImages.length;//设置指示器圆点的数量galleryFlowIndicator.setCount(topSize);//设置当前的位置galleryFlowIndicator.setSeletion(circleSelectedPosition);//设置画廊图片的数量headline_p_w_picpath_gallery.setLength(topSize);headline_p_w_picpath_gallery.setAdapter(newGalleryIndicatorAdapter());gallerySelectedPositon=topSize*20+circleSelectedPosition;//设置画廊当前所指的位置索引headline_p_w_picpath_gallery.setSelection(gallerySelectedPositon);headline_p_w_picpath_gallery.start();//gallery滚动选择监听headline_p_w_picpath_gallery.setOnItemSelectedListener(newAdapterView.OnItemSelectedListener(){@OverridepublicvoidonItemSelected(AdapterView<?>parent,Viewview,intposition,longid){gallerySelectedPositon=position;circleSelectedPosition=position%headline_p_w_picpath_gallery.getLength();galleryFlowIndicator.setSeletion(circleSelectedPosition);}@OverridepublicvoidonNothingSelected(AdapterView<?>parent){}});//gallery点击选中事件headline_p_w_picpath_gallery.setOnItemClickListener(newAdapterView.OnItemClickListener(){@OverridepublicvoidonItemClick(AdapterView<?>parent,Viewview,intposition,longid){intindex=position%headline_p_w_picpath_gallery.getLength()+1;showToastMsgShort("点击了第"+index+"个图片!");}});}classGalleryIndicatorAdapterextendsBaseAdapter{@OverridepublicintgetCount(){returnInteger.MAX_VALUE;}@OverridepublicObjectgetItem(intposition){returnmImages[position];}@OverridepubliclonggetItemId(intposition){returnposition;}@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){Hondler_Hondler=null;if(convertView==null){_Hondler=newHondler();convertView=mInflater.inflate(R.layout.headline_gallery_item,null);_Hondler.headline_gallery_p_w_picpathview=(ImageView)convertView.findViewById(R.id.headline_gallery_p_w_picpathview);convertView.setTag(_Hondler);}else{_Hondler=(Hondler)convertView.getTag();}intmPosition=position%mImages.length;_Hondler.headline_gallery_p_w_picpathview.setImageResource(mImages[mPosition]);returnconvertView;}}staticclassHondler{ImageViewheadline_gallery_p_w_picpathview;}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127

上面是该组件的基本使用方法,由于篇幅的原因,布局文件这类我这边就没有贴上去,主要讲解了控件的主要实现方法,如果需要具体了解该项目和该组件可以去github中clone一下代码;
项目地址如下:https://github.com/jiangqqlmj/FastDev4Android