Android CollapsingToolbarLayout с пользовательским представлением

Я следую примеру проекта Cheesesquare, чтобы понять новую библиотеку материалов проекта.

Мне интересно, есть ли способ использовать пользовательский вид (например, Telegram) с ImageView, заголовком и субтитрами вместо простого заголовка, предоставленного виджлетом CollapsingToolbarLayout.

Спасибо.

Ответ 1

У меня была такая же проблема, и я потратил много часов, пытаясь найти решение. Моим решением было добавить сворачивающиеся представления (ImageView и TextView) внутри CollapsingToolbarLayout, а затем обработать переход в коде. Таким образом, он более гибкий и прост, чем расширение от CollapsingToolbarLayout.

Сначала вам нужно добавить свои представления внутри CollapsingToolbarLayout с помощью свойств параллакса:

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop:"80dp"
            android:src="@drawable/icon"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.8"/> //set vertical transition here

Затем установите масштабирование представлений с помощью OnOffsetchangeListner:

  private static final float SCALE_MINIMUM=0.5f;
  appBarLayout.setOnWorkingOffsetChange(new  ControllableAppBarLayout.OnWorkingOffsetChange() {
        @Override
        public void onOffsetChange(int offSet, float collapseDistance) {
            imageView.setScaleX(1 + (collapseDistance * SCALE_MINIMUM));
            imageView.setScaleY(1 + (collapseDistance * SCALE_MINIMUM));

            textView.setScaleX(1 + (collapseDistance * SCALE_MINIMUM));
            textView.setScaleY(1 + (collapseDistance * SCALE_MINIMUM));

            // You can also setTransitionY/X, setAlpha, setColor etc.
        }
    });

Как-то по умолчанию offsetChangedListener не работал должным образом для меня (вы, вероятно, все равно должны попробовать его с помощью первого слушателя по умолчанию), поэтому я использовал ControllableAppBarLayout из https://gist.github.com/blipinsk/3f8fb37209de6d3eea99 и добавил следующее:

private OnWorkingOffsetChange onWorkingOffsetChange;

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
    if (!isInEditMode()) {
        onWorkingOffsetChange.onOffsetChange(i, (float) i / appBarLayout.getTotalScrollRange());
    }
}

public void setOnWorkingOffsetChange(OnWorkingOffsetChange listener) {
    this.onWorkingOffsetChange = listener;
}

public interface OnWorkingOffsetChange {
    void onOffsetChange(int offSet, float collapseDistance);
}

Единственная проблема заключается в том, что вам нужно будет установить    app:contentScrim="#00000000" (прозрачный) для вашего CollapsingToolbarLayout, поэтому ваши представления все еще видны, когда панель инструментов рушится. Если вам действительно нужен эффект сбрасывания фона, я уверен, что вы могли бы "подделать" это, установив альфа фонового ImageView в OffsetChangeListener.;)

Ответ 2

Из самого виджета, похоже, не существует способа включить это прямо, как можно было добавить пользовательские представления на панель инструментов.

Однако вы могли бы попытаться сделать это, открыть источник CollapsingToolbarLayout.class и проверить, как используется CollapsingTextHelper.class, чтобы установить заголовок. Вы можете попробовать создать свой собственный виджет, простираясь от CollapsingToolbarLayout.

Эти ссылки могут помочь вам в создании пользовательских компонентов/представлений, если вы их еще не создали: Пользовательские виды, Пользовательские компоненты

Я еще не пробовал этого, но на самом деле я думал о том, чтобы попытаться достичь аналогичного решения, которое вы ищете. Шаги я tihkn Я бы следовал, пока:

  • Создание пользовательских атрибутов для настроек субтитров в attrs.xml
  • Создайте свой собственный MyCollapsingToolbarLayout, расширив исходный.
  • Обязательно вызовите super в конструкторах, чтобы исходный компонент оставался неповрежденным.
  • Создайте subtitleTextHelper, добавив новый CollapsingTextHelper к вашему компоненту.
  • Переопределить onDraw, чтобы на самом деле нарисовать ваши субтитры.
  • Обновите макет, содержащий ваш CollapingsToolbarLayout, с вашими атрибутами субтитров (стилизация по умолчанию и, возможно, фиксированный текст субтитров).
  • Примените изменения в Activity, содержащие ваш CollapsingToolbar. (Преобразовать CollapsingToolbarLayout в MyCollapingsToolbarLayout, установить субтитры, дополнительные пользовательские настройки и т.д.).
  • Перекрестные пальцы, тест.

Теперь посмотрим.

Ответ 3

Переход к редактированию Кристофера немного, чтобы показать, как вы можете заставить свой пользовательский вид не исчезнуть при крахе:

Сначала вам нужно добавить свои представления в CollapsingToolbarLayout с помощью свойств параллакса:

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop:"80dp"
            android:src="@drawable/icon"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.8"/> //set vertical transition here

Вместо этого добавьте пользовательский вид программно, и он не исчезнет при крахе. Например, вот представление, которое содержит заголовок и субтитр:

    final FrameLayout frameLayout = new FrameLayout(mActivity);
    FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT);
    frameLayout.setLayoutParams(frameLayoutParams);


    // Create new LinearLayout
    final LinearLayout linearLayout = new LinearLayout(mActivity);
    frameLayoutParams =new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dpToPixels(78));
    frameLayoutParams.gravity = Gravity.BOTTOM;
    linearLayout.setLayoutParams(frameLayoutParams);
    linearLayout.setOrientation(LinearLayout.VERTICAL);


    // Add textviews
    final TextView textView1 = new TextView(mActivity);
    LinearLayout.LayoutParams linearLayoutParams =new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    frameLayoutParams.gravity = Gravity.BOTTOM;
    textView1.setLayoutParams(linearLayoutParams);
    textView1.setText("Title");
    textView1.setTextColor(ContextCompat.getColor(mActivity, R.color.colorWhite));
    textView1.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40);
    linearLayout.addView(textView1);


    final TextView textView2 = new TextView(mActivity);
    linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
            LinearLayout.LayoutParams.WRAP_CONTENT);
    textView2.setLayoutParams(linearLayoutParams);
    textView2.setText("Subtitle");
    textView2.setTextColor(ContextCompat.getColor(mActivity, R.color.colorWhite));
    textView2.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
    linearLayout.addView(textView2);

    frameLayout.addView(linearLayout);


    collapsingToolbar.addView(frameLayout);
    final float SCALE_MIN=0.4f;
    AppBarLayout appBarLayout = (AppBarLayout) mActivity.findViewById(R.id.appBarLayout);
    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int offSet) {
            float collapsedRatio = (float) offSet / appBarLayout.getTotalScrollRange();
            linearLayout.setScaleX(1 + (collapsedRatio * SCALE_MIN));
            linearLayout.setScaleY(1 + (collapsedRatio * SCALE_MIN));
            FrameLayout.LayoutParams frameLayoutParams =new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dpToPixels(78));
            frameLayoutParams.gravity = Gravity.BOTTOM;
            frameLayoutParams.setMargins(Math.round(dpToPixels(48) * (1+collapsedRatio)), 0, 0, Math.round(dpToPixels(15) * collapsedRatio));
            linearLayout.setLayoutParams(frameLayoutParams);
            // You can also setTransitionY/X, setAlpha, setColor etc.
        }
    });

/////

float lastCollapsedRatio = -2;

////

private int dpToPixels(int padding_in_dp){
    final float scale = getResources().getDisplayMetrics().density;
    int padding_in_px = (int) (padding_in_dp * scale + 0.5f);
    return padding_in_px;
}