I'm trying to get StatusBarView (I need it for activity shared element transition animation).
I use the following code in my activity:
View decorView = getWindow().getDecorView();
View statusBar = decorView.findViewById(android.R.id.statusBarBackground);
But statusBar is always null.
I tried this code after onCreate() and after onResume() - no effect.
What's wrong?
Snippet for those who just need a clean answer without reading the whole topic linked by T.Vert
final View decor = getWindow().getDecorView();
decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
decor.getViewTreeObserver().removeOnPreDrawListener(this);
View statusBar = decor.findViewById(android.R.id.statusBarBackground);
return true;
}
});
Related
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);
}
});
I'm trying to animate the add/remove actions of the views of an LinearLayout that has it's height set as wrap_content.
Currently I've tried setting android:animateLayoutChanges="true" for the LinearLayout and programmatically enabling the transitions like this:
LayoutTransition transition = new LayoutTransition();
transition.setDuration(300);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
transition.enableTransitionType(LayoutTransition.APPEARING);
transition.enableTransitionType(LayoutTransition.DISAPPEARING);
transition.setAnimateParentHierarchy(true);
}
container.setLayoutTransition(transition);
The Appearing seems to work very smooth and it animates and resizes as I want.
The issue I'm having is with Disappearing as the LinearLayout container is resized before the remove animation is finished.
I've also tried playing around with the setAnimateParentHierarchy() and it doesn't seem to really affect on how and when the resize of the container is done.
A simple solution is to set a layout transition to the first ancestor which does not use wrap_content.
The following code finds the correct anscestor, sets the required animation and restore the original LayoutTransition (if any existed) at the end of the animation.
Call this method in your code where you perform the add/remove actions.
None of your code which is included in the question is required.
Note: This solution will only work for SDK >= JELLY_BEAN.
private static void setHeightChangeAnimation(ViewGroup animatedLayout) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ViewGroup vg = animatedLayout;
while (vg instanceof ViewGroup) {
vg = (ViewGroup) vg.getParent();
if (vg instanceof ViewGroup && vg.getLayoutParams().height != LayoutParams.WRAP_CONTENT) {
LayoutTransition animatedLayoutLt = animatedLayout.getLayoutTransition();
LayoutTransition lt = new LayoutTransition();
lt.enableTransitionType(LayoutTransition.CHANGING);
lt.setDuration(animatedLayoutLt.getDuration(LayoutTransition.CHANGE_APPEARING));
lt.setStartDelay(LayoutTransition.CHANGING, animatedLayoutLt.getStartDelay(LayoutTransition.APPEARING));
final ViewGroup finalVg = vg;
final LayoutTransition oldLt = finalVg.getLayoutTransition();
lt.addTransitionListener(new LayoutTransition.TransitionListener() {
public void startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {}
public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
finalVg.setLayoutTransition(oldLt);
}
});
finalVg.setLayoutTransition(lt);
break;
}
}
}
}
And use this to call:
setHeightChangeAnimation(yourAnimatedLinearLayout);
I have a fragment that uses the new CoordinatorLayout/AppBarLayout/CollapsingToolbarLayout paradigm, and I'd like to be able to detect when the collapsing toolbar is fully expanded so that I can perform an operation on the entire fragment it's in, e.g. popping the fragment off the stack and going to a new one, dismissing the fragment. I have the dismissing code working, I just need to know when and when not to use it.
I've experimented a bit with AppBarLayout.OnOffsetChangedListener, but didn't have much luck. Is there a way to use it to determine when things are completely expanded, or is there a more preferred method someone knows about?
Thanks in advance!
EDIT:
I also see there are a couple implementations for AppBarLayout.setExpanded(...), however not AppBarLayout.getExpanded() or something similar, so I'm stumped there too.
It doesn't look like there's anything in the APIs, but the following seems to be working for me. It might need testing.
boolean fullyExpanded =
(appBarLayout.getHeight() - appBarLayout.getBottom()) == 0;
Edit: The above solution does seem to work, but since I wanted to test this condition when the appbar was scrolled, I ended up using the following solution with OnOffsetChangedListener.
class Frag extends Fragment implements AppBarLayout.OnOffsetChangedListener {
private boolean appBarIsExpanded = true;
private AppBarLayout appBarLayout;
#Override
public void onActivityCreated(Bundle state) {
super.onActivityCreated(state);
appBarLayout = (AppBarLayout) getActivity().findViewById(R.id.app_bar);
}
#Override
public void onResume() {
super.onResume();
appBarLayout.addOnOffsetChangedListener(this);
}
#Override
public void onStop() {
super.onStop();
appBarLayout.removeOnOffsetChangedListener(this);
}
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
appBarIsExpanded = (verticalOffset == 0);
}
}
My solution is based on creating a custom view. First create a class extending the native AppBarLayout:
public class CustomAppBar extends AppBarLayout { ....
Then inside the class set an addOnOffsetChangedListener like this:
this.addOnOffsetChangedListener...
You can do the above by setting in the constructor or maybe by calling a method inside the constructor. So you need the constructor, remember to use the constructor with 2 params to be able to added to the xml:
public CustomAppBar(Context context, AttributeSet attrs) {
super(context, attrs);
//You can set the listener here or maybe call the method that set the listener
}
Then we have to get access to the state of the view, so create a private boolean inside your custom view class, and set it to true or false if your view start expanded or collapsed, in this case my view is by default expanded:
private boolean isExpanded = true;
Now you have to update the state of that boolean:
this.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (verticalOffset == 0) {
isExpanded = true;
} else {
isExpanded = false;
}
}
});
Next step is to get the state of the boolean by using a getter inside the CustomAppBar class
public boolean isExpanded() {
return isExpanded;
}
The next is go to your xml, use your custom view there, then in the Acivity or Fragment get the view and use the method to know the AppBar status
I know, that it maybe a bit late, but exploring the source code of the AppBArLayout I have found, that the AppBarLayout.OnOffsetChangedListener just translates the value of the int getTopAndBottomOffset() of the AppBar's Behaviour.
So at any time you can just use this code to define whether an AppBarLayout expanded or not:
public boolean isAppBArExpanded(AppBarLayout abl) {
final CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) abl.getLayoutParams()).getBehavior();
return (behavior instanceof AppBarLayout.Behavior) ? (((AppBarLayout.Behavior) behavior).getTopAndBottomOffset() == 0) : false;
}
I want to show an animation of a TextView appearing when an activity is created. What I want is to show the activity without the TextView and then the TextView appearing (ideally, flying from outside) in its final position without user interaction.
I've tried to use the transition framework from API level 19 by having the TextView with visibility gone in the XML layout and setting it to visible in onCreate() with this code:
ViewGroup layout = (ViewGroup) findViewById(R.id.main_layout);
TransitionManager.beginDelayedTransition(layout, new ChangeBounds());
textView.setVisibility(View.VISIBLE);
This doesn't work. However, if I don't do this in onCreate() but as a response to a button click, it works. I think that the problem is that the layout is not created yet, so when I set the visibility to visible in the onCreate(), the layout is created with the final state and there aren't two scenes for the TransitionManager to work.
I've tried putting the code in onPause() but the result was the same. Any ideas how this should be done?
Try onWindowFocusChanged() like this
#Override
public void onWindowFocusChanged(boolean hasFocus) {
if(hasFocus){
// start your animation here
}
}
You should do this in onResume() after the layout has been created. Initially the visibility would be Invisible and then the view will animate.
try like this:
ViewGroup layout = (ViewGroup) findViewById(R.id.main_layout);
layout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
TransitionManager.beginDelayedTransition(layout, new ChangeBounds());
textView.setVisibility(View.VISIBLE);
}
});
because your view should be able to animate only after it's creation
I am working on a fragment and I want to get the dimension of a layout contained in the xml fragment layout.
When I try the code
RelativeLayout myLayout = view.findViewById(R.id.myLayout);
myLayout.getHeight();
it returns 0.
I need these dimensions to put inside myLayout other objects.
I try to use:
myLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener(){
#Override
public void onGlobalLayout() {
mHeight = myLayout.getHeight();
mWidth= myLayout.getWidth();
System.out.println("width: "+mWidth+" height: "+mHeight);
}
});
but this code is invoke a lot of time and I don't know exactly when it is execute.
I need these dimensions into public void onActivityCreated () method. Is it possible?
There's a cleaner solution to this, just use the View.post() method on your fragment's root view, and you can call getMeasuredHeight()/getMeasuredWidth() and get the actual values.
E.g.
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.myfragment, container, false);
root.post(new Runnable() {
#Override
public void run() {
// for instance
int height = root.getMeasuredHeight();
}
});
return root;
}
Very neat and tidy and no messy mucking about with the ViewTreeObserver.
I've only tested this with android-23 devices, but the API has had this method since level 1.
Anyway, WFM.
The addOnGlobalLayoutListener will be called whenever small change of the view happened. So you need to remove this listener from the view.
Simple usage:
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){
if (Build.VERSION.SDK_INT < 16) {
v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
} else {
v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
I would suggest you to check :
if (myLayout.getHeight()>0 && myLayout.getWidth()>0) {
// Do some code...
removeOnGlobalLayoutListener(..)
}
In onActivityCreated, the view hasn't been measured yet.
So no, you can't get the measurements from there. onGlobalLayout is a good place to do it. Layouts happen directly after measurement.
You can remove your listener afterwards, if you only want to do it once. Note that layouts can change size though (soft keyboards appearing, for example)