Android仿今日头条顶部导航栏效果的实例代码

时间:2022-06-02 14:44:51

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

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

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

Android仿今日头条顶部导航栏效果的实例代码

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

Android仿今日头条顶部导航栏效果的实例代码

主界面布局

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

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

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

CatagoryTabStrip控件的实现代码

CategoryTabStrip.java

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

完整项目源代码的资源下载 TouTiaoTabDemo.

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:https://blog.csdn.net/hantangsongming/article/details/42455219