Розгорнути / Згорнути анімацію панелі інструментів Lollipop (додаток Telegram)


76

Я намагаюся зрозуміти, як виконується анімація розгортання / згортання панелі інструментів. Якщо ви заглянете в налаштування програми Telegram, то побачите, що є перегляд списку та панель інструментів. При прокрутці вниз панель інструментів згортається, а при прокручуванні вгору розширюється. Існує також анімація профілю профілю та FAB. Хтось знає про це? Думаєте, вони створили всю анімацію поверх цього? Можливо, мені щось не вистачає в нових API або бібліотеці підтримки.

Я помітив таку ж поведінку в програмі календаря Google, коли ви відкриваєте Spinner (я не думаю, що це спінер, але схоже): Панель інструментів розширюється, і коли ви прокручуєте вгору, вона згортається.

Просто для уточнення: мені не потрібен метод QuickReturn. Я знаю, що, ймовірно, додаток Telegram використовує щось подібне. Точно потрібний мені метод - це ефект програми Google Календар. Я пробував з

android:animateLayoutChanges="true"

і метод розширення працює досить добре. Але очевидно, що якщо я прокручую ListView вгору, панель інструментів не згортається.

Я також замислювався над додаванням, GestureListenerале я хочу знати, чи існують API чи простіші методи досягнення цього.

Якщо таких немає, я думаю, що піду з GestureListener. Будемо сподіватися на плавний ефект анімації.

Дякую!

Відповіді:


109

Редагувати:

З моменту випуску бібліотеки підтримки Android Design є простіше рішення. Перевірте відповідь Хоакіна

-

Ось як я це зробив, мабуть, є багато інших рішень, але це мені вдалося.

  1. Перш за все, ви повинні використовувати a Toolbarз прозорим фоном. Розширення та згортання Toolbarнасправді є фальшивим, що під прозорим Toolbar. (ви можете побачити на першому скріншоті нижче - той, що має поля) - що це також було зроблено в Telegram).

    Ми зберігаємо лише фактичну Toolbarдля NavigationIconі переповнення MenuItem.

    1. Прозора панель інструментів - 2. Розгорнутий заголовок - 3. Згорнутий заголовок

  2. Все, що є в червоному прямокутнику на другому скріншоті (тобто підроблений Toolbarі FloatingActionButton), насправді є заголовком, який ви додаєте до налаштувань ListView(або ScrollView).

    Отже, вам потрібно створити макет для цього заголовка в окремому файлі, який може виглядати так:

     <!-- The headerView layout. Includes the fake Toolbar & the FloatingActionButton -->
    
     <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <RelativeLayout
            android:id="@+id/header_container"
            android:layout_width="match_parent"
            android:layout_height="@dimen/header_height"
            android:layout_marginBottom="3dp"
            android:background="@android:color/holo_blue_dark">
    
            <RelativeLayout
                android:id="@+id/header_infos_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:padding="16dp">
    
                <ImageView
                    android:id="@+id/header_picture"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_marginRight="8dp"
                    android:src="@android:drawable/ic_dialog_info" />
    
                <TextView
                    android:id="@+id/header_title"
                    style="@style/TextAppearance.AppCompat.Title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_toRightOf="@+id/header_picture"
                    android:text="Toolbar Title"
                    android:textColor="@android:color/white" />
    
                <TextView
                    android:id="@+id/header_subtitle"
                    style="@style/TextAppearance.AppCompat.Subhead"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/header_title"
                    android:layout_toRightOf="@+id/header_picture"
                    android:text="Toolbar Subtitle"
                    android:textColor="@android:color/white" />
    
            </RelativeLayout>
        </RelativeLayout>
    
        <FloatingActionButton
            android:id="@+id/header_fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|right"
            android:layout_margin="10dp"
            android:src="@drawable/ic_open_in_browser"/>
    
    </FrameLayout>
    

    (Зверніть увагу, що ви можете використовувати від’ємні поля / відступи для того, щоб фаб трансформувався на 2 Views)

  3. Тепер настає цікава частина. Для того, щоб оживити розширення нашої підробки Toolbar, ми впроваджуємо ListView onScrollListener.

    // The height of your fully expanded header view (same than in the xml layout)
    int headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
    // The height of your fully collapsed header view. Actually the Toolbar height (56dp)
    int minHeaderHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
    // The left margin of the Toolbar title (according to specs, 72dp)
    int toolbarTitleLeftMargin = getResources().getDimensionPixelSize(R.dimen.toolbar_left_margin);
    // Added after edit
    int minHeaderTranslation;
    
    private ListView listView;
    
    // Header views
    private View headerView;
    private RelativeLayout headerContainer;
    private TextView headerTitle;
    private TextView headerSubtitle;
    private FloatingActionButton headerFab;
    
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View rootView = inflater.inflate(R.layout.listview_fragment, container, false);
        listView = rootView.findViewById(R.id.listview);
    
        // Init the headerHeight and minHeaderTranslation values
    
        headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
        minHeaderTranslation = -headerHeight + 
            getResources().getDimensionPixelOffset(R.dimen.action_bar_height);
    
        // Inflate your header view
        headerView = inflater.inflate(R.layout.header_view, listview, false);
    
        // Retrieve the header views
        headerContainer = (RelativeLayout) headerView.findViewById(R.id.header_container);
        headerTitle = (TextView) headerView.findViewById(R.id.header_title);
        headerSubtitle = (TextView) headerView.findViewById(R.id.header_subtitle);
        headerFab = (TextView) headerView.findViewById(R.id.header_fab);;
    
        // Add the headerView to your listView
        listView.addHeaderView(headerView, null, false);
    
        // Set the onScrollListener
        listView.setOnScrollListener(this);        
    
        // ...
    
        return rootView;
    }
    
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState)
    {
        // Do nothing
    }
    
    
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
    {
        Integer scrollY = getScrollY(view);
    
        // This will collapse the header when scrolling, until its height reaches
        // the toolbar height
        headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
    
        // Scroll ratio (0 <= ratio <= 1). 
        // The ratio value is 0 when the header is completely expanded, 
        // 1 when it is completely collapsed
        float offset = 1 - Math.max(
            (float) (-minHeaderTranslation - scrollY) / -minHeaderTranslation, 0f);
    
    
        // Now that we have this ratio, we only have to apply translations, scales,
        // alpha, etc. to the header views
    
        // For instance, this will move the toolbar title & subtitle on the X axis 
        // from its original position when the ListView will be completely scrolled
        // down, to the Toolbar title position when it will be scrolled up.
        headerTitle.setTranslationX(toolbarTitleLeftMargin * offset);
        headerSubtitle.setTranslationX(toolbarTitleLeftMargin * offset);
    
        // Or we can make the FAB disappear when the ListView is scrolled 
        headerFab.setAlpha(1 - offset);
    }
    
    
    // Method that allows us to get the scroll Y position of the ListView
    public int getScrollY(AbsListView view)
    {
        View c = view.getChildAt(0);
    
        if (c == null)
            return 0;
    
        int firstVisiblePosition = view.getFirstVisiblePosition();
        int top = c.getTop();
    
        int headerHeight = 0;
        if (firstVisiblePosition >= 1)
            headerHeight = this.headerHeight;
    
        return -top + firstVisiblePosition * c.getHeight() + headerHeight;
    }
    

Зверніть увагу, що деякі частини цього коду я не тестував, тому сміливо виділяйте помилки. Але загалом я знаю, що це рішення працює, хоча я впевнений, що його можна вдосконалити.

РЕДАКТУВАТИ 2:

У коді вище було кілька помилок (які я не тестував до сьогодні ...), тому я змінив кілька рядків, щоб він працював:

  1. Я представив ще одну змінну, minHeaderTranslation, яка замінила minHeaderHeight;
  2. Я змінив значення перекладу Y, застосоване до подання заголовка з:

        headerView.setTranslationY(Math.max(-scrollY, minHeaderTranslation));
    

    до:

        headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
    

    Попередній вираз взагалі не працював, вибачте за це ...

  3. Розрахунок співвідношення також змінився, так що тепер він переходить від нижньої панелі інструментів (замість верхньої частини екрана) до повного розгорнутого заголовка.


5
Чи не зникне заголовок, як тільки ви прокрутите занадто далеко вниз? Заголовки Afaik також переробляються, тому навіть якщо ви перекладете заголовок так, щоб він здавався закріпленим зверху, я думаю, він просто зникне, як тільки фактична позиція зникне з поля зору.
Куно,

Ви маєте рацію Куно. Я не намагався, але це очікувана поведінка. Ось чому я використовував панель інструментів всередині FrameLayout. Основний вміст - НАД панеллю інструментів із краєм x x. Для відтворення анімації я просто перекладаю Основний вміст між віссю Y
edoardotognoni

@FedeAmura Sweet, але я думаю, що FAB занадто низький, коли він знаходиться внизу екрана :)
MathieuMaree

3
Тепер все простіше завдяки новій бібліотеці підтримки дизайну Android . Ви можете досягти саме цього ефекту, дотримуючись цього посібника користувача Сулеймана , заснованого на роботі Кріса Бейнса для зразка програми Cheesesquare . <br> <br> ** РЕДАКТУВАТИ ** <br> Деякі користувачі запитували, чи можуть вони використовувати той самий ідея, але з піктограмою. [Користувач Saulmm Github пробував щось подібне] ( github.com/saulmm
Хоакін Ірчук

Ця бібліотека є гарним підходом для отримання базової анімації та привабливості, але CollapsingToolbarLayout наразі підтримує лише рядок як заголовок. Без піктограми чи субтитрів .. Зв’язане запитання: stackoverflow.com/questions/31069107/…
Sonic


8

Використовуйте бібліотеку підтримки дизайну http://android-developers.blogspot.in/2015/05/android-design-support-library.html

включити це в build.gradle

compile 'com.android.support:design:22.2.0'    
compile 'com.android.support:appcompat-v7:22.2.+'

для переглядача також включіть це

compile 'com.android.support:recyclerview-v7:22.2.0' 

    <!-- AppBarLayout allows your Toolbar and other views (such as tabs provided by TabLayout) 
    to react to scroll events in a sibling view marked with a ScrollingViewBehavior.-->
    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true">

        <!-- specify tag app:layout_scrollFlags -->
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"/>

        <!-- specify tag app:layout_scrollFlags -->
        <android.support.design.widget.TabLayout
            android:id="@+id/tabLayout"
            android:scrollbars="horizontal"
            android:layout_below="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"/>

        <!--  app:layout_collapseMode="pin" will help to pin this view at top when scroll -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="Title"
            android:gravity="center"
            app:layout_collapseMode="pin" />

    </android.support.design.widget.AppBarLayout>

    <!-- This will be your scrolling view. 
    app:layout_behavior="@string/appbar_scrolling_view_behavior" tag connects this features -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v7.widget.RecyclerView>

</android.support.design.widget.CoordinatorLayout>

Ваша активність повинна розширювати AppCompatActivity

public class YourActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.your_layout);

        //set toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

}

Тема вашого додатку повинна бути такою

    <resources>
            <!-- Base application theme. -->   
            <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
            </style>
    </resources>

Дуже приємно, люблю цей дизайн підтримка lib
luksfarris

І я ненавиджу це. Змінюється весь час. На жаль, мені доведеться цього слідувати.
Яр

1

Це моя реалізація:

collapsedHeaderHeightі expandedHeaderHeightвизначаються десь в іншому місці, за допомогою функції getAnimationProgressя можу отримати прогрес Розгорнути / Згорнути, на основі цього значення я роблю анімацію та показую / приховую реальний заголовок.

  listForumPosts.setOnScrollListener(new AbsListView.OnScrollListener() {

        /**
         * @return [0,1], 0 means header expanded, 1 means header collapsed
         */
        private float getAnimationProgress(AbsListView view, int firstVisibleItem) {
            if (firstVisibleItem > 0)
                return 1;

            // should not exceed 1
            return Math.min(
                    -view.getChildAt(0).getTop() / (float) (expandedHeaderHeight - collapsedHeaderHeight), 1);
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            // at render beginning, the view could be empty!
            if (view.getChildCount() > 0) {
                float animationProgress = getAnimationProgress(view, firstVisibleItem);
                imgForumHeaderAvatar.setAlpha(1-animationProgress);
                if (animationProgress == 1) {
                    layoutForumHeader.setVisibility(View.VISIBLE);
                } else {
                    layoutForumHeader.setVisibility(View.GONE);
                }
            }
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            // do nothing
        }

    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.