From b57dae57aa0ef31577cbd60bb08f030ae5b1618f Mon Sep 17 00:00:00 2001 From: rightnao Date: Fri, 12 May 2023 21:59:34 +0000 Subject: [PATCH] [Carousel] Add Carousel Hero strategy demo PiperOrigin-RevId: 531612383 --- .../catalog/carousel/CarouselFragment.java | 8 +- .../carousel/HeroCarouselDemoFragment.java | 155 ++++++++++++++++++ ...a => MultiBrowseCarouselDemoFragment.java} | 2 +- .../res/layout/cat_carousel_hero_fragment.xml | 119 ++++++++++++++ .../catalog/carousel/res/values/strings.xml | 6 + 5 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 catalog/java/io/material/catalog/carousel/HeroCarouselDemoFragment.java rename catalog/java/io/material/catalog/carousel/{MultiBrowseDemoFragment.java => MultiBrowseCarouselDemoFragment.java} (98%) create mode 100644 catalog/java/io/material/catalog/carousel/res/layout/cat_carousel_hero_fragment.xml diff --git a/catalog/java/io/material/catalog/carousel/CarouselFragment.java b/catalog/java/io/material/catalog/carousel/CarouselFragment.java index fdaa5d471ea..09d8b77efee 100644 --- a/catalog/java/io/material/catalog/carousel/CarouselFragment.java +++ b/catalog/java/io/material/catalog/carousel/CarouselFragment.java @@ -64,7 +64,13 @@ public List getAdditionalDemos() { new Demo(R.string.cat_carousel_multi_browse_demo_title) { @Override public Fragment createFragment() { - return new MultiBrowseDemoFragment(); + return new MultiBrowseCarouselDemoFragment(); + } + }, + new Demo(R.string.cat_carousel_hero_demo_title) { + @Override + public Fragment createFragment() { + return new HeroCarouselDemoFragment(); } }, new Demo(R.string.cat_carousel_default_list_demo_title) { diff --git a/catalog/java/io/material/catalog/carousel/HeroCarouselDemoFragment.java b/catalog/java/io/material/catalog/carousel/HeroCarouselDemoFragment.java new file mode 100644 index 00000000000..01d42bd63aa --- /dev/null +++ b/catalog/java/io/material/catalog/carousel/HeroCarouselDemoFragment.java @@ -0,0 +1,155 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.material.catalog.carousel; + +import io.material.catalog.R; + +import android.os.Bundle; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.SnapHelper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AutoCompleteTextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.carousel.CarouselLayoutManager; +import com.google.android.material.carousel.CarouselSnapHelper; +import com.google.android.material.carousel.HeroCarouselStrategy; +import com.google.android.material.divider.MaterialDividerItemDecoration; +import com.google.android.material.materialswitch.MaterialSwitch; +import com.google.android.material.slider.Slider; +import com.google.android.material.slider.Slider.OnSliderTouchListener; +import io.material.catalog.feature.DemoFragment; + +/** A fragment that displays the hero variant of the Carousel. */ +public class HeroCarouselDemoFragment extends DemoFragment { + + private MaterialDividerItemDecoration horizontalDivider; + + @NonNull + @Override + public View onCreateDemoView( + @NonNull LayoutInflater layoutInflater, + @Nullable ViewGroup viewGroup, + @Nullable Bundle bundle) { + return layoutInflater.inflate( + R.layout.cat_carousel_hero_fragment, viewGroup, false /* attachToRoot */); + } + + @Override + @SuppressWarnings("RestrictTo") + public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) { + super.onViewCreated(view, bundle); + + horizontalDivider = + new MaterialDividerItemDecoration( + requireContext(), MaterialDividerItemDecoration.HORIZONTAL); + + MaterialSwitch debugSwitch = view.findViewById(R.id.debug_switch); + MaterialSwitch drawDividers = view.findViewById(R.id.draw_dividers_switch); + MaterialSwitch enableFlingSwitch = view.findViewById(R.id.enable_fling_switch); + AutoCompleteTextView itemCountDropdown = view.findViewById(R.id.item_count_dropdown); + Slider positionSlider = view.findViewById(R.id.position_slider); + + // A start-aligned hero carousel + RecyclerView heroStartRecyclerView = + view.findViewById(R.id.hero_start_carousel_recycler_view); + CarouselLayoutManager heroStartCarouselLayoutManager = + new CarouselLayoutManager(new HeroCarouselStrategy()); + heroStartCarouselLayoutManager.setDebuggingEnabled( + heroStartRecyclerView, debugSwitch.isChecked()); + heroStartRecyclerView.setLayoutManager(heroStartCarouselLayoutManager); + heroStartRecyclerView.setNestedScrollingEnabled(false); + + debugSwitch.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + heroStartRecyclerView.setBackgroundResource( + isChecked ? R.drawable.dashed_outline_rectangle : 0); + heroStartCarouselLayoutManager.setDebuggingEnabled( + heroStartRecyclerView, isChecked); + }); + + drawDividers.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + if (isChecked) { + heroStartRecyclerView.addItemDecoration(horizontalDivider); + } else { + heroStartRecyclerView.removeItemDecoration(horizontalDivider); + } + }); + + CarouselAdapter adapter = + new CarouselAdapter( + (item, position) -> heroStartRecyclerView.scrollToPosition(position), + R.layout.cat_carousel_item); + + SnapHelper disableFlingSnapHelper = new CarouselSnapHelper(); + SnapHelper enableFlingSnapHelper = new CarouselSnapHelper(false); + + if (enableFlingSwitch.isChecked()) { + enableFlingSnapHelper.attachToRecyclerView(heroStartRecyclerView); + } else { + disableFlingSnapHelper.attachToRecyclerView(heroStartRecyclerView); + } + + enableFlingSwitch.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + if (isChecked) { + disableFlingSnapHelper.attachToRecyclerView(null); + enableFlingSnapHelper.attachToRecyclerView(heroStartRecyclerView); + } else { + enableFlingSnapHelper.attachToRecyclerView(null); + disableFlingSnapHelper.attachToRecyclerView(heroStartRecyclerView); + } + }); + + itemCountDropdown.setOnItemClickListener( + (parent, view1, position, id) -> + adapter.submitList( + CarouselData.createItems().subList(0, position), + updateSliderRange(positionSlider, adapter))); + + positionSlider.addOnSliderTouchListener( + new OnSliderTouchListener() { + @Override + public void onStartTrackingTouch(@NonNull Slider slider) {} + + @Override + public void onStopTrackingTouch(@NonNull Slider slider) { + heroStartRecyclerView.smoothScrollToPosition((int) slider.getValue() - 1); + } + }); + + heroStartRecyclerView.setAdapter(adapter); + adapter.submitList(CarouselData.createItems(), updateSliderRange(positionSlider, adapter)); + } + + private static Runnable updateSliderRange(Slider slider, CarouselAdapter adapter) { + return () -> { + if (adapter.getItemCount() <= 1) { + slider.setEnabled(false); + return; + } + + slider.setValueFrom(1); + slider.setValue(1); + slider.setValueTo(adapter.getItemCount()); + slider.setEnabled(true); + }; + } +} diff --git a/catalog/java/io/material/catalog/carousel/MultiBrowseDemoFragment.java b/catalog/java/io/material/catalog/carousel/MultiBrowseCarouselDemoFragment.java similarity index 98% rename from catalog/java/io/material/catalog/carousel/MultiBrowseDemoFragment.java rename to catalog/java/io/material/catalog/carousel/MultiBrowseCarouselDemoFragment.java index 049b5f8918d..8deb1f79a50 100644 --- a/catalog/java/io/material/catalog/carousel/MultiBrowseDemoFragment.java +++ b/catalog/java/io/material/catalog/carousel/MultiBrowseCarouselDemoFragment.java @@ -36,7 +36,7 @@ import io.material.catalog.feature.DemoFragment; /** A fragment that displays the multi-browse variants of the Carousel. */ -public class MultiBrowseDemoFragment extends DemoFragment { +public class MultiBrowseCarouselDemoFragment extends DemoFragment { private MaterialDividerItemDecoration horizontalDivider; diff --git a/catalog/java/io/material/catalog/carousel/res/layout/cat_carousel_hero_fragment.xml b/catalog/java/io/material/catalog/carousel/res/layout/cat_carousel_hero_fragment.xml new file mode 100644 index 00000000000..2f2e7c65d9d --- /dev/null +++ b/catalog/java/io/material/catalog/carousel/res/layout/cat_carousel_hero_fragment.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/catalog/java/io/material/catalog/carousel/res/values/strings.xml b/catalog/java/io/material/catalog/carousel/res/values/strings.xml index dfea17ca7da..4b610cd1a7b 100644 --- a/catalog/java/io/material/catalog/carousel/res/values/strings.xml +++ b/catalog/java/io/material/catalog/carousel/res/values/strings.xml @@ -20,6 +20,8 @@ Force compact arrangement Draw dividers Enable Snap + Enable fling + Item count Scroll position Enable snapping to carousel items @@ -44,11 +46,15 @@ Carousels are stylized versions of lists that provide a unique viewing and behavior that suit large imagery and other visually rich content. Multi-browse Carousel + Hero Carousel Default List Multi-browse Multi-browse carousels allow quick browsing of many small items, like a photo thumbnail gallery. + Hero + Hero carousels allow browsing with a focus on one main item at a time. + Default A default horizontal list.