Reprint please indicate the source: http://blog.csdn.net/lmj623565791/article/details/43649913, this article from: "Zhang Hongyang Blog" 1, overview
Recently, a lot of friends privately talk about the app Bao, 360 software assistant and other software details page How to do, just, recently have time to imitate 360 software assistant Details page to you to do a demo, for everyone to reference. Well, about implementation, I wrote two ways:
1, ScrollView Embedded software introduction +viewpager+viewpager is ScrollView, this way, pure native, not related to the custom control, but so nested, involving measurement and event conflict handling, we can try to do it yourself, It's pretty easy to imagine, but it's really hard to do it, code I will give, the core code is not much, we refer to. This article will focus on the second approach.
2, will do the outer layer of the scrollview changed to a custom control, inherited from LinearLayout, called stickynavlayout, here thanks to the name of the small seven, at the same time thanks to two group warm supply of source code.
Last look, the first one is 360, the second one is ours:
360:
Rub, don't ask me why so vague, try my best ...
We are:
2. How to use
As we have said above, there is a second way, which is to consider the ease of use.
1. Custom ID resource File
Values/ids_sticky_nav_llayout.xml
<?xml version= "1.0" encoding= "Utf-8"?><resources> <item name= "Id_stickynavlayout_topview" type= "id"/> <item name= "Id_stickynavlayout_viewpager" type= "id"/> <item name= "Id_stickynavlayout_ Indicator "type=" id "/> <item name=" Id_stickynavlayout_innerscrollview "type=" id "/></resources>
Define a few ID resources, mainly for the convenience of use, for the declaration of the layout of use, look at the name should be able to guess it, guess it's OK, next I posted the layout file. This is not actually used in the way, but the following meeting, so put it in advance. 2. layout file
<com.zhy.view.stickynavlayout xmlns:tools= "http://schemas.android.com/tools" xmlns:android= "http// Schemas.android.com/apk/res/android "android:layout_width=" match_parent "android:layout_height=" Match_parent "and roid:orientation= "vertical" > <relativelayout android:id= "@id/id_stickynavlayout_topview" android:l Ayout_width= "Match_parent" android:layout_height= "300DP" android:background= "#4400ff00" > <text View android:layout_width= "match_parent" android:layout_height= "Match_parent" Android:gra vity= "center" android:text= "Software Introduction" android:textsize= "30SP" android:textstyle= "bold"/> </RelativeLayout> <com.zhy.view.simpleviewpagerindicator android:id= "@id/id_stickynavlayout_indicato R "android:layout_width=" Match_parent "android:layout_height=" 50DP "android:background=" #ffffffff "&G T </com.zhy.view.simpleviewpagerindicator> <android.support.v4.view.viewpager android:id= "@id/id_stickynavlayout_viewpager" android:l Ayout_width= "Match_parent" android:layout_height= "wrap_content" android:background= "#44ff0000" > </ Android.support.v4.view.viewpager></com.zhy.view.stickynavlayout>
The outermost layer is our custom control stickynavlayout, then the top content area, the VP indicator, and the Viewpager. Follow, go to write OK, note Part ID use our pre-set ID resource. Because our stickynavlayout need to find the control by ID to do some calculations.
Then in our mainactivity, initialize the Viewpager.
3, Mainactivity
Package Com.zhy.sample.stickynavlayout;import Android.os.bundle;import Android.support.v4.app.fragment;import Android.support.v4.app.fragmentactivity;import Android.support.v4.app.fragmentpageradapter;import Android.support.v4.view.viewpager;import Android.support.v4.view.viewpager.onpagechangelistener;import Com.zhy.view.simpleviewpagerindicator;public class Mainactivity extends Fragmentactivity{private String[] MTitles = New string[] {"Introduction", "Evaluation", "related"};p rivate simpleviewpagerindicator mindicator;private viewpager mviewpager;private Fragme Ntpageradapter madapter;private tabfragment[] mfragments = new Tabfragment[mtitles.length]; @Overrideprotected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (R.layout.activity_main); Initviews (); Initdatas (); initevents ();} private void Initevents () {Mviewpager.setonpagechangelistener (new Onpagechangelistener () {@Overridepublic void onpageselected (int position) {} @Overridepublic void onpagescrolled (int position,Float Positionoffset,int positionoffsetpixels) {mindicator.scroll (position, positionoffset);} @Overridepublic void onpagescrollstatechanged (int state) {}});} private void Initdatas () {mindicator.settitles (mtitles); for (int i = 0; i < mtitles.length; i++) {mfragments[i] = (Tabfra gment) tabfragment.newinstance (Mtitles[i]);} Madapter = new Fragmentpageradapter (Getsupportfragmentmanager ()) {@Overridepublic int getcount () {return Mtitles.length;} @Overridepublic Fragment getItem (int position) {return mfragments[position];}}; Mviewpager.setadapter (Madapter); Mviewpager.setcurrentitem (0);} private void Initviews () {mindicator = (simpleviewpagerindicator) Findviewbyid (r.id.id_stickynavlayout_indicator); Mviewpager = (Viewpager) Findviewbyid (R.id.id_stickynavlayout_viewpager);}}
There is no complicated code, the main thing is to initialize our VP;
On the indicator I was temporarily written, but also a custom control bar, the main is to follow the VP to move, the detailed wording please refer to: Android teach you to create cool viewpagerindicator not only high imitation miui, but the triangle into the underline, not the focus of this article, You can ignore it temporarily.
Our VP in each page is a fragment,fragment code we do not paste, layout is scrollview for the root layout, the internal random fill, specific reference source.
After the introduction of the use of wood has a little excitement, the basic to write the layout of the general is OK, the effect of automatic implementation.
4. Fragment and its layout
Stick to our fragment code:
Package Com.zhy.sample.stickynavlayout;import Android.os.bundle;import Android.support.v4.app.fragment;import Android.view.layoutinflater;import Android.view.view;import Android.view.viewgroup;import Android.widget.TextView ;p ublic class Tabfragment extends fragment{public static final String title = "title";p rivate String mtitle = "Defaut Valu E ";p rivate TextView mtextview; @Overridepublic void OnCreate (Bundle savedinstancestate) {super.oncreate ( Savedinstancestate); if (getarguments () = null) {Mtitle = Getarguments (). getString (TITLE);}} @Overridepublic View Oncreateview (Layoutinflater inflater, ViewGroup container,bundle savedinstancestate) {View view = Inflater.inflate (R.layout.fragment_tab, container, false); Mtextview = (TextView) View.findviewbyid (r.id.id_info); Mtextview.settext (mtitle); return view;} public static tabfragment newinstance (String title) {tabfragment tabfragment = new Tabfragment (); Bundle bundle = new bundle (); bundle.putstring (title, title); Tabfragment.setarguments (bundle); RETUrn tabfragment;}}
<scrollview xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http// Schemas.android.com/tools " android:id=" @id/id_stickynavlayout_innerscrollview " android:layout_width=" Match_parent " android:layout_height=" match_parent " android:scrollbars=" None "> <linearlayout android:layout_width= "match_parent" android:layout_height= "wrap_content" android:background= "#eee " android:orientation=" vertical " android:padding=" 5DP "> <textview android:id=" @+id/id _info " android:layout_width=" match_parent " android:layout_height=" 50DP " android:background=" # FFFFFFFF " android:gravity=" center > </textview>//omits countless controls </linearlayout></ Scrollview>
No, let ' s go.
3, Stickynavlayout Source analysis
1. Construction
public class Stickynavlayout extends Linearlayout{private view mtop;private view Mnav;private Viewpager Mviewpager; private int Mtopviewheight;private ScrollView minnerscrollview;private boolean istophidden = False;private Overscroller mscroller;private velocitytracker mvelocitytracker;private int mtouchslop;private int mmaximumvelocity, Mminimumvelocity;private float Mlasty;private Boolean mdragging;public stickynavlayout (context context, AttributeSet Attrs) {Super (context, attrs); SetOrientation (linearlayout.vertical); mscroller = new Overscroller (context); Mvelocitytracker = Velocitytracker.obtain (); mtouchslop = Viewconfiguration.get (context). Getscaledtouchslop (); Mmaximumvelocity = Viewconfiguration.get (context). Getscaledmaximumflingvelocity (); mminimumvelocity = Viewconfiguration.get (context). Getscaledminimumflingvelocity ();} @Overrideprotected void Onfinishinflate () {super.onfinishinflate (); mtop = Findviewbyid (r.id.id_stickynavlayout_ TopView); Mnav = Findviewbyid (r.id.id_stickynavlayout_inDicator); View view = Findviewbyid (R.id.id_stickynavlayout_viewpager); View instanceof Viewpager) {throw new RuntimeException ("Id_stickynavlayout_viewpager show used by Viewpager!"); Mviewpager = (Viewpager) view;}
OK, first look at the member variables, and our variables of the initialization, Mtop, Mnav, Mviewpager represents our layout of the three chunks, the initialization is done in onfinishinflate, you can see directly through our ID resource read on OK. Then there are Mscroller, Mvelocitytracker, Mtouchslop, mmaximumvelocity, mminimumvelocity, Mlasty, mdragging, Needless to say, everyone can think of this as a move-related, Overscroller is an auxiliary class that is used to help us get rid of the computational parts of math when moving. Mvelocitytracker related to several variables, of course, is calculated when the need to automatically move; Mtouchslop help me to distinguish whether the user is click or drag. Android encapsulates a lot of constants in Viewconfiguration, and it's interesting to know about him, and the reason to use these constants is not only to define ourselves, but to align with the behavior of the system.
After reading the construction, because we are using LinearLayout, direct setorientation (linearlayout.vertical), also do not have to go to layout, the control is set vertically arranged. Then we need to do some processing in the onmeasure.
2, Onmeasure
@Overrideprotected void onmeasure (int widthmeasurespec, int heightmeasurespec) {super.onmeasure (Widthmeasurespec, HEIGHTMEASURESPEC); Viewgroup.layoutparams params = Mviewpager.getlayoutparams ();p arams.height = Getmeasuredheight ()- Mnav.getmeasuredheight ();} @Overrideprotected void onsizechanged (int w, int h, int oldw, int oldh) {super.onsizechanged (W, H, OLDW, OLDH); Mtopviewheig HT = Mtop.getmeasuredheight ();}
The main is to set the height of the Viewpager, give it a fixed value, Viewpager himself in the measurement of their own time, if you do not give it fixed value, may be measured results and your expectations will be very large. For example, if you set a wrap_content, you want him to calculate the height of the child to set his own, then you think more. We can look at the source of the Viewpager measurement:
@Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {//For simple implementation, Our internal size are always 0. We depend on the container to specify the layout size of//our view. We can ' t really know what's it is since we'll be//adding and removing different arbitrary views and does not Want the layout to change as this happens. Setmeasureddimension (getdefaultsize (0, Widthmeasurespec), getdefaultsize (0, Heightmeasurespec)); public static int getdefaultsize (int size, int measurespec) {int result = size; int specmode = Measurespec.getmode (Measurespec); int specsize = measurespec.getsize (Measurespec); Switch (specmode) {case MeasureSpec.UNSPECIFIED:result = size; Break Case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specsize; Break } return result; }
As you can see, for both At_most and exactly, two modes are directly reading the incoming measurements of the parent class, that is, he does not measure his child's height. Then if the pattern is: UNSPECIFIED, then the height is directly 0. Here if you have done this example, should be able to encounter this situation, ScrollView in the Viewpager, the measurement mode is unspecified, then the VP directly does not display, the reason is here.
Pull away, come back, we continue.
After we set the value to VP, in theory, our display is normal and the controls are displayed as we expect, but what? We are now in the custom linearlayout, then the move is not supposed to be written by ourselves.
Moving the code is very simple, presumably everyone knows, directly get DX, and then Scrollby on the line.
3, Ontouchevent
@Overridepublic boolean ontouchevent (Motionevent event) {mvelocitytracker.addmovement (event); int action = Event.getaction (); Float y = event.gety (); switch (action) {Case MotionEvent.ACTION_DOWN:if (!mscroller.isfinished ()) Mscroller.abortanimation (); Mvelocitytracker.clear (); Mvelocitytracker.addmovement (event); mlasty = Y;return true; Case MotionEvent.ACTION_MOVE:float dy = y-mlasty;if (!mdragging && math.abs (dy) > mtouchslop) {mdragging = Tru e;} if (mdragging) {Scrollby (0, (int)-dy); mlasty = y;} Break;case MotionEvent.ACTION_CANCEL:mDragging = false;if (!mscroller.isfinished ()) {mscroller.abortanimation ();} Break;case MotionEvent.ACTION_UP:mDragging = false;mvelocitytracker.computecurrentvelocity (mmaximumvelocity) ; int velocityy = (int) mvelocitytracker.getyvelocity (); if (Math.Abs (velocityy) > Mminimumvelocity) {Fling (- VELOCITYY);} Mvelocitytracker.clear (); break;} Return Super.ontouchevent (event);}
Relatively simple ha, because we only need to judge the y direction, so down when the value of the Y record, and then move the time to get dy, go directly into the Scrollby, of course, we are in the process. Addmovement (event); We get velocityy in the v direction and call fling to move.
Fortunately fling's core code Overscroller to us to achieve, so nice.
It should be clear to us that when we use auxiliary classes such as scroller, they help us with the mathematical calculation of knowledge, and we still need to do it ourselves.
How do you do that, and where do you do it? This is nothing more than rewrite Computescroll method, in the inside to determine whether scroller is over, if not, then Scrollto, and finally remember invalidate, related code:
public void Fling (int velocityy) {mscroller.fling (0, getscrolly (), 0, velocityy, 0, 0, 0, mtopviewheight); invalidate ();} @Overridepublic void ScrollTo (int x, int y) {if (Y < 0) {y = 0;} if (Y > mtopviewheight) {y = mtopviewheight;} if (Y! = getscrolly ()) {Super.scrollto (x, y);} Istophidden = getscrolly () = = Mtopviewheight;} @Overridepublic void Computescroll () {if (Mscroller.computescrolloffset ()) {scrollTo (0, Mscroller.getcurry ()); Invalidate ();}}
OK, here, our ontouchevent the ~~but, don't be complacent, why do you say so? Because of what you have done, the knowledge of course view is for upper and lower drag processing. We do not forget that our current stickynavlayout internal but there is a scrollview, then according to the forwarding mechanism of the event, this internal scrollview will certainly handle the situation of dragging up and down, that is, our event will be intercepted by it.
4, Onintercepttouchevent
OK, next we have to deal with interception, for interception, we need to know clearly when to intercept, when not needed, current our example:
1, if our top view as long as not completely hidden, then the direct interception up and down the drag;
2, there is a need to intercept, is the when top department of the view completely hidden, we are now the internal SC should be able to swipe up and down, but if the SC sliding to the top and then down, and then the interception, we need to put the top view can be slipped out.
After the analysis is done, look at the code, which is called a sour cool:
@Overridepublic boolean onintercepttouchevent (motionevent ev) {int action = ev.getaction (); Float y = ev.gety (); switch ( Action) {Case MotionEvent.ACTION_DOWN:mLastY = y;break;case MotionEvent.ACTION_MOVE:float dy = y-mlasty; Getcurrentscrollview (), if (Math.Abs (dy) > mtouchslop) {mdragging = true;if (!istophidden| | (minnerscrollview.getscrolly () = = 0 && istophidden && dy > 0)) {return true;}} break;} return super.onintercepttouchevent (EV);}
Ok,move in the above two cases, O.
SOURCE Click Download, if there is a bug, welcome to put forward.
Group number: 423372824
Bo Master part of the video has been online, if you do not like boring text, please poke (first record, look forward to your support):
Video Directory Address: I recorded a video tutorial
Android custom controls easily implement 360 software details page