Why do I say it is the most practical viewpager indicator control?
It has the following characteristics:
1, through the custom View to realize, the code is simple to understand;
2, the use is very convenient;
3, the versatility is high, most involves the viewpager indicator the place can use this control;
4. Two kinds of indicator effects are realized (see the details)
First, to see
The traditional version of the indicator:
Effects of the popular version indicator
Second, analysis
If the simple to achieve this function, I believe that everyone can achieve, and I will not come out here to say, here I am to make it a control, the popular point is that in the future can be directly used, and do not need to modify the code.
Control, that can not be separated from the custom view, I also said in front of a custom view of the article Android custom view, you must know the points, although the talk is very shallow, but I think it is very useful, interested can read, to understand this article is very helpful. Well, it's off the topic! Looking back at those two, the whole View requires only two images of resources, the only difficulty is how to calculate the position of the picture; since it is a universal, easy-to-use control, it is no longer possible to change the state of the indicator in Viewpager onpagerchangerlistener. So this time, you have to pass the Viewpager into this control, here, the analysis is almost;
Third, the Code realization function
Like a mouthful of rice to eat, here you have to create a class, and then let him inherit the View, the previous steps with my previous blog very much like, it is not cumbersome, directly on the code
Public class indicatorview extends View implements Viewpager. Onpagechangelistener { //indicator icon, here is a drawable, consisting of two states, //Check and fly check status PrivateDrawable Mindicator;//indicator icon size, depending on the width and height of the icon to determine, select the larger person Private intMindicatorsize;//width of the entire indicator control Private intMwidth;/ * icon plus a space at home padding width * / Private intMcontextwidth;number of indicator icons, which is the current Viwpager item number Private intMCount;/ * The size of the interval between each indicator * / Private intMmargin;/ * The item of the current view, the main function, is to determine the current indicator's selection * / Private intMselectitem;/ * Indicator sliding offset according to Viewpager * / Private floatMoffset;/ * indicator is refreshed in real time * / Private BooleanMsmooth;/* because Viewpager's pagechangelistener is occupied, it is necessary to define a * so that other calls * * / PrivateViewpager.onpagechangelistener Mpagechangelistener; Public Indicatorview(Context context) { This(Context,NULL); } Public Indicatorview(context context, AttributeSet attrs) { This(Context, Attrs,0); } Public Indicatorview(context context, AttributeSet attrs,intDEFSTYLEATTR) {Super(Context, attrs, defstyleattr);//Get custom properties by TypedArrayTypedArray TypedArray = Getresources (). Obtainattributes (Attrs, R.styleable.indicatorview);//Get the number of custom attributes intN = Typedarray.getindexcount (); for(inti =0; i < N; i++) {intattr = Typedarray.getindex (i);Switch(attr) { CaseR.styleable.indicatorview_indicator_icon://Get an indicator with custom attributesMindicator = typedarray.getdrawable (attr); Break; CaseR.styleable.indicatorview_indicator_margin:floatDefaultmargin = (int) Typedvalue.applydimension (Typedvalue.complex_unit_dip,5, Getresources (). Getdisplaymetrics ()); Mmargin = (int) typedarray.getdimension (attr, Defaultmargin); Break; CaseR.styleable.indicatorview_indicator_smooth:msmooth = Typedarray.getboolean (attr,false) ; Break; } }//After use, remember to recycleTypedarray.recycle (); Initindicator (); }Private void Initindicator() {//Gets the size value of the indicator. In general, the square, but also when your artist hand shook a bit, cut out a rectangle came, //Do not be afraid, the treatment here does not deformMindicatorsize = Math.max (Mindicator.getintrinsicwidth (), Mindicator.getintrinsicheight ());/ * Set the border of the indicator * /Mindicator.setbounds (0,0, Mindicator.getintrinsicwidth (), Mindicator.getintrinsicwidth ()); }}
One thing to note here is that drawable mindicator this member variable, which is a drawable file defined under the Drawable folder that contains the selected and selected two images.
And then the measurement work.
/** * Measure the size of the view, this method of my previous blog spoke a lot, * @param widthmeasurespec * @param Heightmeasuresp EC * / @Override protected void onmeasure(intWidthmeasurespec,intHeightmeasurespec) {setmeasureddimension (Measurewidth (Widthmeasurespec), Measureheight (HeightMeasureSpec)); }/** * measure width, calculate width of Current View * @param widthmeasurespec * @return * * Private int Measurewidth(intWIDTHMEASURESPEC) {intmode = Measurespec.getmode (WIDTHMEASURESPEC);intSize = Measurespec.getsize (WIDTHMEASURESPEC);intwidth;intdesired = Getpaddingleft () + getpaddingright () + Mindicatorsize*mcount + mmargin* (MCount-1) ; Mcontextwidth = desired;if(mode = = measurespec.exactly) {width = Math.max (desired, size); }Else{if(mode = = Measurespec.at_most) {width = math.min (desired,size); }Else{width = desired; }} mwidth = width;returnwidth; }Private int Measureheight(intHEIGHTMEASURESPEC) {intmode = Measurespec.getmode (HEIGHTMEASURESPEC);intSize = Measurespec.getsize (HEIGHTMEASURESPEC);intHeight;if(mode = = measurespec.exactly) {height = size; }Else{intdesired = Getpaddingtop () + getpaddingbottom () + mindicatorsize;if(mode = = Measurespec.at_most) {height = math.min (desired,size); }Else{height = desired; } }returnHeight; }
When the measurement is finished, it is time to draw the View. Here's a look at the OnDraw () method, first, the general flow,
First, draw all the indicators that are selected, here is the draw drawable, so you need to use some of the methods in the canvas to pan the canvas, so that they draw all the drawable in order, here is a special note is the Canvas.restore () method, This method is intended to return to the original position and state call after the drawing is completed, but it must be used in conjunction with Canvas.save (). Canvas.save () is to record the state of the current canvas, so here, I think the name of this method should be replaced by a record () is not more in line with our understanding? Here is a purely personal opinion, understand the good, how to name does not hinder our work, the following is the code of OnDraw (), the comments are very detailed
/** * Draw indicator * @param Canvas */ @Override protected void OnDraw(Canvas canvas) {/ * * First to save the current state of the canvas, if the location line this method * Wait for the restore () will be invalidated, the canvas does not know what state to restore * So this save, restore is in pairs, so It's a good idea. * */Canvas.save ();/ * * This is where the calculation needs to be drawn, * if you don't understand it, please do as I say, pick up the paper and pen near you, draw it on the paper, and then * you'll be at a glance, * */ intleft = mwidth/2-mcontextwidth/2+getpaddingleft (); Canvas.translate (Left,getpaddingtop ()); for(inti =0; i < MCount; i++) {/* * This also needs to be explained, * because we drawable is a selector file * So we need to set his state To get the appropriate picture. * Here is an image to get unselected * */Mindicator.setstate (Empty_state_set);/ * Draw drawable*/Mindicator.draw (canvas);/* Move one indicator to the right once per draw */Canvas.translate (Mindicatorsize+mmargin,0); }/ * * Restore all the canvas settings, not all of them, * According to Google, Matrix/clip * can only revert to the location where the Save method was last called. * */Canvas.restore ();/ * Here we start to calculate the location of the drawing again * / floatLeftdraw = (mindicatorsize+mmargin) * (Mselectitem + moffset);/ * * calculated, again, panning, why do you want to pan two times? * Also for good understanding. * */Canvas.translate (Left,getpaddingtop ()); Canvas.translate (Leftdraw,0);/ * * Set the status of Drawable to selected * This drawable is the image that has been selected. * */Mindicator.setstate (Selected_state_set);/ * Start drawing again * * here!Mindicator.draw (canvas); }
Now our control is actually one step not implemented, that is, when and where to update the view, the first analysis, the view is required to pass in Viewpager, the purpose of the incoming Viewpager, there are actually three,
1, get the number of viewpager item, so as to determine the number of indicators;
2, get the current Viewpager selected item, is also to determine the indicator selected item;
3, access to Onpagerchangelistener, to control when the View needs to be refreshed;
/** * This viewpager must be set adapter first, * and adapter need all the data, subsequent cannot * modify data * @param Viewpager */ Public void Setviewpager(Viewpager Viewpager) {if(Viewpager = =NULL){return; } pageradapter Pageradapter = Viewpager.getadapter ();if(Pageradapter = =NULL){Throw NewRuntimeException ("See Instructions for use"); } MCount = Pageradapter.getcount (); Viewpager.setonpagechangelistener ( This); Mselectitem = Viewpager.getcurrentitem (); Invalidate (); } Public void Setonpagechangelistener(Viewpager.onpagechangelistener Mpagechangelistener) { This. Mpagechangelistener = Mpagechangelistener; }@Override Public void onpagescrolled(intPositionfloatPositionoffset,intPositionoffsetpixels) {LOG.V ("Zgy","========"+position+", ===offset"+ Positionoffset);if(Msmooth) {Mselectitem = position; Moffset = Positionoffset; Invalidate (); }if(Mpagechangelistener! =NULL) {mpagechangelistener.onpagescrolled (position,positionoffset,positionoffsetpixels); } }@Override Public void onpageselected(intPosition) {Mselectitem = position; Invalidate ();if(Mpagechangelistener! =NULL) {mpagechangelistener.onpageselected (position); } }@Override Public void onpagescrollstatechanged(intState) {if(Mpagechangelistener! =NULL) {mpagechangelistener.onpagescrollstatechanged (state); } }
This location also has a point to mention, that is, when Msmooth is true, this time is required to be refreshed in real time, so need in onpagescrolled (int position, float positionoffset, int Positionoffsetpixels) call invalidate () and save the offset to calculate the position of the drawing indicator.
Well, the above is the implementation of the indicator control of the whole process;
Now that's a control, let's look at how XML is referenced.
<com.gyzhong.viewpagerindicator.IndicatorView android:id="@+id/id_indicator" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:layout_marginBottom="20dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="5dp" zgy:indicator_icon="@drawable/indicator_selector" zgy:indicator_margin="5dp"/>
Take a look at the references in the code
mIndicatorView = (IndicatorView) findViewById(R.id.id; mIndicatorView.setViewPager(mViewPager);
The code is simple and straightforward.
Iv. Summary
Overall, not difficult, the code is very small, the main use of knowledge points, 1, custom attributes, 2, how to measure the use of some methods in view,2, Cavans; Finally, if you find it useful, please top it, thank you!
Source: Poke Me
Build Android's most useful Viewpager indicator control