I am trying to implement Collapsing Toolbar By Using ConstraintLayout. After Trying a lot, I couldn't able to get the desire effects.I have follow this link in youtube (https://www.youtube.com/watch?v=8lAXJ5NFXTM at the 21 min). My Source code is:-
activity.main.xml:-
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="#dimen/app_bar_height"
android:theme="#style/AppTheme.AppBarOverlay"
android:fitsSystemWindows="false">
<com.example.study.helpers.NoAnimCollapsibleConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:minHeight="40dp"
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"/>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:src="#drawable/ic_border_color_white_24dp"/>
</android.support.design.widget.CoordinatorLayout>
content_main.xml:-
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".activities.MainActivity"
tools:showIn="#layout/activity_main">
<TextView
android:id="#+id/textView3"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/some_long_text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
NoAnimCollapsibleConstraintLayout.java:-
import android.content.Context;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.ConstraintSet;
import android.support.design.widget.AppBarLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Toast;
import com.example.study.R;
public class NoAnimCollapsibleConstraintLayout extends ConstraintLayout implements AppBarLayout.OnOffsetChangedListener {
private static final String TAG = NoAnimCollapsibleConstraintLayout.class.getSimpleName();
private float mTransitionThreshold = 0.35f;
private int mLastPosition = 0;
private boolean mToolbarOpen = true;
private ConstraintSet mOpenToolbarSet = new ConstraintSet();
private ConstraintSet mCloseToolbarSet = new ConstraintSet();
public NoAnimCollapsibleConstraintLayout(Context context) { super(context, null); }
public NoAnimCollapsibleConstraintLayout(Context context, AttributeSet attrs) { super(context, attrs, 0); }
public NoAnimCollapsibleConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getParent() instanceof AppBarLayout){
Log.d(TAG, "Instance AppBarLayout");
AppBarLayout appBarLayout = (AppBarLayout)getParent();
appBarLayout.addOnOffsetChangedListener(this);
mOpenToolbarSet.clone(getContext(), R.layout.open);
mCloseToolbarSet.clone(getContext(), R.layout.close);
}else {
Log.d(TAG, "Instance if Not Parent");
}
}
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
Log.d(TAG, "onOffsetChanged");
if (mLastPosition == verticalOffset){
return;
}
mLastPosition = verticalOffset;
Float progress = ( Math.abs(verticalOffset / (float) (appBarLayout.getHeight()))) ;
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) getLayoutParams();
params.topMargin = -verticalOffset;
setLayoutParams(params);
if ( mToolbarOpen && progress > mTransitionThreshold) {
Log.d(TAG, "Apply close.xml");
mCloseToolbarSet.applyTo(this);
mToolbarOpen = false;
}else if ( !mToolbarOpen && progress < mTransitionThreshold){
Log.d(TAG, "Apply open.xml");
mOpenToolbarSet.applyTo(this);
mToolbarOpen = true;
}
}
}
open.xml:-
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/open"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:contentDescription="#string/app_name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/study" />
</android.support.constraint.ConstraintLayout>
close.xml:-
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/close"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView4"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/app_name"
android:textColor="#android:color/white"
android:textSize="#dimen/text_size_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
After Applying this code open.xml and close.xml design not shown when I am trying to collapse Toolbar. This is the screenshot which I am getting in output:-
Without Collapse (open.xml design missing) :-
https://www.dropbox.com/s/c0rh126rinmopd4/Screen_Shot1.png?dl=0)
With Collpase (close.xml design missing):-
https://www.dropbox.com/s/cdlbg84lmht3gv0/Screen_Shot2.png?dl=0
I've been trying to do the same thing after watching the video.
Add the id "constraint" to both root ConstraintLayout
You did inflate your view in NoAnimCollapsibleConstraintLayout, so inflate and then find constraint view
import android.content.Context;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.ConstraintSet;
import android.support.design.widget.AppBarLayout;
import android.support.transition.TransitionManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
public class NoAnimCollapsibleConstraintLayout extends ConstraintLayout
implements AppBarLayout.OnOffsetChangedListener {
private static final String TAG = NoAnimCollapsibleConstraintLayout.class.getSimpleName();
private float mTransitionThreshold = 0.35f;
private int mLastPosition = 0;
private boolean mToolbarOpen = true;
private ConstraintSet mOpenToolbarSet = new ConstraintSet();
private ConstraintSet mCloseToolbarSet = new ConstraintSet();
private ConstraintLayout constraint;
public NoAnimCollapsibleConstraintLayout(Context context) {
this(context, null);
}
public NoAnimCollapsibleConstraintLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NoAnimCollapsibleConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(getContext()).inflate(R.layout.open, this, true);
constraint = findViewById(R.id.constraint);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getParent() instanceof AppBarLayout) {
Log.d(TAG, "Instance AppBarLayout");
AppBarLayout appBarLayout = (AppBarLayout) getParent();
appBarLayout.addOnOffsetChangedListener(this);
mOpenToolbarSet.clone(constraint);
mCloseToolbarSet.clone(getContext(), R.layout.close);
} else {
Log.d(TAG, "Instance if Not Parent");
}
}
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
Log.d(TAG, "onOffsetChanged");
if (mLastPosition == verticalOffset) {
return;
}
mLastPosition = verticalOffset;
Float progress = (Math.abs(verticalOffset / (float) (appBarLayout.getHeight())));
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) getLayoutParams();
params.topMargin = -verticalOffset;
setLayoutParams(params);
TransitionManager.beginDelayedTransition(constraint);
if (mToolbarOpen && progress > mTransitionThreshold) {
Log.d(TAG, "Apply close.xml");
mCloseToolbarSet.applyTo(constraint);
mToolbarOpen = false;
} else if (!mToolbarOpen && progress < mTransitionThreshold) {
Log.d(TAG, "Apply open.xml");
mOpenToolbarSet.applyTo(constraint);
mToolbarOpen = true;
}
}
}
This has an animation in and it seems to work for me. try and make sure the same view is in both, even if it is just to move it or visibility gone
EDIT: https://github.com/BigFishStudios/ConstraintToolbar here is a code example of this working
Hope this helps :D
Related
I have the following XML file that is built out of a custom layout that is created via Java code and 3 buttons inside a linear layout below it -
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="#color/grey_200"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="#layout/toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/grey_200"
android:gravity="center_vertical"
android:orientation="vertical"
tools:layout_height="500dp">
<com.etiennelawlor.tinderstack.ui.TinderStackLayout
android:id="#+id/activity_main_tinder_stack_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="5dp">
<Button
android:layout_width="wrap_content"
android:id="#+id/activity_main_delete_button"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#ff0000"
android:tag="1"
android:text="#string/activity_main_delete" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/activity_main_pass_button"
android:layout_margin="10dp"
android:background="#A9A9A9"
android:tag="2"
android:text="#string/activity_main_pass" />
<Button
android:layout_width="wrap_content"
android:id="#+id/activity_main_approve_button"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#98FB98"
android:tag="3"
android:text="#string/activity_main_approve" />
</LinearLayout>
</LinearLayout>
The issue I am facing looks like this -
I want the image to be able to move on top of the buttons when touching it, but also of course for the buttons to be touchable and functional.
Here is my TinderStackLayout class -
public class TinderStackLayout extends FrameLayout {
// Constants
private static final int DURATION = 300;
// Variable members
private OnCardSwipedListener onCardSwipedListener;
private int screenWidth;
private int yMultiplier;
//Top card
private TinderCardView topCardOnStack;
private Button mDeleteButton, mPassButton, mApproveButton;
//Constructors
public TinderStackLayout(Context context) {
super(context);
init();
}
public TinderStackLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TinderStackLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
if (onCardSwipedListener != null)
onCardSwipedListener.onNext(getChildCount());
}
#Override
public void removeView(View view) {
super.removeView(view);
if (onCardSwipedListener != null)
onCardSwipedListener.onNext(getChildCount());
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
// Helper Methods
private void init() {
setClipChildren(false);
screenWidth = DisplayUtility.getScreenWidth(getContext());
yMultiplier = DisplayUtility.dp2px(getContext(), 8);
mDeleteButton = new Button(getContext());
mPassButton = new Button(getContext());
mApproveButton = new Button(getContext());
}
public void addCard(TinderCardView tinderCardView) {
View firstCard = getChildAt(0);
if (firstCard != null && firstCard.equals(tinderCardView)) {
return;
}
if (onCardSwipedListener == null)
onCardSwipedListener = tinderCardView.getOnCardSwipedListener();
topCardOnStack = tinderCardView;
ViewGroup.LayoutParams layoutParams;
layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int childCount = getChildCount();
addView(tinderCardView, 0, layoutParams);
float scaleValue = 1 - (childCount / 50.0f);
tinderCardView.animate()
.x(0)
.y(childCount * yMultiplier)
.scaleX(scaleValue)
.setInterpolator(new AnticipateOvershootInterpolator())
.setDuration(DURATION);
}
public TinderCardView getTopCardOnStack() {
return topCardOnStack;
}
}
Do I need to add them dynamically? or is there a simpler way?
And if I do need to add them dynamically - I would be happy to get a code example of how to do so.
I have a frame layout call view_snap_tabs.xml that have image and view like below
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="184dp"
xmlns:tools="http://schemas.android.com/tools"
tools:layout_gravity="bottom"
tools:background="#color/light_purple">
<ImageView
android:id="#+id/vst_bottom_image"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginBottom="48dp"
android:layout_gravity="center|bottom"
android:src="#drawable/small_circle"/>
<View
android:id="#+id/vst_indicator"
android:layout_width="48dp"
android:layout_height="4dp"
android:layout_gravity="bottom|center"
android:layout_marginBottom="44dp"
android:background="#drawable/indicator_background"/>
</FrameLayout>
I have included this layout in my activity_main.xml like below
<include
layout="#layout/view_snap_tabs"
android:id="#+id/am_snap_tabs"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
I have created a method call SetUpViewPager and I'm trying to add that method in main activity like below
SnapTabView snapTabView = findViewById(R.id.am_snap_tabs);
snapTabView.setUpWithViewPager(viewPager);
But when I run the application It will crash from there and give a ClassCastExeception how can I fix this?
EDITED====================================================================
public class SnapTabView extends FrameLayout implements ViewPager.OnPageChangeListener {
private ImageView mCenterImage;
private ImageView mStartImage;
private ImageView mBottomImage;
private ImageView mEndImage;
private View mIndicator;
private ArgbEvaluator mArgbEvaluator;
private int mCenterColor;
private int mSideColor;
private int mEndViewsTranslationX;
private int mIndicatorTranslationX;
private int mCenterTransationY;
public SnapTabView(#NonNull Context context) {
this(context, null);
}
public SnapTabView(#NonNull Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SnapTabView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view_snap_tabs, this, false);
mCenterImage = (ImageView) findViewById(R.id.vst_center_image);
mBottomImage = (ImageView) findViewById(R.id.vst_bottom_image);
mEndImage = (ImageView) findViewById(R.id.vst_end_image);
mStartImage = (ImageView) findViewById(R.id.vst_start_image);
mIndicator = (View) findViewById(R.id.vst_indicator);
mCenterColor = ContextCompat.getColor(getContext(), R.color.white);
mSideColor = ContextCompat.getColor(getContext(), R.color.dark_grey);
mArgbEvaluator = new ArgbEvaluator();
mIndicatorTranslationX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());
mBottomImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mEndViewsTranslationX = (int) ((mBottomImage.getX() - mStartImage.getX()) - mIndicatorTranslationX);
mBottomImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mCenterTransationY = getHeight() - mBottomImage.getBottom();
}
});
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(position == 0) {
setColor(1 - positionOffset);
moveViews(1 - positionOffset);
mIndicator.setTranslationX((positionOffset - 1) * mIndicatorTranslationX);
moveAndScaleCenter(1 - positionOffset);
} else if(position == 1) {
setColor(positionOffset);
moveViews(positionOffset);
mIndicator.setTranslationX(positionOffset * mIndicatorTranslationX);
moveAndScaleCenter(positionOffset);
}
}
public void setUpWithViewPager(final ViewPager viewPager) {
viewPager.addOnPageChangeListener(this);
mStartImage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
if(viewPager.getCurrentItem() != 0)
viewPager.setCurrentItem(0);
}
});
mEndImage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
if(viewPager.getCurrentItem() != 2)
viewPager.setCurrentItem(2);
}
});
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
private void setColor(float fractionFromCenter) {
int color = (int) mArgbEvaluator.evaluate(fractionFromCenter, mCenterColor, mSideColor);
mCenterImage.setColorFilter(color);
mStartImage.setColorFilter(color);
mEndImage.setColorFilter(color);
}
private void moveViews(float fractionFromCenter) {
mStartImage.setTranslationX(fractionFromCenter * mEndViewsTranslationX);
mEndImage.setTranslationX(-fractionFromCenter * mEndViewsTranslationX);
mIndicator.setAlpha(fractionFromCenter);
mIndicator.setScaleX(fractionFromCenter);
}
private void moveAndScaleCenter(float fractionFromCenter) {
float scale = .7f + ((1 - fractionFromCenter) * .3f);
mCenterImage.setScaleX(scale);
mCenterImage.setScaleY(scale);
int translation = (int) (fractionFromCenter * mCenterTransationY);
mCenterImage.setTranslationY(translation);
mBottomImage.setTranslationY(translation);
mBottomImage.setAlpha(1 - fractionFromCenter);
}
}
view_snap_tabs.xml file like below
<?xml version="1.0" encoding="utf-8"?>
<com.example.crowderia.chat.view.SnapTabView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="184dp"
tools:layout_gravity="bottom"
tools:background="#color/light_purple"
android:id="#+id/snaps">
<ImageView
android:id="#+id/vst_center_image"
android:layout_width="88dp"
android:layout_height="88dp"
android:layout_gravity="center|bottom"
android:src="#drawable/large_circle"
android:layout_marginBottom="96dp"/>
<ImageView
android:id="#+id/vst_start_image"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginBottom="56dp"
android:layout_marginStart="24dp"
android:layout_gravity="start|bottom"
android:src="#drawable/ic_chat_bubble_24dp"/>
<ImageView
android:id="#+id/vst_end_image"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginBottom="56dp"
android:layout_marginEnd="24dp"
android:layout_gravity="end|bottom"
android:src="#drawable/ic_group_work_24dp"/>
<ImageView
android:id="#+id/vst_bottom_image"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginBottom="48dp"
android:layout_gravity="center|bottom"
android:src="#drawable/small_circle"/>
<View
android:id="#+id/vst_indicator"
android:layout_width="48dp"
android:layout_height="4dp"
android:layout_gravity="bottom|center"
android:layout_marginBottom="44dp"
android:background="#drawable/indicator_background"/>
</com.example.crowderia.chat.view.SnapTabView>
do this:
<?xml version="1.0" encoding="utf-8"?>
<com.example.crowderia.chat.view.SnapTabView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="184dp"
android:id="#+id/snaps"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="184dp"
tools:layout_gravity="bottom"
tools:background="#color/light_purple">
<ImageView
android:id="#+id/vst_bottom_image"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginBottom="48dp"
android:layout_gravity="center|bottom"
android:src="#drawable/small_circle"/>
<View
android:id="#+id/vst_indicator"
android:layout_width="48dp"
android:layout_height="4dp"
android:layout_gravity="bottom|center"
android:layout_marginBottom="44dp"
android:background="#drawable/indicator_background"/>
</FrameLayout>
</com.example.crowderia.chat.view.SnapTabView>
in the activity do this:
View view=findViewById(R.id.am_snap_tabs);
SnapTabView snap=(SnapTabView)view.findViewById(R.id.snaps);
snap.setUpWithViewPager(viewPager);
//am_snap_tabs it is the id of the include in the question
It's because you are trying to assign a view with FrameLayout to SnapTabView, modify your xml as below
view_snap_tabs.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.crowderia.chat.view.SnapTabView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="184dp"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="184dp"
tools:layout_gravity="bottom"
tools:background="#color/light_purple">
<ImageView
android:id="#+id/vst_bottom_image"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginBottom="48dp"
android:layout_gravity="center|bottom"
android:src="#drawable/small_circle"/>
<View
android:id="#+id/vst_indicator"
android:layout_width="48dp"
android:layout_height="4dp"
android:layout_gravity="bottom|center"
android:layout_marginBottom="44dp"
android:background="#drawable/indicator_background"/>
</FrameLayout>
</com.example.crowderia.chat.view.SnapTabView>
So, situation is this:
I need to implement AppBar scroll behaviour in my main activity, but, I already have CoordinatorLayout and AppBarLayout in my fragment with the same scroll behaviour.
Here is the xml from both of the layouts:
activity_main:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#EBEDEC">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/toolbar_open_nav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="#drawable/filter_icon" />
<RelativeLayout
android:id="#+id/main_activity_images_relative"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/toolbar_fencity_image"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_margin="#dimen/layout_padding"
android:src="#drawable/toolbarimg"
android:visibility="gone" />
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.Toolbar>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#android:color/transparent" />
<com.bitage.carlo.fencity.ui.view.BottomMenuView
android:id="#+id/bottom_menu"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</LinearLayout>
</RelativeLayout>
and a fragment xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="#+id/novita_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="#+id/novita_search_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/background"
app:layout_scrollFlags="scroll|enterAlways|snap">
<EditText
android:id="#+id/search_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/layout_padding"
android:background="#null"
android:hint="#string/search"
android:imeOptions="actionDone"
android:paddingEnd="#dimen/layout_padding"
android:paddingLeft="#dimen/padding_start"
android:paddingRight="#dimen/layout_padding"
android:paddingStart="#dimen/padding_start"
android:singleLine="true" />
<ImageView
android:id="#+id/search_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="#dimen/layout_padding"
android:layout_marginStart="#dimen/layout_padding"
android:src="#drawable/ic_search" />
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="#+id/places_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/transparent"
android:src="#drawable/nav_shadow" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
Now, I need this to stay the same if possible, just somehow to add the scroll behavior app:layout_scrollFlags="scroll|enterAlways|snap" on toolbar only in this fragment, while search from the fragment still has the same behaviour.
How can I achieve this?
So, you can do next:
in your Fragment class, you have recyclerview. Now, what you want to do is put the recyclerView.addOnScrollListener(new YourScrollListenerClass(context, toolbar, coordinatorLayout));
Coordinator layout is the one of the fragment, please bear that in mind.
Now, create a new class YourScrollListenerClass:
public class YourScrollListenerClass extends RecyclerView.OnScrollListener {
private Context mContext;
private Toolbar mToolbar;
private CoordinatorLayout mCoordinatorLayout;
private int mToolbarHeight;
private int mCoordinatorLayoutTopMargin;
public YourScrollListenerClass(Context context, #Nullable Toolbar toolbar, #Nullable final CoordinatorLayout mCoordinatorLayout) {
this.mContext = context;
mToolbar = toolbar;
this.mCoordinatorLayout = mCoordinatorLayout;
if (mToolbar != null && mCoordinatorLayout != null) {
mToolbar.post(new Runnable() {
#Override
public void run() {
mToolbarHeight = mToolbar.getHeight();
}
});
mCoordinatorLayout.post(new Runnable() {
#Override
public void run() {
mCoordinatorLayoutTopMargin = ((FrameLayout.LayoutParams) mCoordinatorLayout.getLayoutParams()).topMargin;
}
});
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (recyclerView != null) {
if (mToolbar != null) {
float offset = mToolbar.getTranslationY() - dy;
if (offset <= 0 && offset >= -(mToolbar.getHeight())) {
mToolbar.setTranslationY(mToolbar.getTranslationY() - dy);
FrameLayout.LayoutParams params1 = (FrameLayout.LayoutParams) mCoordinatorLayout.getLayoutParams();
params1.setMargins(0, params1.topMargin - dy, 0, 0);
mCoordinatorLayout.setLayoutParams(params1);
}
}
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (RecyclerView.SCROLL_STATE_IDLE == newState) {
if(mToolbar != null) {
if (Math.abs(mToolbar.getTranslationY()) <= mToolbar.getHeight() / 2) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mToolbar, View.TRANSLATION_Y, mToolbar.getTranslationY(), 0);
animator.setDuration(300);
animator.start();
MarginAnimation animation = new MarginAnimation(mCoordinatorLayout, ((FrameLayout.LayoutParams) mCoordinatorLayout.getLayoutParams()).topMargin, mCoordinatorLayoutTopMargin);
mCoordinatorLayout.startAnimation(animation);
} else {
ObjectAnimator animator = ObjectAnimator.ofFloat(mToolbar, View.TRANSLATION_Y, mToolbar.getTranslationY(), -mToolbarHeight);
animator.setDuration(300);
animator.start();
MarginAnimation animation = new MarginAnimation(mCoordinatorLayout, ((FrameLayout.LayoutParams) mCoordinatorLayout.getLayoutParams()).topMargin, 0);
mCoordinatorLayout.startAnimation(animation);
}
}
}
}
}
And for the margin animation, create new Class
public class MarginAnimation extends Animation {
private View mView;
private float mToMargin;
private float mFromMargin;
private Context mContext;
public MarginAnimation(View v, float fromTop, float toTop) {
mFromMargin = fromTop;
mToMargin = toTop;
mView = v;
setDuration(300);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float topMargin = (mToMargin - mFromMargin) * interpolatedTime + mFromMargin;
Log.d("TopMargin", String.valueOf(topMargin));
FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) mView.getLayoutParams();
p.setMargins(0, (int) topMargin, 0, 0);
mView.setLayoutParams(p);
mView.requestLayout();
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
Hope this helps...
With Google’s recent release of the Design Support Library several cool new views have been introduced. Using some of the new components (e.g. CoordinatorLayout ) might (!) enable you to achieve the scrolling behavior.
I was tryed with some built in scrolling behavior but nothing is working for me,
I have a bottombar(LinearLayout) in my layout in place of FloatingActionButton
Here what I want.
OnLaunch of this screen bottom bar should appear.
On scrollup of recyclerview bottom bar should scrolldown.
On scrolldown of recyclerview bottom bar should scrollup
Is there any builtin mechanism to achieve this ? or We need write java code?
Here is my code:
main_activty.xml
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<include layout="#layout/toolbar_srp" />
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="5dp"
app:tabGravity="fill"
app:tabIndicatorColor="#android:color/white"
app:tabMode="fixed" />
</android.support.design.widget.AppBarLayout>
<!-- All Scrollable Views -->
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<!-- Bottom bar-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#a0000000"
android:orientation="horizontal"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.33"
android:gravity="center"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small"
android:text="AC"
android:textColor="#color/white" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.33"
android:gravity="center"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small"
android:text="Sleeper"
android:textColor="#color/white" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.33"
android:gravity="center"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small"
android:text="Premium"
android:textColor="#color/white" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
fragment.xml (here I was putted my recyclerview code)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small"
android:weightSum="1">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.33"
android:background="#drawable/bg_srp_sorter"
android:clickable="true"
android:gravity="center"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small"
android:text="Departure" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.33"
android:background="#drawable/bg_srp_sorter"
android:gravity="center"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small"
android:text="Duration" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.33"
android:gravity="center"
android:paddingBottom="#dimen/padding_small"
android:paddingTop="#dimen/padding_small"
android:text="Price" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/lite_gray"></View>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:weightSum="1">
<android.support.v7.widget.RecyclerView
android:id="#+id/bus_list_recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
android:minHeight="?attr/actionBarSize">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="1">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.10">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/abc_ic_ab_back_mtrl_am_alpha" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.78"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<TextView
android:id="#+id/toolbar_title_source"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.47"
android:ellipsize="end"
android:singleLine="true"
android:text="Thiruvananthapuram "
android:textColor="#color/white"
android:textSize="#dimen/label_text_size_large" />
<TextView
android:id="#+id/toolbar_title_arrow"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.08"
android:text="#string/char_right"
android:textColor="#color/white"
android:textSize="#dimen/label_text_size_large"
android:textStyle="bold" />
<TextView
android:id="#+id/toolbar_title_destination"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.47"
android:ellipsize="end"
android:singleLine="true"
android:text=" Cochin"
android:textColor="#color/white"
android:textSize="#dimen/label_text_size_large" />
</LinearLayout>
<TextView
android:id="#+id/toolbar_sub_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2 Seat(s)"
android:textColor="#color/lite_gray"
android:textSize="#dimen/label_text_size_normal" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.13">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:scaleType="fitCenter"
android:src="#drawable/filter" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.Toolbar>
This how I did it:
mRecylerView.addOnScrollListener(new HideShowScrollListener() {
#Override
public void onHide() {
//fab.animate().setInterpolator(new AccelerateDecelerateInterpolator()).scaleX(0).scaleY(0);
// do your hiding animation here
}
#Override
public void onShow() {
// fab.animate().setInterpolator(new AccelerateDecelerateInterpolator()).scaleX(1).scaleY(1);
// do your showing animation here
}
});
You will be needing HideShowScrollListener.class
/**
* This class is a ScrollListener for RecyclerView that allows to show/hide
* views when list is scrolled.
* */
public abstract class HideShowScrollListener extends RecyclerView.OnScrollListener {
private static final int HIDE_THRESHOLD = 20;
private int scrolledDistance = 0;
private boolean controlsVisible = true;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
onHide();
controlsVisible = false;
scrolledDistance = 0;
} else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
onShow();
controlsVisible = true;
scrolledDistance = 0;
}
if((controlsVisible && dy>0) || (!controlsVisible && dy<0)) {
scrolledDistance += dy;
}
}
public abstract void onHide();
public abstract void onShow();
}
since you are using CoordinatorLayout you could create a custom Behaviour that can achieve what you requested follow example below:
create a class that extends CoordinatorLayout.Behavior<View> follow example below:
public class QuickReturnFloaterBehavior extends CoordinatorLayout.Behavior<View> {
private int distance;
public QuickReturnFloaterBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
#Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
if (dy > 0 && distance < 0 || dy < 0 && distance > 0) {
child.animate().cancel();
distance = 0;
}
distance += dy;
final int height = child.getHeight() > 0 ? (child.getHeight()) : 600/*update this accordingly*/;
if (distance > height && child.isShown()) {
hide(child);
} else if (distance < 0 && !child.isShown()) {
show(child);
}
}
private void hide(View view) {
view.setVisibility(View.GONE);// use animate.translateY(height); instead
}
private void show(View view) {
view.setVisibility(View.VISIBLE);// use animate.translateY(-height); instead
}
}
now to apply this behaviour add this to your layout
app:layout_behavior="com.example.QuickReturnFloaterBehavior"
Visibility and Hide with Animation in Your Fragment:
recyclerView.addOnScrollListener(new HideShowScrollListener() {
final Fragment parentFragment = getParentFragment();
#Override
public void onHide() {
bottomLayout.animate().setDuration(200).translationYBy(-bottomLayout.getHeight()).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
bottomLayout.setVisibility(View.GONE);
}
});
}
#Override
public void onShow() {
bottomLayout.animate().setDuration(200).translationY(0).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
bottomLayout.setVisibility(View.VISIBLE);
}
});
}
#Override
public void onScrolled() {
// To load more data
}
});
HideShowScrollListener.java
public abstract class HideShowScrollListener extends RecyclerView.OnScrollListener {
private static final int HIDE_THRESHOLD = 20;
private int scrolledDistance = 0;
private boolean controlsVisible = true;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
onScrolled();
if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
onHide();
controlsVisible = false;
scrolledDistance = 0;
} else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
onShow();
controlsVisible = true;
scrolledDistance = 0;
}
if((controlsVisible && dy>0) || (!controlsVisible && dy<0)) {
scrolledDistance += dy;
}
}
public abstract void onHide();
public abstract void onShow();
public abstract void onScrolled();
}
package com.keshav.hideactionbarandfooterexample;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import java.util.ArrayList;
import java.util.List;
import adapters.RecyclerAdapter;
import listners.HidingScrollListener;
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
private Toolbar toolbar_bottom;
private ImageButton mFabButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppThemeRed);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("keshav", "MainActivity called");
initToolbar();
mFabButton = (ImageButton) findViewById(R.id.fabButton);
initRecyclerView();
}
private void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar_bottom = (Toolbar) findViewById(R.id.toolbar_bottom);
setSupportActionBar(mToolbar);
setSupportActionBar(toolbar_bottom);
setTitle(getString(R.string.app_name));
mToolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
toolbar_bottom.setTitleTextColor(getResources().getColor(android.R.color.white));
toolbar_bottom.setVisibility(View.GONE);
}
private void initRecyclerView() {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
RecyclerAdapter recyclerAdapter = new RecyclerAdapter(createItemList());
recyclerView.setAdapter(recyclerAdapter);
recyclerView.addOnScrollListener(new HidingScrollListener() {
#Override
public void onHide() {
hideViews();
}
#Override
public void onShow() {
showViews();
}
});
}
private void hideViews() {
// TODO (-mToolbar) plus means 2 view above ho jaye or not visible to user
mToolbar.animate().translationY(-mToolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2));
// TODO uncomment this Hide Footer in android when Scrolling
// TODO (+mToolbar) plus means 2 view forward ho jaye or not visible to user
toolbar_bottom.animate().translationY(+toolbar_bottom.getHeight()).setInterpolator(new AccelerateInterpolator(2));
// TODO keshav Hide Also Floatng Button In Android
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFabButton.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
mFabButton.animate().translationY(mFabButton.getHeight() + fabBottomMargin).setInterpolator(new AccelerateInterpolator(2)).start();
// TODO keshav Hide Also Floatng Button In Android
}
private void showViews() {
mToolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
// TODO uncomment this Hide Footer in android when Scrolling
toolbar_bottom.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
mFabButton.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start();
}
private List<String> createItemList() {
List<String> itemList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
itemList.add("Item " + i);
}
return itemList;
}
}
=============================================
RecyclerAdapter
=============================================
package adapters;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.keshav.hideactionbarandfooterexample.R;
import java.util.List;
/*
* RecyclerView Adapter that allows to add a header view.
* */
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 2;
private static final int TYPE_ITEM = 1;
private List<String> mItemList;
public RecyclerAdapter(List<String> itemList) {
mItemList = itemList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
if (viewType == TYPE_ITEM) {
final View view = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false);
return RecyclerItemViewHolder.newInstance(view);
} else if (viewType == TYPE_HEADER) {
final View view = LayoutInflater.from(context).inflate(R.layout.recycler_header, parent, false);
return new RecyclerHeaderViewHolder(view);
}
throw new RuntimeException("There is no type that matches the type " + viewType + " + make sure your using types correctly");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (!isPositionHeader(position)) {
RecyclerItemViewHolder holder = (RecyclerItemViewHolder) viewHolder;
String itemText = mItemList.get(position - 1); // header
holder.setItemText(itemText);
}
}
public int getBasicItemCount() {
return mItemList == null ? 0 : mItemList.size();
}
#Override
public int getItemViewType(int position) {
if (isPositionHeader(position)) {
return TYPE_HEADER;
}
return TYPE_ITEM;
}
#Override
public int getItemCount() {
return getBasicItemCount() + 1; // header
}
private boolean isPositionHeader(int position) {
return position == 0;
}
}
=====================================================
RecyclerHeaderViewHolder
=====================================================
package adapters;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class RecyclerHeaderViewHolder extends RecyclerView.ViewHolder {
public RecyclerHeaderViewHolder(View itemView) {
super(itemView);
}
}
=====================================================
RecyclerItemViewHolder
=====================================================
package adapters;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.keshav.hideactionbarandfooterexample.R;
public class RecyclerItemViewHolder extends RecyclerView.ViewHolder {
private final TextView mItemTextView;
public RecyclerItemViewHolder(final View parent, TextView itemTextView) {
super(parent);
mItemTextView = itemTextView;
}
public static RecyclerItemViewHolder newInstance(View parent) {
TextView itemTextView = (TextView) parent.findViewById(R.id.itemTextView);
return new RecyclerItemViewHolder(parent, itemTextView);
}
public void setItemText(CharSequence text) {
mItemTextView.setText(text);
}
}
===================================================
activity_main.xml
===================================================
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
<ImageButton
android:id="#+id/fabButton"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_gravity="bottom|right"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:background="#drawable/fab_bcg"
android:src="#drawable/ic_favorite_outline_white_24dp"
android:contentDescription="#string/fab_description"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar_bottom"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
</RelativeLayout>
</FrameLayout>
==================================================
recycle_header.xml in layout folder
==================================================
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
==================================================
recycle_item.xml in layout folder
==================================================
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<TextView
android:id="#+id/itemTextView"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:padding="8dp"
style="#style/Base.TextAppearance.AppCompat.Body2"/>
</android.support.v7.widget.CardView>
=================================================
styles.xml
=================================================
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
</style>
<style name="AppThemeRed" parent="AppTheme">
<item name="colorPrimary">#color/color_primary_red</item>
<item name="colorPrimaryDark">#color/color_primary_red_dark</item>
</style>
<style name="AppThemeGreen" parent="AppTheme">
<item name="colorPrimary">#color/color_primary_green</item>
<item name="colorPrimaryDark">#color/color_primary_green_dark</item>
</style>
<style name="AppThemeBlue" parent="AppTheme">
<item name="colorPrimary">#color/color_primary_blue</item>
<item name="colorPrimaryDark">#color/color_primary_blue_dark</item>
<item name="colorAccent">#color/color_accent_pink</item>
</style>
</resources>
build.gradle Dependency
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
#k0sh gave a fine solution. I want to update it to get a smooth animation
while showing and hiding the layout. Replace complete code inside
onNestedPreScroll() with the below code:
if (dy > 0) {
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
int viewBottomMargin = layoutParams.bottomMargin;
child.animate().translationY(child.getHeight() + viewBottomMargin).setInterpolator(new LinearInterpolator()).start();
} else if (dy < 0) {
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
}
#k0sh's QuickReturnFloaterBehavior works but it sometimes not listen to the slight scrolls.
I am having contents in NestedScrollView with a LinearLayout as a bottom bar & didn't have the recycler view. so, I can't use other suggested answers.
Those who are having the same situation can use the BottomNavigationBehavior from this post Bottom Navigation Behavior. it works perfectly & useful.
When the current top-level view has a scrolling content, the bottom
navigation should hide when it is scrolled down and retract back when
the content is scrolled up. For this purpose, we need to be aware of
the direction of the scroll. VerticalScrollingBehavior is a generic
layout behavior that it is somehow an extension to the
CoordinatorLayout.Behavior that dispatches events on for direction of
scrolling.
source code for BottomNavigationBehavior - https://gist.github.com/NikolaDespotoski/1d6fef4949eb9be05a46#file-bottomnavigationbehavior-java
snippet from BottomNavigationBehavior
#Override
public void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, #ScrollDirection int scrollDirection) {
handleDirection(child, scrollDirection);
}
#Override
protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, #ScrollDirection int scrollDirection) {
handleDirection(child, scrollDirection);
return true;
}
private void handleDirection(V child, #ScrollDirection int scrollDirection) {
if (!scrollingEnabled) return;
if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) {
hidden = false;
animateOffset(child, 0);
} else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) {
hidden = true;
animateOffset(child, child.getHeight());
}
}
I have to do this layout:
I was trying to align the views, using RelativeLayout and layout_toRightOf, layout_below, etc, but the best that I achieved was this:
Here are the xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<RelativeLayout
android:id="#+id/big"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#drawable/circular"
android:layout_margin="10dp"
android:layout_centerInParent="true"/>
<RelativeLayout
android:id="#+id/right"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_toRightOf="#+id/big"
android:layout_centerVertical="true"/>
<RelativeLayout
android:id="#+id/left"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_toLeftOf="#+id/big"
android:layout_centerVertical="true"/>
<RelativeLayout
android:id="#+id/top"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_above="#+id/big"
android:layout_centerHorizontal="true"/>
<RelativeLayout
android:id="#+id/bottom"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_below="#+id/big"
android:layout_centerHorizontal="true"/>
<RelativeLayout
android:id="#+id/northeast"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_toRightOf="#+id/big"
android:layout_alignTop="#+id/top"/>
<RelativeLayout
android:id="#+id/northwest"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_toLeftOf="#+id/big"
android:layout_alignTop="#+id/top"/>
<RelativeLayout
android:id="#+id/southeast"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_toRightOf="#+id/big"
android:layout_below="#+id/big"/>
<RelativeLayout
android:id="#+id/southwest"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/circular"
android:layout_toLeftOf="#+id/big"
android:layout_below="#+id/big"/>
</RelativeLayout>
I'm trying to avoid using margin on the little circles, because the diagonal circles have to be aligned exactly to the center, in comparison with the top/bottom/right/left circles.
How can I do that?
I show you another approach.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class CircleMenu extends View {
private Paint mainPaint;
private Paint secondPaint;
private Paint textPaint;
private int radius_main =130;
private int menuRadialButtonsCount =7;
private int menuInnerPadding = 40;
private int radialCircleRadius = 60;
private int textPadding = 25;
private double startAngle = - Math.PI/2f;;
public CircleMenu(Context context) {
super(context);
}
public CircleMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircleMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mainPaint = new Paint();
mainPaint.setColor(Color.BLUE);
secondPaint = new Paint();
secondPaint.setColor(Color.DKGRAY);
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = canvas.getWidth()/2 ;
int centerY= canvas.getHeight()/2;
canvas.drawCircle(centerX,centerY,radius_main,mainPaint);
for(int i=0;i<menuRadialButtonsCount;i++){
double angle =0;
if(i==0){
angle = startAngle;
}else{
angle = startAngle+(i * ((2 * Math.PI) / menuRadialButtonsCount));
}
int x = (int) (centerX + Math.cos(angle)*(radius_main+menuInnerPadding+radialCircleRadius));
int y = (int) (centerY + Math.sin(angle)*(radius_main+menuInnerPadding+radialCircleRadius));
canvas.drawCircle(x,y,radialCircleRadius,secondPaint);
float tW = textPaint.measureText("Text "+i);
canvas.drawText("Text "+i,x-tW/2,y+radialCircleRadius+textPadding,textPaint);
}
}
}
You can extend this class, add methods to set dimmensions from resources, controlling numer of circles, their size, paddings, onTouch, shadows, colors ....
<your.package.CircleMenu
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Updated version:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
public class CircleMenu extends View {
public static interface IMenuListener{
public void onMenuClick(MenuCircle item);
}
public static class MenuCircle{
private int x,y,radius;
public int id;
public String text;
}
private Paint mainPaint;
private Paint secondPaint;
private Paint textPaint;
private int radius_main =130;
private int menuInnerPadding = 40;
private int radialCircleRadius = 60;
private int textPadding = 25;
private double startAngle = - Math.PI/2f;
private ArrayList<MenuCircle> elements;
private IMenuListener listener;
public void setListener(IMenuListener listener){
this.listener = listener;
}
public void clear(){
elements.clear();
listener=null;
}
public CircleMenu(Context context) {
super(context);
init();
}
public CircleMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CircleMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
elements = new ArrayList<>();
}
public void addMenuItem(String text,int id){
MenuCircle item = new MenuCircle();
item.id = id;
item.text=text;
elements.add(item);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mainPaint = new Paint();
mainPaint.setColor(Color.BLUE);
secondPaint = new Paint();
secondPaint.setColor(Color.DKGRAY);
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = canvas.getWidth()/2 ;
int centerY= canvas.getHeight()/2;
canvas.drawCircle(centerX,centerY,radius_main,mainPaint);
for(int i=0;i<elements.size();i++){
double angle =0;
if(i==0){
angle = startAngle;
}else{
angle = startAngle+(i * ((2 * Math.PI) / elements.size()));
}
elements.get(i).x = (int) (centerX + Math.cos(angle)*(radius_main+menuInnerPadding+radialCircleRadius));
elements.get(i).y = (int) (centerY + Math.sin(angle)*(radius_main+menuInnerPadding+radialCircleRadius));
canvas.drawCircle( elements.get(i).x,elements.get(i).y,radialCircleRadius,secondPaint);
float tW = textPaint.measureText(elements.get(i).text);
canvas.drawText(elements.get(i).text,elements.get(i).x-tW/2,elements.get(i).y+radialCircleRadius+textPadding,textPaint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
for(MenuCircle mc : elements){
double distance = Math.hypot(event.getX()-mc.x,event.getY()-mc.y);
if(distance<= radialCircleRadius){
//touched
if(listener!=null)
listener.onMenuClick(mc);
return true;
}
}
}
return super.onTouchEvent(event);
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
}
In Fragment:
CircleMenu cm = (CircleMenu) view.findViewById(R.id.c_menu);
cm.addMenuItem("one",1);
cm.addMenuItem("two",2);
cm.addMenuItem("three",3);
cm.addMenuItem("ten",10);
cm.addMenuItem("oh oh",156);
cm.addMenuItem("exit",134);
cm.setListener(new CircleMenu.IMenuListener() {
#Override
public void onMenuClick(CircleMenu.MenuCircle item) {
Toast.makeText(getActivity(),item.text+" "+item.id,Toast.LENGTH_LONG).show();
}
});
This helps you.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
<RelativeLayout
android:id="#+id/rlBig"
android:layout_width="250dp"
android:layout_height="260dp"
android:layout_centerInParent="true">
<RelativeLayout
android:id="#+id/big"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerInParent="true"
android:background="#drawable/round_orange_schdule_meet" />
<RelativeLayout
android:id="#+id/northwest"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="10dp"
android:background="#drawable/round_orange_schdule_meet" />
<RelativeLayout
android:id="#+id/southeast"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="#drawable/round_orange_schdule_meet" />
<RelativeLayout
android:id="#+id/southwest"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="#drawable/round_orange_schdule_meet" />
<RelativeLayout
android:id="#+id/northeast"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:background="#drawable/round_orange_schdule_meet" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/right"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/rlBig"
android:layout_marginLeft="-15dp"
android:background="#drawable/round_orange_schdule_meet" />
<RelativeLayout
android:id="#+id/left"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_marginRight="-15dp"
android:layout_toLeftOf="#+id/rlBig"
android:background="#drawable/round_orange_schdule_meet" />
<RelativeLayout
android:id="#+id/top"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_above="#+id/rlBig"
android:layout_marginBottom="-15dp"
android:layout_centerHorizontal="true"
android:background="#drawable/round_orange_schdule_meet" />
<RelativeLayout
android:id="#+id/bottom"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="#+id/rlBig"
android:layout_centerHorizontal="true"
android:layout_marginTop="-10dp"
android:background="#drawable/round_orange_schdule_meet" />
Output is:
You should go with a custom-view here - with a lot of force you might be able to do it with a layout - but it will be messy and not perform well