Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android CollapsingToolbarLayout with custom View

I'm following the Cheesesquare example project to understand the new design material library.

I'm wondering if there's a way to use a custom view (like Telegram) with ImageView, title and subtitle instead of the simple Title provided by CollapsingToolbarLayout widget.

Thanks.

like image 917
CeccoCQ Avatar asked Jun 17 '15 08:06

CeccoCQ


3 Answers

I had the same problem and spend many hours trying to find a solution. My solution was to add the collapsing Views (ImageView and TextView) inside the CollapsingToolbarLayout and then handle the transition in code. This way it's more flexible and simpler than extending from CollapsingToolbarLayout.

First you'll need to add your Views inside the CollapsingToolbarLayout with the parallax properties:

        <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

Then set the scaling of the Views with the help of an 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.
        }
    });

Somehow the default offsetChangedListener didn't work properly for me (you probably still should try it with the default listener first), so I used the ControllableAppBarLayout from https://gist.github.com/blipinsk/3f8fb37209de6d3eea99 and added the following:

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);
}

The only problem is, that you would need to set app:contentScrim="#00000000" (transparent) for your CollapsingToolbarLayout, so your views are still visible when the toolbar is collapsed. If you really need the collapsing-background effect I'm sure you could "fake" this by setting the alpha of a background ImageView in the OffsetChangeListener. ;)

like image 145
Ciron Avatar answered Nov 12 '22 18:11

Ciron


From the widget itself there doesn't seem to be a way to enable this directly, like it was possible to add custom views to the Toolbar.

What you could try to do however, is open the source of the CollapsingToolbarLayout.class and check out how the CollapsingTextHelper.class is used to have the title set. You could try to make your own widget by extending from the the CollapsingToolbarLayout.

These links can help you out with creating custom components/views, if you haven't created them before: Custom Views, Custom Components

I haven't tried this yet, but it's actually something I was thinking about trying to achieve a similar solution as you are looking for. Steps I tihkn I would follow, so far:

  1. Create custom attributes for subtitle settings in attrs.xml
  2. Create your own MyCollapsingToolbarLayout by extending the original one.
  3. Make sure to call super in the constructors, so the original component will stay intact.
  4. Create a subtitleTextHelper by adding a new CollapsingTextHelper to your component.
  5. Override onDraw to actually draw your subtitle.
  6. Update the layout containing your CollapingsToolbarLayout with your subtitle attributes (default styling and such, maybe a fixed subtitle Text).
  7. Apply the changes in the Activity containing your CollapsingToolbar. (Convert CollapsingToolbarlayout to MyCollapingsToolbarLayout, set subtitles, extra custom settings, etc).
  8. Cross fingers, test.

Going to have a look at it now.

like image 32
StingRay5 Avatar answered Nov 12 '22 18:11

StingRay5


Going to edit Christopher's answer slightly to show how you can get your custom view to not disappear on collapse:

First you'll need to add your Views inside the CollapsingToolbarLayout with the parallax properties:

        <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

Instead add the custom view's programmatically and it won't disappear on collapse. For example here is a view that contains a title and a subtitle:

    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;
}
like image 2
luca992 Avatar answered Nov 12 '22 18:11

luca992