我是靠谱客的博主 花痴山水,这篇文章主要介绍Android实现小米相机底部滑动指示器,现在分享给大家,希望可以做个参考。

近期工作内容需要涉及到相机开发,其中一个功能点就是实现一个相机预览页底部的滑动指示器,现在整理出来供大家讨论参考。

先上一张图看下效果:

主要实现功能有:

1.支持左右滑动,每次滑动一个tab

2.支持tab点击,直接跳到对应tab

3.选中的tab一直处于居中位置

4.支持部分UI自定义(大家可根据需要自己改动)

5.tab点击回调

6.内置Tab接口,放入的内容需要实现Tab接口

7.设置预选中tab

复制代码
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
public class CameraIndicator extends LinearLayout { // 当前选中的位置索引 private int currentIndex; //tabs集合 private Tab[] tabs; // 利用Scroller类实现最终的滑动效果 public Scroller mScroller; //滑动执行时间(ms) private int mDuration = 300; //选中text的颜色 private int selectedTextColor = 0xffffffff; //未选中的text的颜色 private int normalTextColor = 0xffffffff; //选中的text的背景 private Drawable selectedTextBackgroundDrawable; private int selectedTextBackgroundColor; private int selectedTextBackgroundResources; //是否正在滑动 private boolean isScrolling = false; private int onLayoutCount = 0; public CameraIndicator(Context context) { this(context, null); } public CameraIndicator(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CameraIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScroller = new Scroller(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //测量所有子元素 measureChildren(widthMeasureSpec, heightMeasureSpec); //处理wrap_content的情况 int width = 0; int height = 0; if (getChildCount() == 0) { setMeasuredDimension(0, 0); } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); width += child.getMeasuredWidth(); height = Math.max(height, child.getMeasuredHeight()); } setMeasuredDimension(width, height); } else if (widthMode == MeasureSpec.AT_MOST) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); width += child.getMeasuredWidth(); } setMeasuredDimension(width, heightSize); } else if (heightMode == MeasureSpec.AT_MOST) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); height = Math.max(height, child.getMeasuredHeight()); } setMeasuredDimension(widthSize, height); } else { //如果自定义ViewGroup之初就已确认该ViewGroup宽高都是match_parent,那么直接设置即可 setMeasuredDimension(widthSize, heightSize); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //给选中text的添加背景会多次进入onLayout,会导致位置有问题,暂未解决 if (onLayoutCount > 0) { return; } onLayoutCount++; int counts = getChildCount(); int childLeft = 0; int childRight = 0; int childTop = 0; int childBottom = 0; //居中显示 int widthOffset = 0; //计算最左边的子view距离中心的距离 for (int i = 0; i < currentIndex; i++) { View childView = getChildAt(i); widthOffset += childView.getMeasuredWidth() + getMargins(childView).get(0)+getMargins(childView).get(2); } //计算出每个子view的位置 for (int i = 0; i < counts; i++) { View childView = getChildAt(i); childView.setOnClickListener(v -> moveTo(v)); if (i != 0) { View preView = getChildAt(i - 1); childLeft = preView.getRight() +getMargins(preView).get(2)+ getMargins(childView).get(0); } else { childLeft = (getWidth() - getChildAt(currentIndex).getMeasuredWidth()) / 2 - widthOffset; } childRight = childLeft + childView.getMeasuredWidth(); childTop = (getHeight() - childView.getMeasuredHeight()) / 2; childBottom = (getHeight() + childView.getMeasuredHeight()) / 2; childView.layout(childLeft, childTop, childRight, childBottom); } TextView indexText = (TextView) getChildAt(currentIndex); changeSelectedUIState(indexText); } private List<Integer> getMargins(View view) { LayoutParams params = (LayoutParams) view.getLayoutParams(); List<Integer> listMargin = new ArrayList<Integer>(); listMargin.add(params.leftMargin); listMargin.add(params.topMargin); listMargin.add(params.rightMargin); listMargin.add(params.bottomMargin); return listMargin; } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { // 滑动未结束,内部使用scrollTo方法完成实际滑动 scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } else { //滑动完成 isScrolling = false; if (listener != null) { listener.onChange(currentIndex,tabs[currentIndex]); } } super.computeScroll(); } /** * 改变选中TextView的颜色 * * @param currentIndex 滑动之前选中的那个 * @param nextIndex 滑动之后选中的那个 */ public final void scrollToNext(int currentIndex, int nextIndex) { TextView selectedText = (TextView) getChildAt(currentIndex); if (selectedText != null) { selectedText.setTextColor(normalTextColor); selectedText.setBackground(null); } selectedText = (TextView) getChildAt(nextIndex); if (selectedText != null) { changeSelectedUIState(selectedText); } } private void changeSelectedUIState(TextView view) { view.setTextColor(selectedTextColor); if (selectedTextBackgroundDrawable != null) { view.setBackground(selectedTextBackgroundDrawable); } if (selectedTextBackgroundColor != 0) { view.setBackgroundColor(selectedTextBackgroundColor); } if (selectedTextBackgroundResources != 0) { view.setBackgroundResource(selectedTextBackgroundResources); } } /** * 向右滑一个 */ public void moveToRight() { moveTo(getChildAt(currentIndex - 1)); } /** * 向左滑一个 */ public void moveToLeft() { moveTo(getChildAt(currentIndex + 1)); } /** * 滑到目标view * * @param view 目标view */ private void moveTo(View view) { for (int i = 0; i < getChildCount(); i++) { if (view == getChildAt(i)) { if (i == currentIndex) { //不移动 break; } else if (i < currentIndex) { //向右移 if (isScrolling) { return; } isScrolling = true; int dx = getChildAt(currentIndex).getLeft() - view.getLeft() + (getChildAt(currentIndex).getMeasuredWidth() - view.getMeasuredWidth()) / 2; //这里使用scroll会使滑动更平滑不卡顿,scroll会根据起点、终点及时间计算出每次滑动的距离,其内部有一个插值器 mScroller.startScroll(getScrollX(), 0, -dx, 0, mDuration); scrollToNext(currentIndex, i); setCurrentIndex(i); invalidate(); } else if (i > currentIndex) { //向左移 if (isScrolling) { return; } isScrolling = true; int dx = view.getLeft() - getChildAt(currentIndex).getLeft() + (view.getMeasuredWidth() - getChildAt(currentIndex).getMeasuredWidth()) / 2; mScroller.startScroll(getScrollX(), 0, dx, 0, mDuration); scrollToNext(currentIndex, i); setCurrentIndex(i); invalidate(); } } } } /** * 设置tabs * * @param tabs */ public void setTabs(Tab... tabs) { this.tabs = tabs; //暂时不通过layout布局添加textview if (getChildCount()>0){ removeAllViews(); } for (Tab tab : tabs) { TextView textView = new TextView(getContext()); textView.setText(tab.getText()); textView.setTextSize(14); textView.setTextColor(selectedTextColor); textView.setPadding(dp2px(getContext(),5), dp2px(getContext(),2), dp2px(getContext(),5),dp2px(getContext(),2)); LayoutParams layoutParams= new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); layoutParams.rightMargin=dp2px(getContext(),2.5f); layoutParams.leftMargin=dp2px(getContext(),2.5f); textView.setLayoutParams(layoutParams); addView(textView); } } public int getCurrentIndex() { return currentIndex; } //设置默认选中第几个 public void setCurrentIndex(int currentIndex) { this.currentIndex = currentIndex; } //设置滑动时间 public void setDuration(int mDuration) { this.mDuration = mDuration; } public void setSelectedTextColor(int selectedTextColor) { this.selectedTextColor = selectedTextColor; } public void setNormalTextColor(int normalTextColor) { this.normalTextColor = normalTextColor; } public void setSelectedTextBackgroundDrawable(Drawable selectedTextBackgroundDrawable) { this.selectedTextBackgroundDrawable = selectedTextBackgroundDrawable; } public void setSelectedTextBackgroundColor(int selectedTextBackgroundColor) { this.selectedTextBackgroundColor = selectedTextBackgroundColor; } public void setSelectedTextBackgroundResources(int selectedTextBackgroundResources) { this.selectedTextBackgroundResources = selectedTextBackgroundResources; } public interface OnSelectedChangedListener { void onChange(int index, Tab tag); } private OnSelectedChangedListener listener; public void setOnSelectedChangedListener(OnSelectedChangedListener listener) { if (listener != null) { this.listener = listener; } } private int dp2px(Context context, float dpValue) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return (int) (metrics.density * dpValue + 0.5F); } public interface Tab{ String getText(); } private float startX = 0f; @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { startX = event.getX(); } if (event.getAction() == MotionEvent.ACTION_UP) { float endX = event.getX(); //向左滑条件 if (endX - startX > 50 && currentIndex > 0) { moveToRight(); } if (startX - endX > 50 && currentIndex < getChildCount() - 1) { moveToLeft(); } } return true; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { startX = event.getX(); } if (event.getAction() == MotionEvent.ACTION_UP) { float endX = event.getX(); //向左滑条件 if (Math.abs(startX-endX)>50){ onTouchEvent(event); } } return super.onInterceptTouchEvent(event); } }

在Activity或fragment中使用

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private var tabs = listOf("慢动作", "短视频", "录像", "拍照", "108M", "人像", "夜景", "萌拍", "全景", "专业") lateinit var imageAnalysis:ImageAnalysis override fun initView() { //实现了CameraIndicator.Tab的对象 val map = tabs.map { CameraIndicator.Tab { it } }?.toTypedArray() ?: arrayOf() //将tab集合设置给cameraIndicator,(binding.cameraIndicator即xml布局里的控件) binding.cameraIndicator.setTabs(*map) //默认选中 拍照 binding.cameraIndicator.currentIndex = 3 //点击某个tab的回调 binding.cameraIndicator.setSelectedTextBackgroundResources(R.drawable.selected_text_bg) binding.cameraIndicator.setOnSelectedChangedListener { index, tag -> Toast.makeText(this,tag.text,Toast.LENGTH_SHORT).show() } }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持靠谱客。

最后

以上就是花痴山水最近收集整理的关于Android实现小米相机底部滑动指示器的全部内容,更多相关Android实现小米相机底部滑动指示器内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部