44 private List<LevelInfo> floors =
new ArrayList<>();
45 private int selectedFloorIndex = -1;
47 private RecyclerView recyclerView;
48 private LinearLayoutManager layoutManager;
49 private FloorAdapter adapter;
51 private Button topButton;
52 private Button bottomButton;
59 this(context, onSublocationSelected,
null);
66 this.onSublocationSelectedListener = onSublocationSelected;
72 setBackgroundColor(Color.TRANSPARENT);
73 setClipChildren(
false);
74 setClipToPadding(
false);
76 GradientDrawable bg =
new GradientDrawable();
78 bg.setCornerRadius(radius);
79 bg.setColor(Color.WHITE);
82 setClipToOutline(
true);
85 recyclerView =
new RecyclerView(getContext());
86 recyclerView.setHasFixedSize(
true);
87 layoutManager =
new LinearLayoutManager(getContext());
88 recyclerView.setLayoutManager(layoutManager);
89 adapter =
new FloorAdapter();
90 recyclerView.setAdapter(adapter);
91 recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
92 recyclerView.setScrollbarFadingEnabled(
true);
93 recyclerView.addOnScrollListener(
new RecyclerView.OnScrollListener() {
95 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
96 updateButtonVisibility();
100 recyclerView.addItemDecoration(
new DividerDecoration());
102 addView(recyclerView);
104 topButton = createArrowButton(
"▲",
true);
105 bottomButton = createArrowButton(
"▼",
false);
107 topButton.setOnClickListener(v -> scrollUp());
108 bottomButton.setOnClickListener(v -> scrollDown());
110 topButton.setVisibility(GONE);
111 bottomButton.setVisibility(GONE);
114 addView(bottomButton);
117 private Button createArrowButton(String text,
boolean isTop) {
118 Button button =
new Button(getContext());
119 button.setText(text);
120 button.setTextSize(WidgetStyles.SCROLL_BUTTON_FONT_SIZE);
121 button.setTextColor(WidgetStyles.BASE_BLACK);
122 button.setTypeface(
null, Typeface.BOLD);
123 button.setAllCaps(
false);
125 GradientDrawable drawable =
new GradientDrawable();
126 float radius = dpToPx((
int) WidgetStyles.BORDER_RADIUS);
128 drawable.setCornerRadii(
new float[]{
135 drawable.setCornerRadii(
new float[]{
144 button.setBackground(drawable);
146 LayoutParams lp =
new LayoutParams(
147 ViewGroup.LayoutParams.MATCH_PARENT,
148 ViewGroup.LayoutParams.WRAP_CONTENT
150 lp.gravity = isTop ? Gravity.TOP : Gravity.BOTTOM;
151 button.setLayoutParams(lp);
158 return floors.size();
164 if (adapter !=
null) {
165 adapter.notifyDataSetChanged();
174 if (newFloors !=
null) floors.addAll(newFloors);
176 if (selectedFloorIndex < 0 || selectedFloorIndex >= this.floors.size()) {
177 selectedFloorIndex = this.floors.isEmpty() ? -1 : 0;
180 adapter.notifyDataSetChanged();
181 recyclerView.scrollToPosition(selectedFloorIndex >= 0 ? selectedFloorIndex : 0);
182 updateButtonVisibility();
189 int newSelectedIndex = -1;
191 for (
int i = 0; i < floors.size(); i++) {
192 if (floors.get(i).getSublocationId() == sublocationId) {
193 newSelectedIndex = i;
198 if (newSelectedIndex == -1) {
202 if (selectedFloorIndex == newSelectedIndex) {
203 adapter.notifyDataSetChanged();
204 updateButtonVisibility();
208 selectedFloorIndex = newSelectedIndex;
209 adapter.notifyDataSetChanged();
212 recyclerView.smoothScrollToPosition(selectedFloorIndex);
214 layoutManager.scrollToPositionWithOffset(selectedFloorIndex, 0);
217 recyclerView.post(this::updateButtonVisibility);
220 private void updateFrame() {
221 if (floors.isEmpty() || floors.size() == 1) {
224 setVisibility(VISIBLE);
227 post(this::updateButtonPositions);
230 private int dpToPx(
float dp) {
231 return (
int) (dp * getResources().getDisplayMetrics().density + 0.5f);
235 protected void onMeasure(
int widthMeasureSpec,
int heightMeasureSpec) {
238 int count = floors.size();
247 desiredHeight = rowHeight * count;
252 int width = resolveSize(desiredWidth, widthMeasureSpec);
253 int height = resolveSize(desiredHeight, heightMeasureSpec);
255 setMeasuredDimension(width, height);
257 int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
258 int childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
260 recyclerView.measure(childWidthSpec, childHeightSpec);
262 int arrowHeightSpec = MeasureSpec.makeMeasureSpec(rowHeight, MeasureSpec.EXACTLY);
263 topButton.measure(childWidthSpec, arrowHeightSpec);
264 bottomButton.measure(childWidthSpec, arrowHeightSpec);
268 protected void onLayout(
boolean changed,
int left,
int top,
int right,
int bottom) {
269 int width = right - left;
270 int height = bottom - top;
272 if (floors.isEmpty() || floors.size() == 1) {
276 recyclerView.layout(0, 0, width, height);
279 topButton.layout(0, 0, width, rowHeight);
280 bottomButton.layout(0, height - rowHeight, width, height);
283 private void updateButtonPositions() {
284 if (getHeight() <= 0)
return;
286 topButton.layout(0, 0, getWidth(), rowHeight);
287 bottomButton.layout(0, getHeight() - rowHeight, getWidth(), getHeight());
290 private void updateButtonVisibility() {
291 if (floors.size() <= 1 || floors.size() <= WidgetStyles.MAX_VISIBLE_FLOORS ) {
292 topButton.setVisibility(GONE);
293 bottomButton.setVisibility(GONE);
297 int firstVisible = layoutManager.findFirstCompletelyVisibleItemPosition();
298 int lastVisible = layoutManager.findLastCompletelyVisibleItemPosition();
300 if (firstVisible == RecyclerView.NO_POSITION || lastVisible == RecyclerView.NO_POSITION) {
301 topButton.setVisibility(GONE);
302 bottomButton.setVisibility(GONE);
306 boolean canScrollUp = firstVisible > 0;
307 boolean canScrollDown = lastVisible < floors.size() - 1;
309 topButton.setVisibility(canScrollUp ? VISIBLE : GONE);
310 bottomButton.setVisibility(canScrollDown ? VISIBLE : GONE);
313 private void scrollUp() {
314 int firstVisible = layoutManager.findFirstVisibleItemPosition();
315 if (firstVisible == RecyclerView.NO_POSITION)
return;
316 int target = Math.max(0, firstVisible - WidgetStyles.SCROLL_ITEMS_COUNT);
317 recyclerView.smoothScrollToPosition(target);
320 private void scrollDown() {
321 int lastVisible = layoutManager.findLastVisibleItemPosition();
322 if (lastVisible == RecyclerView.NO_POSITION)
return;
324 recyclerView.smoothScrollToPosition(target);
327 private class FloorAdapter
extends RecyclerView.Adapter<FloorAdapter.ViewHolder> {
330 public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
int viewType) {
331 TextView textView =
new TextView(getContext());
332 textView.setGravity(Gravity.CENTER);
333 textView.setTextSize(WidgetStyles.FLOOR_SELECTOR_FONT_SIZE);
334 int horizontalPadding = dpToPx((
int) WidgetStyles.FLOOR_SELECTOR_HORIZONTAL_PADDING);
335 int verticalPadding = dpToPx((
int) WidgetStyles.FLOOR_SELECTOR_VERTICAL_PADDING);
336 textView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
338 RecyclerView.LayoutParams lp =
new RecyclerView.LayoutParams(
339 ViewGroup.LayoutParams.MATCH_PARENT,
340 dpToPx((
int) WidgetStyles.FLOOR_ROW_HEIGHT)
342 textView.setLayoutParams(lp);
344 return new ViewHolder(textView);
348 public void onBindViewHolder(@NonNull ViewHolder holder,
int position) {
349 LevelInfo info = floors.get(position);
350 String displayText = truncateLevelId(info.getLevelId());
351 holder.textView.setText(displayText);
353 boolean isSelected = (position == selectedFloorIndex);
354 FloorSelectorViewConfig c = config !=
null ? config : FloorSelectorViewConfig.
defaultConfig();
355 int accentColor = c.getAccentColor() !=
null ? c.getAccentColor() : WidgetStyles.BASE_BLUE;
356 int textColor = c.getTextColor() !=
null ? c.getTextColor() : WidgetStyles.BASE_BLACK;
357 holder.textView.setBackgroundColor(isSelected ? accentColor : Color.WHITE);
358 holder.textView.setTextColor(isSelected ? Color.WHITE : textColor);
362 public int getItemCount() {
363 return floors.size();
366 class ViewHolder
extends RecyclerView.ViewHolder {
369 ViewHolder(@NonNull View itemView) {
371 textView = (TextView) itemView;
372 textView.setOnClickListener(v -> {
373 int pos = getAdapterPosition();
374 if (pos == RecyclerView.NO_POSITION || pos == selectedFloorIndex)
return;
376 selectedFloorIndex = pos;
377 notifyDataSetChanged();
379 LevelInfo info = floors.get(pos);
380 if (onSublocationSelectedListener !=
null) {
388 private class DividerDecoration
extends RecyclerView.ItemDecoration {
390 private final Paint paint =
new Paint();
391 private final int heightPx;
393 DividerDecoration() {
394 paint.setAntiAlias(
true);
395 paint.setColor(WidgetStyles.DIVIDER_COLOR);
396 heightPx = dpToPx((
int) WidgetStyles.DIVIDER_HEIGHT);
400 public void onDrawOver(@NonNull Canvas c,
401 @NonNull RecyclerView parent,
402 @NonNull RecyclerView.State state) {
403 int childCount = parent.getChildCount();
405 int right = parent.getWidth();
407 for (
int i = 0; i < childCount - 1; i++) {
408 View child = parent.getChildAt(i);
409 float top = child.getBottom();
410 float bottom = top + heightPx;
411 c.drawRect(left, top, right, bottom, paint);
415 private String truncateLevelId(String levelId) {
416 if (levelId ==
null)
return "";
417 if (levelId.length() <= 5)
return levelId;
418 return levelId.substring(0, 5) +
"...";
425 private final String levelId;
426 private final int sublocationId;
434 this.levelId = levelId;
435 this.sublocationId = sublocationId;
449 return sublocationId;