Android之仿今日头条顶部导航栏效果

摘要:
随着时间的推移,当前的软件需要显示越来越多的内容,所以为了更好地在小屏幕上显示更多的内容,我们首先想到的是底部菜单栏。但有时,像今天的头条新闻一样,客户端需要显示太多内容,而且还想在主界面上显示所有内容,因此有一个顶部导航栏。今天标题顶部导航栏区域的主要部分是导航菜单。

随着时间的推移现在的软件要求显示的内容越来越多,所以要在小的屏幕上能够更好的显示更多的内容,首先我们会想到底部菜单栏,但是有时候像今日头条新闻客户端要显示的内容太多,而且又想在主界面全部显示出来,所以有加了顶部导航栏。

今日头条顶部导航栏区域的主要部分是一个导航菜单。导航菜单是一组标签的集合,在新闻客户端中,每个标签标示一个新闻类别,对应下面ViewPager控件的一个分页面。当用户在ViewPager区域滑动页面时,对应的导航菜单标签也会相应的被选中,选中的标签通过一个矩形红框高亮显示,红框背景中的标签文字变为白色,红框外的区域标签文字仍为灰色。当用户直接在导航菜单选中某个标签时,ViewPager会自动的切换到对应的分页面。在本文中导航菜单作为一个单独的UI控件实现,类名为CatagoryTabStrip,继承自HorizontalScrollView,这样就可以很容易的实现导航菜单的左右滑动效果以及与下面ViewPager控件的联动。

先看一下实现的效果对比:
Android之仿今日头条顶部导航栏效果第1张
顶部导航栏区域和ViewPager区域View层次结构
Android之仿今日头条顶部导航栏效果第2张

主界面布局

  1. <RelativeLayout android:id="@+id/main_layout"   
  2.     android:background="@color/activity_bg_color"   
  3.     android:layout_width="fill_parent"   
  4.     android:layout_height="fill_parent"  
  5.     xmlns:android="http://schemas.android.com/apk/res/android">  
  6.     <RelativeLayout android:id="@+id/title_bar" style="@style/main_title_bar_style">  
  7.         <FrameLayout android:id="@+id/top_head_container"   
  8.             android:paddingLeft="10.0dip"   
  9.             android:paddingRight="10.0dip"   
  10.             android:layout_width="wrap_content"  
  11.              android:layout_height="fill_parent">  
  12.             <ImageView android:layout_gravity="center_vertical"   
  13.                 android:id="@+id/top_head"   
  14.                 android:contentDescription="@string/app_name"  
  15.                 android:background="@drawable/bg_head"   
  16.                 android:src="@drawable/default_round_head"  
  17.                 android:padding="2.0dip"   
  18.                 android:layout_width="@dimen/head_size"   
  19.                 android:layout_height="@dimen/head_size"   
  20.                 android:scaleType="fitXY" />  
  21.         </FrameLayout>  
  22.         <ImageView android:gravity="center"   
  23.             android:id="@+id/top_more"   
  24.             android:contentDescription="@string/app_name"  
  25.             android:layout_width="wrap_content"   
  26.             android:layout_height="fill_parent"   
  27.             android:layout_marginRight="12.0dip"   
  28.             android:src="@drawable/right_drawer"   
  29.             android:scaleType="centerInside"   
  30.             android:layout_alignParentRight="true"   
  31.             android:layout_centerVertical="true" />  
  32.         <RelativeLayout android:id="@+id/title_click_layout"   
  33.             android:paddingLeft="13.0dip"   
  34.             android:layout_width="wrap_content"   
  35.             android:layout_height="fill_parent"   
  36.             android:layout_centerInParent="true">  
  37.             <FrameLayout android:id="@+id/title_parent"   
  38.                 android:layout_width="wrap_content"   
  39.                 android:layout_height="wrap_content"   
  40.                 android:layout_centerVertical="true">  
  41.                 <ImageView android:layout_gravity="center"   
  42.                     android:id="@+id/title_recent"   
  43.                     android:contentDescription="@string/app_name"  
  44.                     android:layout_width="wrap_content"   
  45.                     android:layout_height="wrap_content"   
  46.                     android:src="@drawable/title" />  
  47.             </FrameLayout>  
  48.             <ImageView android:id="@+id/top_refresh"   
  49.                 android:contentDescription="@string/app_name"  
  50.                 android:padding="3.0dip"   
  51.                 android:layout_width="wrap_content"   
  52.                 android:layout_height="wrap_content"   
  53.                 android:src="@drawable/refreshicon_titlebar"   
  54.                 android:layout_toRightOf="@id/title_parent"   
  55.                 android:layout_centerVertical="true" />  
  56.         </RelativeLayout>  
  57.     </RelativeLayout>  
  58.       
  59.     <RelativeLayout android:id="@+id/category_layout"   
  60.         android:background="@drawable/bg_category_bar"   
  61.         android:layout_width="fill_parent"   
  62.         android:layout_height="@dimen/top_category_height"   
  63.         android:layout_below="@id/title_bar" >  
  64.   
  65.         <ImageView android:id="@+id/icon_category"   
  66.                android:layout_width="@dimen/top_category_height"   
  67.                android:layout_height="@dimen/top_category_height"   
  68.                android:src="@drawable/ic_category_expand"  
  69.                android:contentDescription="@string/app_name"  
  70.                android:scaleType="center"   
  71.                android:layout_alignParentRight="true"   
  72.                android:layout_centerVertical="true" />  
  73.   
  74.         <LinearLayout android:layout_width="wrap_content"   
  75.            android:layout_height="@dimen/top_category_height"  
  76.            android:layout_toLeftOf="@id/icon_category"  
  77.            android:layout_alignParentLeft="true"   
  78.            android:layout_centerVertical="true">  
  79.              
  80.             <com.rainsong.toutiaotabdemo.CategoryTabStrip   
  81.                 android:id="@+id/category_strip"   
  82.                 android:paddingLeft="6.0dip"   
  83.                 android:paddingRight="6.0dip"   
  84.                 android:clipToPadding="false"   
  85.                 android:layout_width="wrap_content"   
  86.                 android:layout_height="@dimen/top_category_height" />  
  87.         </LinearLayout>  
  88.     </RelativeLayout>  
  89.     <android.support.v4.view.ViewPager android:id="@+id/view_pager"   
  90.         android:layout_width="fill_parent"   
  91.         android:layout_height="fill_parent"   
  92.         android:layout_below="@id/category_layout" />  
  93. </RelativeLayout>  


在Activity中CatagoryTabStrip控件与ViewPager控件的联合使用

MainActivity.java

  1. package com.rainsong.toutiaotabdemo;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.os.Bundle;  
  7. import android.support.v4.app.Fragment;  
  8. import android.support.v4.app.FragmentActivity;  
  9. import android.support.v4.app.FragmentManager;  
  10. import android.support.v4.app.FragmentPagerAdapter;  
  11. import android.support.v4.view.ViewPager;  
  12.   
  13. public class MainActivity extends FragmentActivity {  
  14.     private CategoryTabStrip tabs;  
  15.     private ViewPager pager;  
  16.     private MyPagerAdapter adapter;  
  17.   
  18.     @Override  
  19.     protected void onCreate(Bundle savedInstanceState) {  
  20.         super.onCreate(savedInstanceState);  
  21.         setContentView(R.layout.activity_main);  
  22.           
  23.         tabs = (CategoryTabStrip) findViewById(R.id.category_strip);  
  24.         pager = (ViewPager) findViewById(R.id.view_pager);  
  25.         adapter = new MyPagerAdapter(getSupportFragmentManager());  
  26.   
  27.         pager.setAdapter(adapter);  
  28.   
  29.         tabs.setViewPager(pager);  
  30.   
  31.     }  
  32.   
  33.     public class MyPagerAdapter extends FragmentPagerAdapter {  
  34.   
  35.         private final List<String> catalogs = new ArrayList<String>();  
  36.   
  37.         public MyPagerAdapter(FragmentManager fm) {  
  38.             super(fm);  
  39.             catalogs.add(getString(R.string.category_hot));  
  40.             catalogs.add("u672cu5730");  
  41.             catalogs.add(getString(R.string.category_video));  
  42.             catalogs.add(getString(R.string.category_society));  
  43.             catalogs.add(getString(R.string.category_entertainment));  
  44.             catalogs.add(getString(R.string.category_tech));  
  45.             catalogs.add(getString(R.string.category_finance));  
  46.             catalogs.add(getString(R.string.category_military));  
  47.             catalogs.add(getString(R.string.category_world));  
  48.             catalogs.add(getString(R.string.category_image_ppmm));  
  49.             catalogs.add(getString(R.string.category_health));  
  50.             catalogs.add(getString(R.string.category_government));  
  51.         }  
  52.   
  53.         @Override  
  54.         public CharSequence getPageTitle(int position) {  
  55.             return catalogs.get(position);  
  56.         }  
  57.   
  58.         @Override  
  59.         public int getCount() {  
  60.             return catalogs.size();  
  61.         }  
  62.   
  63.         @Override  
  64.         public Fragment getItem(int position) {  
  65.             return NewsFragment.newInstance(position);  
  66.         }  
  67.   
  68.     }  
  69.   
  70. }  

CatagoryTabStrip控件的实现代码

CategoryTabStrip.java

  1. package com.rainsong.toutiaotabdemo;  
  2. import android.content.Context;  
  3. import android.graphics.Canvas;  
  4. import android.graphics.Rect;  
  5. import android.graphics.drawable.Drawable;  
  6. import android.support.v4.view.ViewPager;  
  7. import android.support.v4.view.ViewPager.OnPageChangeListener;  
  8. import android.util.AttributeSet;  
  9. import android.util.DisplayMetrics;  
  10. import android.util.TypedValue;  
  11. import android.view.Gravity;  
  12. import android.view.LayoutInflater;  
  13. import android.view.View;  
  14. import android.view.ViewGroup;  
  15. import android.widget.HorizontalScrollView;  
  16. import android.widget.LinearLayout;  
  17. import android.widget.TextView;  
  18. public class CategoryTabStrip extends HorizontalScrollView {  
  19.     private LayoutInflater mLayoutInflater;  
  20.     private final PageListener pageListener = new PageListener();  
  21.     private ViewPager pager;  
  22.     private LinearLayout tabsContainer;  
  23.     private int tabCount;  
  24.   
  25.     private int currentPosition = 0;  
  26.     private float currentPositionOffset = 0f;  
  27.   
  28.     private Rect indicatorRect;  
  29.   
  30.     private LinearLayout.LayoutParams defaultTabLayoutParams;  
  31.   
  32.     private int scrollOffset = 10;  
  33.     private int lastScrollX = 0;  
  34.   
  35.     private Drawable indicator;  
  36.     private TextDrawable[] drawables;  
  37.     private Drawable left_edge;  
  38.     private Drawable right_edge;  
  39.   
  40.     public CategoryTabStrip(Context context) {  
  41.         this(context, null);  
  42.     }  
  43.   
  44.     public CategoryTabStrip(Context context, AttributeSet attrs) {  
  45.         this(context, attrs, 0);  
  46.     }  
  47.   
  48.     public CategoryTabStrip(Context context, AttributeSet attrs, int defStyle) {  
  49.         super(context, attrs, defStyle);  
  50.         mLayoutInflater = LayoutInflater.from(context);  
  51.         drawables = new TextDrawable[3];  
  52.         int i = 0;  
  53.         while (i < drawables.length) {  
  54.             drawables[i] = new TextDrawable(getContext());  
  55.             i++;  
  56.         }  
  57.   
  58.         indicatorRect = new Rect();  
  59.   
  60.         setFillViewport(true);  
  61.         setWillNotDraw(false);  
  62.   
  63.         // 标签容器  
  64.         tabsContainer = new LinearLayout(context);  
  65.         tabsContainer.setOrientation(LinearLayout.HORIZONTAL);  
  66.         tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));  
  67.         addView(tabsContainer);  
  68.   
  69.         DisplayMetrics dm = getResources().getDisplayMetrics();  
  70.         scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm);  
  71.   
  72.         defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);  
  73.         // 绘制高亮区域作为滑动分页指示器  
  74.         indicator = getResources().getDrawable(R.drawable.bg_category_indicator);  
  75.         // 左右边界阴影效果  
  76.         left_edge = getResources().getDrawable(R.drawable.ic_category_left_edge);  
  77.         right_edge = getResources().getDrawable(R.drawable.ic_category_right_edge);  
  78.     }  
  79.   
  80.     // 绑定与CategoryTabStrip控件对应的ViewPager控件,实现联动  
  81.     public void setViewPager(ViewPager pager) {  
  82.         this.pager = pager;  
  83.         if (pager.getAdapter() == null) {  
  84.             throw new IllegalStateException("ViewPager does not have adapter instance.");  
  85.         }  
  86.         pager.setOnPageChangeListener(pageListener);  
  87.         notifyDataSetChanged();  
  88.     }  
  89.   
  90.     // 当附加在ViewPager适配器上的数据发生变化时,应该调用该方法通知CategoryTabStrip刷新数据  
  91.     public void notifyDataSetChanged() {  
  92.         tabsContainer.removeAllViews();  
  93.         tabCount = pager.getAdapter().getCount();  
  94.         for (int i = 0; i < tabCount; i++) {  
  95.             addTab(i, pager.getAdapter().getPageTitle(i).toString());  
  96.         }  
  97.     }  
  98.   
  99.     // 添加一个标签到导航菜单  
  100.     private void addTab(final int position, String title) {  
  101.         ViewGroup tab = (ViewGroup)mLayoutInflater.inflate(R.layout.category_tab, this, false);  
  102.         TextView category_text = (TextView) tab.findViewById(R.id.category_text);  
  103.         category_text.setText(title);  
  104.         category_text.setGravity(Gravity.CENTER);  
  105.         category_text.setSingleLine();  
  106.         category_text.setFocusable(true);  
  107.         category_text.setTextColor(getResources().getColor(R.color.category_tab_text));  
  108.         tab.setOnClickListener(new OnClickListener() {  
  109.             @Override  
  110.             public void onClick(View v) {  
  111.                 pager.setCurrentItem(position);  
  112.             }  
  113.         });  
  114.         tabsContainer.addView(tab, position, defaultTabLayoutParams);  
  115.     }  
  116.   
  117.     // 计算滑动过程中矩形高亮区域的上下左右位置  
  118.     private void calculateIndicatorRect(Rect rect) {  
  119.         ViewGroup currentTab = (ViewGroup)tabsContainer.getChildAt(currentPosition);  
  120.         TextView category_text = (TextView) currentTab.findViewById(R.id.category_text);  
  121.   
  122.         float left = (float) (currentTab.getLeft() + category_text.getLeft());  
  123.         float width = ((float) category_text.getWidth()) + left;  
  124.   
  125.         if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {  
  126.             ViewGroup nextTab = (ViewGroup)tabsContainer.getChildAt(currentPosition + 1);  
  127.             TextView next_category_text = (TextView) nextTab.findViewById(R.id.category_text);  
  128.   
  129.             float next_left = (float) (nextTab.getLeft() + next_category_text.getLeft());  
  130.             left = left * (1.0f - currentPositionOffset) + next_left * currentPositionOffset;  
  131.             width = width * (1.0f - currentPositionOffset) + currentPositionOffset * (((float) next_category_text.getWidth()) + next_left);  
  132.         }  
  133.   
  134.         rect.set(((int) left) + getPaddingLeft(), getPaddingTop() + currentTab.getTop() + category_text.getTop(),  
  135.                 ((int) width) + getPaddingLeft(), currentTab.getTop() + getPaddingTop() + category_text.getTop() + category_text.getHeight());  
  136.     }  
  137.   
  138.     // 计算滚动范围  
  139.     private int getScrollRange() {  
  140.         return getChildCount() > 0 ? Math.max(0, getChildAt(0).getWidth() - getWidth() + getPaddingLeft() + getPaddingRight()) : 0;  
  141.     }  
  142.   
  143.     // CategoryTabStrip与ViewPager联动逻辑  
  144.     private void scrollToChild(int position, int offset) {  
  145.         if (tabCount == 0) {  
  146.             return;  
  147.         }  
  148.   
  149.         calculateIndicatorRect(indicatorRect);  
  150.         int newScrollX = lastScrollX;  
  151.         if (indicatorRect.left < getScrollX() + scrollOffset) {  
  152.             newScrollX = indicatorRect.left - scrollOffset;  
  153.         } else if (indicatorRect.right > getScrollX() + getWidth() - scrollOffset) {  
  154.             newScrollX = indicatorRect.right - getWidth() + scrollOffset;  
  155.         }  
  156.         if (newScrollX != lastScrollX) {  
  157.             lastScrollX = newScrollX;  
  158.             scrollTo(newScrollX, 0);  
  159.         }  
  160.     }  
  161.   
  162.     // 自定义绘图  
  163.     @Override  
  164.     public void draw(Canvas canvas) {  
  165.         super.draw(canvas);  
  166.   
  167.         // 绘制高亮背景矩形红框  
  168.         calculateIndicatorRect(indicatorRect);  
  169.         if(indicator != null) {  
  170.             indicator.setBounds(indicatorRect);  
  171.             indicator.draw(canvas);  
  172.         }  
  173.   
  174.         // 绘制背景红框内标签文本  
  175.         int i = 0;  
  176.         while (i < tabsContainer.getChildCount()) {  
  177.             if (i < currentPosition - 1 || i > currentPosition + 1) {  
  178.                 i++;  
  179.             } else {  
  180.                 ViewGroup tab = (ViewGroup)tabsContainer.getChildAt(i);  
  181.                 TextView category_text = (TextView) tab.findViewById(R.id.category_text);  
  182.                 if (category_text != null) {  
  183.                     TextDrawable textDrawable = drawables[i - currentPosition + 1];  
  184.                     int save = canvas.save();  
  185.                     calculateIndicatorRect(indicatorRect);  
  186.                     canvas.clipRect(indicatorRect);  
  187.                     textDrawable.setText(category_text.getText());  
  188.                     textDrawable.setTextSize(0, category_text.getTextSize());  
  189.                     textDrawable.setTextColor(getResources().getColor(R.color.category_tab_highlight_text));  
  190.                     int left = tab.getLeft() + category_text.getLeft() + (category_text.getWidth() - textDrawable.getIntrinsicWidth()) / 2 + getPaddingLeft();  
  191.                     int top = tab.getTop() + category_text.getTop() + (category_text.getHeight() - textDrawable.getIntrinsicHeight()) / 2 + getPaddingTop();  
  192.                     textDrawable.setBounds(left, top, textDrawable.getIntrinsicWidth() + left, textDrawable.getIntrinsicHeight() + top);  
  193.                     textDrawable.draw(canvas);  
  194.                     canvas.restoreToCount(save);  
  195.                 }  
  196.                 i++;  
  197.             }  
  198.         }  
  199.   
  200.         // 绘制左右边界阴影效果  
  201.         i = canvas.save();  
  202.         int top = getScrollX();  
  203.         int height = getHeight();  
  204.         int width = getWidth();  
  205.         canvas.translate((float) top, 0.0f);  
  206.         if (left_edge == null || top <= 0) {  
  207.             if (right_edge == null || top >= getScrollRange()) {  
  208.                 canvas.restoreToCount(i);  
  209.             }  
  210.             right_edge.setBounds(width - right_edge.getIntrinsicWidth(), 0, width, height);  
  211.             right_edge.draw(canvas);  
  212.             canvas.restoreToCount(i);  
  213.         }  
  214.         left_edge.setBounds(0, 0, left_edge.getIntrinsicWidth(), height);  
  215.         left_edge.draw(canvas);  
  216.         if (right_edge == null || top >= getScrollRange()) {  
  217.             canvas.restoreToCount(i);  
  218.         }  
  219.         right_edge.setBounds(width - right_edge.getIntrinsicWidth(), 0, width, height);  
  220.         right_edge.draw(canvas);  
  221.         canvas.restoreToCount(i);  
  222.     }  
  223.   
  224.     private class PageListener implements OnPageChangeListener {  
  225.         @Override  
  226.         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  227.             currentPosition = position;  
  228.             currentPositionOffset = positionOffset;  
  229.   
  230.             scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));  
  231.             invalidate();  
  232.         }  
  233.   
  234.         @Override  
  235.         public void onPageScrollStateChanged(int state) {  
  236.             if (state == ViewPager.SCROLL_STATE_IDLE) {  
  237.                 if(pager.getCurrentItem() == 0) {  
  238.                     // 滑动到最左边  
  239.                     scrollTo(0, 0);  
  240.                 } else if (pager.getCurrentItem() == tabCount - 1) {  
  241.                     // 滑动到最右边  
  242.                     scrollTo(getScrollRange(), 0);  
  243.                 } else {  
  244.                     scrollToChild(pager.getCurrentItem(), 0);  
  245.                 }  
  246.             }  
  247.         }  
  248.   
  249.         @Override  
  250.         public void onPageSelected(int position) {  
  251.         }  
  252.     }  
  253. }  




完整项目源代码CSDN资源下载 TouTiaoTabDemo

免责声明:文章转载自《Android之仿今日头条顶部导航栏效果》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇从数据库里面取值绑定到Ztreetcpdump移植和使用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

黑马程序员----java基础--String字符串

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 一、String类概述 java中用String类进行描述对字符串进行了对象的封装。这样的好处是可以对字符串这种常见数据进行方便的操作。对象封装后,可以定义N多属性和行为。 String类是final的,也就是说它没有子类。 二、String字符串的特点...

spring mvc实现新增用户

spring mvc实现新增用户 1、先在展示页面(查询出来的结果页)添加一个连接<a href="http://t.zoukankan.com/add">添加</a> 2、在后台添加一个添加的方法,点击添加,是跳转到add.jsp页面,刚过去没数据,应该是get方法请求 @RequestMapping(value="/add",...

.Net程序员学用Oracle系列(9):系统函数(上)

1、字符函数 1.1、字符函数简介 1.2、语法说明及案例 2、数字函数 2.1、数字函数简介 2.2、语法说明及案例 3、日期函数 3.1、日期函数简介 3.2、语法说明及案例 3.3、日期函数补充 4、总结 Oracle 中系统函数特别多,有好几百个,其中大部分函数对开发者而言,似乎永远都用不到,本文将要介绍 Ora...

Assembly中Load, LoadFrom, LoadFile以及AppDomain, Activator类中相应函数的区别

Assembly和AppDomain的一些关于动态加载程序集的函数有些令人头疼,但细细研究后还是可以将他们区分的。 这些函数大致可以分为四类: 第一类:加载到Load Context内 Load Context: Load Context是所有动态加载程序集首选应该被加载到的地方。 它只能加载在AppDomain信息中的ApplicationBase目录...

第十章 内部类1

说明:内部类和组合是完全不同的概念,内部类看起来更像是一种代码隐藏机制。内部类了解外围类,并能与之通信。要创建一个内部类是非常简单的,只要将一个类定义在另一个类的内部即可。 创建内部类对象的方式: 1.如果内部类为公有、静态内部类: packagethink.in.java.test; /*** 一个包裹着公有的、静态的、内部类的外部类 * @aut...

angular写的一个导航栏

ts import { Component } from '@angular/core'; // 定义一个interface interface Menu{ title:string,link:string,id:string } @Component({ selector: 'app-root', templateUrl: './app.co...