CollapsingToolbar odd behavior in API 21 - android

For some reason, I am getting this odd state where the status bar seems to have moved below the collapsingtoolbarlayout, and the status bar is blank. I see this issue when I swipe the CollapsingToolbarLayout up and down repeatedly. The image below shows a blue horizontal line beneath the collapsingToolbarLayout, and I think this is actually the status bar? (I don't know for sure). I've tried using setFitSystemWindow(true) in multiple places but can't seem to fix this.
The issue seems to happen when I use the SCROLL_FLAG_ENTER_ALWAYS flag. It doesn't occur when I swap this out with SCROLL_FLAG_SNAP. This doesn't occur in API 19.
I am using a programmatically generated collapsingToolbarLayout.
#Override
protected void onPostCreate(Bundle savedInstanceState)
{
super.onPostCreate(savedInstanceState);
CollapsingToolbarLayout collapsingToolbarLayout = new CollapsingToolbarLayout(this);
AppBarLayout.LayoutParams collapsingToolbarlayoutParams = new AppBarLayout.LayoutParams(AppBarLayout.LayoutParams.MATCH_PARENT,AppBarLayout.LayoutParams.MATCH_PARENT);
collapsingToolbarlayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS |
AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
collapsingToolbarLayout.setContentScrimColor(ThemeUtil.resolveThemeColor(this, android.R.attr.colorPrimary));
} else {
collapsingToolbarLayout.setContentScrimColor(resources.getColor(R.color.primary_col));
}
appBarLayout.setFitsSystemWindows(true);
appBarLayout.addView(collapsingToolbarLayout, collapsingToolbarLayoutParams);
CollapsingToolbarLayout.LayoutParams primaryToolbarLayoutParams = new CollapsingToolbarLayout.LayoutParams(CollapsingToolbarLayout.LayoutParams.MATCH_PARENT, actionBarSize);
primaryToolbarLayoutParams.setCollapseMode(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_PIN);
primaryToolbarParams.gravity = Gravity.TOP;
collapsingToolbarLayout.addView(primaryToolbar, primaryToolbarLayoutParams);
LayoutInflater inflater = getLayoutInflater();
View imageLayout = inflater.inflate(R.layout.sell_augmented_list_item_background, collapsingToolbarLayout, false);
ImageView backgroundImage = (ImageView)imageLayout.findViewById(R.id.m_Image); backgroundImage.setBackgroundResource(R.drawable.green_stock);
collapsingToolbarLayout.addView(backgroundImage, 0);
View mLayout = inflater.inflate(R.layout.m_layout, collapsingToolbarLayout, false);
exampleText.setText("Some Text");
collapsingToolbarLayout.addView(mLayout, 1);
}
UPDATE This answer solved my issue: https://stackoverflow.com/a/37105296/4138919

Related

RecyclerView findViewHolderForAdapterPosition returns sometimes null

What I'm trying to do is make a video full screen from RecyclerView and while the video is playing, on pressing the minimize button to put it back into the list. Note that the state of the video is still on play after I minimize it.
Sometimes I get null object from position using .findViewHolderForAdapterPosition
My code:
LinearLayout mAdapterRow = null;
mAdapterRow = (LinearLayout) ((ViewHolder_Article_Video) mList.findViewHolderForAdapterPosition(mVideoPosition)).itemView;
if (mAdapterRow instanceof ViewGroup) {
ViewGroup v = (ViewGroup) nVideoFullScreenLayout.getChildAt(0);
if (v.getParent() != null )
((ViewGroup)v.getParent()).removeView(v); // <- fix
((ViewGroup) mAdapterRow).addView(v);
((ImageView) v.findViewById(R.id.mFullScreen)).setVisibility(View.VISIBLE);
((ImageView) v.findViewById(R.id.mExitFullScreen)).setVisibility(View.GONE);
nVideoFullScreenLayout.removeAllViews();
nVideoFullScreenLayout.setVisibility(View.GONE);
if (nVideoFullScreenLayout.getParent() != null)
((ViewGroup) nVideoFullScreenLayout.getParent()).removeView(nVideoFullScreenLayout);
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(uiOptions);
mHeader.setVisibility(View.VISIBLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mVideoFullScreen = false;
} else {
Log.d("dasdasdas","remove nu: "+ mAdapterRow.getTag());
}
Previously.
When I make the full screen video layout, I'm taking the layout with the video instance from inside the list(RecyclerView) and put it in a new dynamic LinearLayout with the MATCH_PARENT params and set SCREEN_ORIENTATION_LANDSCAPE.
Now the idea was to take the layout with the video instance (full screen) that is over the RecyclerView and put it back into his position.
For some reason findViewHolderForAdapterPosition doesn't always return the layout at the position of where I got the video in the first place.
Also I can see the item at position in list data adapter is not null.
Any idea is appreciated. Thanks in advance.
Finally found a solution that works for me. (this helped https://www.creospiders.com/2016/04/how-to-access-each-view-of-item-by.html , better than a post delay)
Setted the full screen layout on GONE at the beginning of the method so I can have no other view over the reycler, used getViewTreeObserver().addOnGlobalLayoutListener on recycler list to wait for the views to build and setted a boolean value that allows or not to cycle my view in onBindViewHolder (in case the video layout is at limit of being out of screen)
nVideoFullScreenLayout.setVisibility(View.GONE);
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(uiOptions);
mHeader.setVisibility(View.VISIBLE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mVideoFullScreen = false;
mAdapter.setCyclingVideoItem(false);
final LinearLayout[] mAdapterRow = {null};
mList.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mList.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mAdapterRow[0] = (LinearLayout) ((ViewHolder_Article_Video) mList.findViewHolderForAdapterPosition(mVideoPosition)).itemView;
setAndCleanLayouts(mAdapterRow[0]);
mAdapter.setCyclingVideoItem(true);
}
});

How to lock CollapsingToolbarLayout from Android support library

I am using Activity class with (usually) one fragment as a content. In the Activity I use CollapsingToolbarLayout as some kind of header for some information and everything works fine. But in some cases (when some fragments are attached) I don't want to show that info, I don't want CollapsingToolbarLayout to open on scroll.
What I want to achieve is to lock CollapsingToolbarLayout, prevent it from opening from the fragment. I am collapsing it programmatically with appBarLayout.setExpanded(false, true);
I came up with a different method as setting the nested scrolling flag only works when dragging the NestedScrollView. The appbar can still be expanded by swiping on the bar itself.
I set this up as a static function in "Utils" class. Obviously the flags you set upon unlocking will depend on which ones are relevant for your use case.
This function assumes you are are starting with an expanded toolbar
public static void LockToolbar(boolean locked, final AppBarLayout appbar, final CollapsingToolbarLayout toolbar) {
if (locked) {
// We want to lock so add the listener and collapse the toolbar
appbar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (toolbar.getHeight() + verticalOffset < 2 * ViewCompat.getMinimumHeight(toolbar)) {
// Now fully expanded again so remove the listener
appbar.removeOnOffsetChangedListener(this);
} else {
// Fully collapsed so set the flags to lock the toolbar
AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED);
}
}
});
appbar.setExpanded(false, true);
} else {
// Unlock by restoring the flags and then expand
AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
appbar.setExpanded(true, true);
}
}
Well, I managed to solve it myself. The trick is to disable nested scrolling behaviour with ViewCompat.setNestedScrollingEnabled(recyclerView, expanded);
As I am using one fragment in the activity as a content view and putting it on the backstack I simply check when backstack has changed and which fragment is visibile. Note that I NestedScrollView in every fragment to trigger collapsible toolbar. This is my code:
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
NestedScrollView nestedScrollView = (NestedScrollView)findViewById(R.id.nested_scroll_view);
int size = getSupportFragmentManager().getBackStackEntryCount();
if (size >= 1 && nestedScrollView != null) {
if (getSupportFragmentManager().getBackStackEntryAt(size - 1).getName().equals("SpotDetailsFragment")) {
Log.d(LOG_TAG, "Enabling collapsible toolbar.");
ViewCompat.setNestedScrollingEnabled(nestedScrollView, true);
} else {
Log.d(LOG_TAG, "Disabling collapsible toolbar.");
ViewCompat.setNestedScrollingEnabled(nestedScrollView, false);
}
}
}
});
This thread helped me a lot, where another possible solution is presented:
Need to disable expand on CollapsingToolbarLayout for certain fragments

Changing Toolbar and CollapsingToolbarLayout scroll flags programmatically

I have a single Activity android app with lots of fragments. When I'm showing a list screen I want to use the Toolbar with the, app:layout_scrollFlags="scroll|enterAlways" property. And in the detail fragments I want to use the CollapsingToolbarLayout with an image in it. Since it's a single Activity app, I have only one Toolbar. Is it possible to modify my layout programmatically to suit both cases?
Yes. Let's say you are going from the CollapsingToolbarLayout fragment to the Toolbar one.
You collapse your AppBarLayout using AppBarLayout.setExpanded(false);
You change the scroll flags to fit your needs.
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
p.setScrollFlags(...);
toolbar.setLayoutParams(p);
Same goes for the CollapsingToolbarLayout if necessary. I guess it should be something like:
collapsingToolbarParams.setScrollFlags(0); //no flags for ctl
toolbarParams.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS); //new flags for toolbar
I found it working
public void disableToolBarScrolling() {
CollapsingToolbarLayout toolbar = findViewById(R.id.collap_toolbar);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(0);
}
public void enableToolBarScrolling() {
CollapsingToolbarLayout toolbar = findViewById(R.id.collap_toolbar);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS);
}
Works for me.
public void enableToolBarScrolling(CollapsingToolbarLayout toolbar) {
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
params.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS);
toolbar.setLayoutParams(params);
}

Indeterminate Horizontal ProgressBar BELOW ActionBar using AppCompat?

I have been looking for answers on how to place the indeterminate horizontal progress bar below the action bar using AppCompat. I'm able to get the horizontal progress bar to appear, but it is at the top of the action bar. I want it under/below the action bar kind of like how gmail does it (except without the pull to refresh).
I used the following code to have the progress bar appear:
supportRequestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.main_activity);
setSupportProgressBarIndeterminate(Boolean.TRUE);
setSupportProgressBarVisibility(true);
but this places the horizontal progress bar at the top of the action bar. Anyone know how to place the progress bar below the action bar?
I faced a similar problem recently and solved it by creating my own progressbar and then aligning it by manipulating getTop() of the content view.
So first create your progressbar.
final LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 20); //Use dp resources
mLoadingProgressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
mLoadingProgressBar.setIndeterminate(true);
mLoadingProgressBar.setLayoutParams(lp);
Add it to the window (decor view)
final ViewGroup decor = (ViewGroup) getWindow().getDecorView();
decor.addView(mLoadingProgressBar);
And in order to get it to its correct position Im using a ViewTreeObserver that listens until the view has been laid out (aka the View.getTop() isnt 0).
final ViewTreeObserver vto = decor.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
final View content = getView(android.R.id.content);
#Override
public void onGlobalLayout() {
int top = content.getTop();
//Dont do anything until getTop has a value above 0.
if (top == 0)
return;
//I use ActionBar Overlay in some Activities,
//in those cases it's size has to be accounted for
//Otherwise the progressbar will show up at the top of it
//rather than under.
if (getSherlock().hasFeature((int) Window.FEATURE_ACTION_BAR_OVERLAY)) {
top += getSupportActionBar().getHeight();
}
//Remove the listener, we dont need it anymore.
Utils.removeOnGlobalLayoutListener(decor, this);
//View.setY() if you're using API 11+,
//I use NineOldAndroids to support older
ViewHelper.setY(mLoadingProgressBar, top);
}
});
Hope that makes sense for you. Good luck!

Shadow under ActionBar when using ActionBarSherlock

I'm using ActionBarSherlock. On Android 3.0+, there's a subtle shadow under the ActionBar. But not on older Androids.
The problem is, that the shadow set in android:windowContentOverlay appears at the top of the Activity Window. And on older Androids, ActionBar is part of the Window. So the shadow overlays the top of the ActionBar, instead of overlaying the content under ActionBar.
Is there a way to solve this, without manually inserting the shadow to every Activity's layout?
As #wingman hinted in the comments, I overrided setContentView(...) in my base Activity like this:
#Override
public void setContentView(int layoutResId) {
View view = getLayoutInflater().inflate(layoutResId, null);
setContentView(view);
}
#Override
public void setContentView(View view) {
int wrapContent = ViewGroup.LayoutParams.WRAP_CONTENT;
int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;
view.setLayoutParams(new ViewGroup.LayoutParams(matchParent, matchParent));
View shadow = new View(this);
shadow.setBackgroundResource(R.drawable.action_bar_shadow);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(matchParent,
wrapContent);
shadow.setLayoutParams(params);
RelativeLayout container = new RelativeLayout(this);
container.addView(view);
container.addView(shadow);
super.setContentView(container);
}

Categories

Resources