我是靠谱客的博主 朴素小霸王,这篇文章主要介绍Android自定义控件之实现类似文件夹顶部的层层显示的横栏效果,现在分享给大家,希望可以做个参考。

目的概述

要实现的控件效果如图(本图来自小米3)
文件夹层层显示的横栏效果图

复制代码
1
2
3
如果说大家之前实现过的这种控件的,非常希望能和大家交流学习一下。 或者说如果大家知道类似这种效果的开源控件,也非常希望能够告知一下。谢谢!!

思路演化

  • 先讲一下我一开始的思路
    1. 一开始我的想法是定义一个LinearLayout,不断在里面添加TextView的控件,在onlayout()方法中进行TextView的布局,并滑动到最尾端
    2. 滑动的话,然后通过Scroller实现滚动,当然滚动效果不是很好
    3. TextView点击事件,因为与滑动冲突,通过GestureDetector很好解决了这两者的关系
  • 但是上面这种的效果不是很好,而且还涉及到了布局边缘位置的判断 ,处理得也不是很好。接下来讲一下我现在的实现方式,比较简单,主要采用了HorizontalScrollView布局实现
    1. 通过向HorizontalScrollView添加TextView,并滑动到最尾端
    2. 由于采用HorizontalScrollView,滑动与点击事件之间的冲突已经被解决,也不用进行边缘判断
  • 进一步改造,实现第一个标签不动,只是其余标签滑动的效果。看了一下小米对于这个布局的实现即可明白,在最外层添加一个RelativeLayout,子布局为HorizontalScrollView,并添加第一个布局标签固定在左边遮挡住HorizontalScrollView的第一个标签即可

  • 最后补充一点关于标签TextView右边箭头的实现,通过自定义一个继承TextView的控件,并在onDraw()方法中画出这样一条路径

代码实现

复制代码
1
2
通过上面思路的讲解,下面展现实现的代码及效果图
  • 标签TabView
复制代码
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
public class TabView extends TextView { /** * 背景颜色 */ private int mBgColor; /** * 线段颜色 */ private int mLineColor; /** * 画笔 */ private Paint mPaint; /** * 路径 */ private Path mPath; @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); // 清除上次路径 mPaint.setColor(mBgColor); canvas.drawPath(mPath, mPaint); mPaint.setColor(mLineColor); mPath.reset(); mPath.moveTo(getWidth() - 20 - getHeight() / 2, 0); mPath.lineTo(getWidth() - 20, getHeight() / 2); mPath.lineTo(getWidth() - 20 - getHeight() / 2, getHeight()); canvas.drawPath(mPath, mPaint); } public void setTab(String tab) { setText(tab); } /** * 初始化属性 */ private void init() { setGravity(Gravity.CENTER_VERTICAL); setMaxLines(1); mBgColor = getContext().getResources().getColor( R.color.TabBackGroundColor); mLineColor = getContext().getResources().getColor(R.color.TabLineColor); setBackgroundColor(mBgColor); mPath = new Path(); mPaint = new Paint(); mPaint.setStrokeWidth(2); mPaint.setStyle(Paint.Style.STROKE); } public TabView(Context context) { this(context, null); } public TabView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TabView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } }
  • 布局FlowLayout
复制代码
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
public class FlowLayout extends RelativeLayout implements View.OnClickListener { /** * Tab集合 */ private List<String> mTabs; /** * 存放Tab的横向HorizontalScrollView */ private HorizontalScrollView mHsv; /** * 横向HorizontalScrollView的子布局 */ private LinearLayout mHsvInnerLayout; /** * 添加一个新的标签 * * @param text */ public void addTab(String text) { mTabs.add(text); if (getHsvInnerLayoutChildCount() == 0) { // 为什么add的TextView的text没有居中,因为在addview后,高度为最高,再后来addview后,高度增加 // 添加了该tv,导致scrolleView最后面的View显示不全 addView(getTabView(text)); } mHsvInnerLayout.addView(getTabView(text)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // 滑到尾端 mHsv.smoothScrollTo(mHsvInnerLayout.getWidth(), 0); } /** * 获取mHsvInnerLayout的子View数量 * * @return */ private int getHsvInnerLayoutChildCount() { return mHsvInnerLayout.getChildCount(); } /** * 根据tab生产View * * @param tab * @return */ private View getTabView(String tab) { View view = LayoutInflater.from(getContext()).inflate( R.layout.item_file_tag, this, false); TabView tv = (TabView) view.findViewById(R.id.item_tag); tv.setTab(tab); tv.setTag(tab); // 用于点击事件的判断 ((TextView) tv).setTextColor(getTabColorStateList()); tv.setOnClickListener(this); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); view.setLayoutParams(params); return view; } private void init() { mTabs = new ArrayList<String>(); // 设置子view垂直方向上居中 setGravity(Gravity.CENTER_HORIZONTAL); setBackgroundColor(getResources().getColor(R.color.TabBackGroundColor)); initHSV(); } /** * 初始化HorizontalScrollView */ private void initHSV() { mHsv = new HorizontalScrollView(getContext()); // 隐藏滚动条 mHsv.setHorizontalScrollBarEnabled(false); LayoutParams mHsvParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mHsvParams.rightMargin = 50; mHsvInnerLayout = new LinearLayout(getContext()); LayoutParams mHsvInnerLayoutParams = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); mHsvInnerLayout.setGravity(Gravity.CENTER_VERTICAL); // 添加子View mHsv.addView(mHsvInnerLayout, mHsvInnerLayoutParams); addView(mHsv, mHsvParams); } public FlowLayout(Context context) { this(context, null); } public FlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } /** * 获取ColorStateList对象 * * @return */ private ColorStateList getTabColorStateList() { ColorStateList csl = new ColorStateList(new int[][] { { android.R.attr.state_pressed }, { -android.R.attr.state_pressed } }, new int[] { Color.BLACK, Color.GRAY }); return csl; } private onTabClickListener mOnTabClickListener; /** * tab点击事件接口 */ public interface onTabClickListener { void onTabClick(String tab); } public void setOnTabClickListener(onTabClickListener l) { this.mOnTabClickListener = l; } @Override public void onClick(View v) { String tab = (String) v.getTag(); if (tab == null) return; if (mOnTabClickListener != null) { mOnTabClickListener.onTabClick(tab); } // 移除后面Tab int index = mTabs.indexOf(tab); int size = mTabs.size(); // 不作处理 if (index == -1 || index == size - 1) { return; } for (int i = size - 1; i > index; i--) { mHsvInnerLayout.removeViewAt(i); mTabs.remove(i); } } }

效果

最终效果

  • Demo下载地址

总结

  • 总体来说没什么难度,但是还是值得学习记录一下过程思想

最后

  • 最近可能开始要忙了,不过最近在学习Android开源框架Volley,所以接下来将会对记录一下我对Volley的理解。对Volley的用法,源码,设计模式,HTTP,自定义异常等方面做个记录

  • 最后呢,如果你有其他思路或者开源控件,非常期待你的分享,谢谢

  • 如果本篇文章有哪里错误不足之处,或者哪方面可以做更好的封装,也希望能够指出,一起探讨学习。

最后

以上就是朴素小霸王最近收集整理的关于Android自定义控件之实现类似文件夹顶部的层层显示的横栏效果的全部内容,更多相关Android自定义控件之实现类似文件夹顶部内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部