Navigation bar special effects-news apps (better than Netease And toutiao.com) and navigation bar apps
I haven't written an article for a long time. I am not accumulating any problems I encountered in my work. I will forget it next time. Ah ....
Boring single programmer on Sunday-only the program you know... reading the program listens to the song is also excellent !!
In recent work, we want to achieve the effect of navigation bar on top of news apps such as toutiao.com. However, we need to add the gradient and zoom of the text color when switching this effect.
I won't get dynamic images. Could you please give me some advice:
I. Analysis
We want to implement this special effect today.
The open-source projects used are: master-nineoldandroids-library.jar this jar package, which is backward compatible with the jar package, including a series of android animations.
First, let's talk about this.The basic implementation of news apps such as toutiao is ViewPage + Fragment + HorizontalScrollView.
What we are talking about today is the special effect of this HorizontalScrollView.
Implementation principle:
I believe you have understood
That is, at the beginning, we initialize textview and initialize the selected and normal textview. Put the two textviews in framelayout to the HashMap set <String, View>. Used to maintain the status of all textviews.
How to write the code ?!
The android system provides a class called PagerSlidingTabStrip, which is included in the v4 package. We put java in our James, and modified the code.
We use viewpage to control the navigation bar. Upload viewpagePagerSlidingTabStrip and set the listener. The Code is as follows:
public void setViewPager(ViewPager pager) {this.pager = pager;if (pager.getAdapter() == null) {throw new IllegalStateException("ViewPager does not have adapter instance.");}pager.setOnPageChangeListener(pageListener);notifyDataSetChanged();}
2. Obtain the current View and the destination View to which the user switches.
ViewPager also needs to listen to user gestures, so it certainly provides a method. So looking at the ViewPager method, we found a method called onPageScrolled (int position, float positionOffset, int positionOffsetPixels ~~
That's right. This method is called during Page scrolling ~
The following describes the parameters:
Test results:
When the first and last pages are not displayed, slide to the next page. position indicates the current page position. Move to the previous page. position indicates the current page-1.
PositionOffset slides to the next page, and changes in the range [); slides to the previous page: (1, 0]
PositionOffsetPixels this is similar to positionOffset: Sliding to the next page, [0, width) interval changes; sliding to the previous page: (width, 0] interval changes
On the first page: slide to the previous page position = 0, the other is basically 0; the last page slides to the next page position is the current page position, the other two parameters are 0
Suddenly, we need to solve the problem in step 2. positionOffset is suitable for the gradient and scaling control parameters. positionOffsetPixels can be used as the control parameters for translation.
So how can we get the current View and the target View:
Share some of my mistakes:
1. [Error] I use getChildAt (position), getChildAt (position + 1), and getChildAt (position-1) to get the two views on the left and right. At first glance, really nice ~~~ When the code is written, the effect will be improved ~~ Cause of error: We ignore a very large object. The ViewPager mechanism dynamically loads and deletes views during sliding. ViewPager actually only maintains two to three views, the range of position is basically infinite ~~
2. [Error] I get the current location through getCurrentItem, and then + 1,-1 to get the next or previous one ~~ Stealing joy, hurry up and change the code. What's wrong with the effect ~~ Observe the log carefully. The getCurrentItem changes when the user's finger leaves the screen and the Page is still being animated ~~ No wonder ~ The entire sliding process is not fixed ~~ Alas, my heart is broken ~
3. [Error] The position does not change throughout the Sliding Process, and ViewPager saves two or three views. So I think, for the first or last page, getChildAt (0) and getChildAt (1) are used. For other pages, getChildAt (0) and getChildAt (2) are used ), after a series of changes ~ I think this will always be the right one, so I encountered the first problem. On the first page, no matter whether the position is 0, Nima, which is the left View and the right View ~~
With so many mistakes, you can bypass these detours and see what you want from them ~
The following is correct. In fact, when ViewPager adds a View or destroys a View, it is controlled by our own PageAdapter. Therefore, we can maintain a HashMap <Position, view>, and then slide it through get (position). For example, the above effect is always the View change on the right side, either from small to large or from large to small.
Slide the next page: View on the left: map. get (position), View on the right: map. get (position + 1 ).
Slide the previous page: View on the left: map. get (position), View on the right: map. get (position + 1), same, because slide to the previous page, position is the current page-1
Key code:
<pre name="code" class="java">private class PageListener implements OnPageChangeListener {private int oldPosition = 0;@Overridepublic void onPageScrolled(int position, float positionOffset,int positionOffsetPixels) {currentPosition = position;currentPositionOffset = positionOffset;scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));invalidate();if (delegatePageListener != null) {delegatePageListener.onPageScrolled(position, positionOffset,positionOffsetPixels);}if (mState == State.IDLE && positionOffset > 0) {oldPage = pager.getCurrentItem();mState = position == oldPage ? State.GOING_RIGHT: State.GOING_LEFT;}boolean goingRight = position == oldPage;if (mState == State.GOING_RIGHT && !goingRight)mState = State.GOING_LEFT;else if (mState == State.GOING_LEFT && goingRight)mState = State.GOING_RIGHT;float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;View mLeft = tabsContainer.getChildAt(position);View mRight = tabsContainer.getChildAt(position + 1);if (effectOffset == 0) {mState = State.IDLE;}if (mFadeEnabled)animateFadeScale(mLeft, mRight, effectOffset, position);}@Overridepublic void onPageScrollStateChanged(int state) {if (state == ViewPager.SCROLL_STATE_IDLE) {scrollToChild(pager.getCurrentItem(), 0);mFadeEnabled = true;}if (delegatePageListener != null) {delegatePageListener.onPageScrollStateChanged(state);}}@Overridepublic void onPageSelected(int position) {// selectedPosition = position;// updateTabStyles();currentPosition = position;// set old view statueViewHelper.setAlpha(tabViews.get(oldPosition).get("normal"), 1);ViewHelper.setAlpha(tabViews.get(oldPosition).get("selected"), 0);View v_old = tabsContainer.getChildAt(oldPosition);ViewHelper.setPivotX(v_old, v_old.getMeasuredWidth() * 0.5f);ViewHelper.setPivotY(v_old, v_old.getMeasuredHeight() * 0.5f);ViewHelper.setScaleX(v_old, 1f);ViewHelper.setScaleY(v_old, 1f);// set new view statueViewHelper.setAlpha(tabViews.get(position).get("normal"), 0);ViewHelper.setAlpha(tabViews.get(position).get("selected"), 1);View v_new = tabsContainer.getChildAt(position);ViewHelper.setPivotX(v_new, v_new.getMeasuredWidth() * 0.5f);ViewHelper.setPivotY(v_new, v_new.getMeasuredHeight() * 0.5f);ViewHelper.setScaleX(v_new, 1 + ZOOM_MAX);ViewHelper.setScaleY(v_new, 1 + ZOOM_MAX);if (delegatePageListener != null) {delegatePageListener.onPageSelected(position);}// oldPosition = selectedPosition;oldPosition = currentPosition;}}
You can see the code: scaling the view is critical:
View v_old = tabsContainer.getChildAt(oldPosition);ViewHelper.setPivotX(v_old, v_old.getMeasuredWidth() * 0.5f);ViewHelper.setPivotY(v_old, v_old.getMeasuredHeight() * 0.5f);ViewHelper.setScaleX(v_old, 1f);ViewHelper.setScaleY(v_old, 1f);
Find the center point of the view, that is, set0000tx set0000ty, and then scale the X axis Y axis. 1 is the original size.> 1 zoom in, <1 and> 0 zoom out.
The key code is: positionOffset sliding percentage of the bottom two parameters of the onPageScrolled method.
The gradient is controlled by the animateFadeScale method:
protected void animateFadeScale(View left, View right,float positionOffset, int position) {if (mState != State.IDLE) {if (left != null) {ViewHelper.setAlpha(tabViews.get(position).get("normal"),positionOffset);ViewHelper.setAlpha(tabViews.get(position).get("selected"),1 - positionOffset);float mScale = 1 + ZOOM_MAX - ZOOM_MAX * positionOffset;ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f);ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f);ViewHelper.setScaleX(left, mScale);ViewHelper.setScaleY(left, mScale);}if (right != null) {ViewHelper.setAlpha(tabViews.get(position + 1).get("normal"),1 - positionOffset);ViewHelper.setAlpha(tabViews.get(position + 1).get("selected"),positionOffset);float mScale = 1 + ZOOM_MAX * positionOffset;ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f);ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f);ViewHelper.setScaleX(right, mScale);ViewHelper.setScaleY(right, mScale);}}}
In the activity/reference area, you can directly set the color size, normal color, and selected color of the TAB. EG:
/*** Assign values to the attributes of PagerSlidingTabStrip. */Private void setTabsValue () {// set the Tab to automatically fill the tabs with the full screen. setShouldExpand (true); // set the Tab line to transparent tabs. setDividerColor (Color. TRANSPARENT); // set the height of the line at the bottom of the Tab. setUnderlineHeight (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, 1, dm); // sets the height of the Tab Indicator tabs. setIndicatorHeight (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_DIP, 4, dm); // set the text size of the Tab title tabs. setTextSize (int) TypedValue. applyDimension (TypedValue. COMPLEX_UNIT_SP, 16, dm); // sets the color of Tab Indicator tabs. setIndicatorColor (Color. parseColor ("# 45c01a"); // sets the color of the Selected Tab text (this is a custom method) tabs. setSelectedTextColor (Color. parseColor ("# 45c01a"); // set the color of normal Tab text (this is a custom method) tabs. setTextColor (Color. parseColor ("# C231C7"); // deselect the background color tabs when you click Tab. setTabBackground (0 );}
Note: This Code was modified on an imitation android-main interface. There may be extra classes. Forget to understand !!
Source code