zoukankan      html  css  js  c++  java
  • Android之仿今日头条顶部导航栏效果

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

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

    先看一下实现的效果对比:

    顶部导航栏区域和ViewPager区域View层次结构


    主界面布局

    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

  • 相关阅读:
    memcached +mysql+php 例子
    PHP利用memcache缓存技术提高响应速度
    实现QQ第三方登录教程(php)
    php如何解决多线程同时读写一个文件的问题
    php数组函数常见的那些
    PHP 5种方式获取文件后缀名
    函数与方程
    函数图像习题
    高中数学中需要重点关注的函数和图像
    特殊分段函数的图像画法
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/7797618.html
Copyright © 2011-2022 走看看