i have a method i created that is called throughout my project, i made it because i have a Crouton (toast) that tells a user to activate there account, or it will also alert them when there is no valid internet connection... And i dont want it to interfere with the top of my View because theres user actions that are important there. Im using a RelativeLayout
First off, this piece of code doesnt work anyways, but as i was fixing it i realized my bar i have at the bottom to switch between different Activities is now gone because it was slid down, im thinking i can just resize the hieght.
can anyone point me int he right direction for two things, one resizing height instead of sliding the whole view down, and two, help me fix the crash im getting, which occurs on setLayoutParam
i call this like this teknoloGenie.slideViewDown("100", findViewById(R.id.RootView));
public void slideViewDown(final String distX, final View view) {
final TranslateAnimation slideDown = new TranslateAnimation(0, 0, 0, Float.parseFloat(distX));
slideDown.setDuration(500);
slideDown.setFillEnabled(true);
slideDown.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override public void onAnimationEnd(Animation animation) {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)view.getLayoutParams();
params.addRule(RelativeLayout.CENTER_HORIZONTAL, -1);
params.setMargins(0, view.getTop()+Integer.parseInt(distX), 0, 0);
view.setLayoutParams(params);
view.requestLayout();
}
});
view.startAnimation(slideDown);
}
If you want to animate the height of a View, you need to write your own custom-animation and modify the LayoutParams of your animated view.
In this example, the animation animates the height of the animated View.
This is how it could look like:
public class ResizeAnimation extends Animation {
private int startHeight;
private int deltaHeight; // distance between start and end height
private View view;
/**
* constructor, do not forget to use the setParams(int, int) method before
* starting the animation
* #param v
*/
public ResizeAnimation (View v) {
this.view = v;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (startHeight + deltaHeight * interpolatedTime);
view.requestLayout();
}
/**
* set the starting and ending height for the resize animation
* starting height is usually the views current height, the end height is the height
* we want to reach after the animation is completed
* #param start height in pixels
* #param end height in pixels
*/
public void setParams(int start, int end) {
this.startHeight = start;
deltaHeight = end - startHeight;
}
#Override
public boolean willChangeBounds() {
return true;
}
}
In code, create a new Animation and apply it to the RelativeLayout that you want to animate:
View v = findViewById(R.id.youranimatedview);
// getting the layoutparams might differ in your application, it depends on the parent layout
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
ResizeAnimation a = new ResizeAnimation(v);
a.setDuration(500);
// set the starting height (the current height) and the new height that the view should have after the animation
a.setParams(lp.height, newHeight);
v.startAnimation(a);
To your LayoutParams problem:
My guess is that you are getting a ClassCastException because you are not using the correct LayoutParams class. If your animated view for example is contained by a RelativeLayout, you can only set RelativeLayout.LayoutParams to it. If your View is contained by a LinearLayout, you can only set LinearLayout.LayoutParams for your View.
Related
I know how it can be applied to the toolbar w.r.t. any scrollView in Coordinator layout.
I wanna do the same thing with the button w.r.t. RecycleView.
Just like the bottomNav in the app Pinterest.
Thanks!
A simple way that comes to mind is to use the TranslateAnimation provided by Android and to check if the RecyclerView is being scrolled. Here is what you need to do
Add a scroll listener to the RecyclerView and check if the scrolling direction is downward or upward
YourRecyclerView.addScrollListener(new ScrollListener() {
someScrollListenerMethod() {
if (scrolling up) {
showButton();
} else {
hideButton();
}
}
);
Create an animation for sliding the button up and down.
slideDownAnimation = TranslateAnimation(0f, 0f, 0f, ScreenHeight)
slideDownAnimation.duration = 1000f //1 second
slideUpAnimation = TranslateAnimation(0f, 0f, ScreenHeight, 0f)
slideUpAnimation.duration = 1000f //1 second
Create the show and hide button methods which will show and hide the button
private void showButton() {
yourButton.visibility = View.VISIBLE;
yourButton.startAnimation(slideUpAnimation);
}
private void hideButton() {
yourButton.visibility = View.INVISIBLE;
yourButton.startAnimation(slideDownAnimation);
}
Just a note, the code shared in step 1 is not the actual library code (I could not remember the exact code) but you can easily find it out.
Here's the applied code as explained by Bilal. Just in case anybody need it,
private void buttonSlideAnimation() {
addButton = findViewById(R.id.bottom_add_button);
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
final TranslateAnimation slideDownAnimation = new TranslateAnimation(0f, 0f, 0f, height);
final TranslateAnimation slideUpAnimation = new TranslateAnimation(0f, 0f, height, 0f);
slideDownAnimation.setDuration(1000);
slideUpAnimation.setDuration(1000);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy>0){
addButton.setVisibility(View.INVISIBLE);
addButton.startAnimation(slideDownAnimation);
} else {
addButton.setVisibility(View.VISIBLE);
addButton.startAnimation(slideUpAnimation);
}
}
});
}
I have a kids drawing app, but the gestures in Q keep quitting the app. I tried removing the system gesture but it does not seem to work.
In this case, I am trying to exclude the whole screen from system gesture:
List<Rect> exclusionRects = new ArrayList();
public void onLayout(boolean changedCanvas, int left, int top, int right, int bottom) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
exclusionRects.clear();
exclusionRects.add(new Rect(left, top, right, bottom));
setSystemGestureExclusionRects(exclusionRects);
}
}
As stated by Google:
First, to ensure reliable and consistent operation, there’s a 200dp vertical app exclusion limit for the Back gesture.
https://android-developers.googleblog.com/2019/08/final-beta-update-official-android-q.html
This means that the operating system will not allow you to override the back gesture fully.
This makes sense as it is a fairly fundamental part of the operating system and they probably don't want to allow apps that remove the gesture entirely, as it is bad for consistency across the platform
Try this.
Define this code in your Utils class.
static List<Rect> exclusionRects = new ArrayList<>();
public static void updateGestureExclusion(AppCompatActivity activity) {
if (Build.VERSION.SDK_INT < 29) return;
exclusionRects.clear();
Rect rect = new Rect(0, 0, SystemUtil.dpToPx(activity, 16), getScreenHeight(activity));
exclusionRects.add(rect);
activity.findViewById(android.R.id.content).setSystemGestureExclusionRects(exclusionRects);
}
public static int getScreenHeight(AppCompatActivity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
return height;
}
public static int dpToPx(Context context, int i) {
return (int) (((float) i) * context.getResources().getDisplayMetrics().density);
}
Check if your layout is set in that activity where you want to exclude the edge getures and then apply this code.
// 'content' is the root view of your layout xml.
ViewTreeObserver treeObserver = content.getViewTreeObserver();
treeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
SystemUtil.updateGestureExclusion(MainHomeActivity.this);
}
});
We are adding the view width to 16dp to trigger the code when user swipe right from left edge & height to screen height to do it fully left side.
I want to add a new view to a FrameLayout, but it does only work "sometimes".
For example: I have set an OnClickListener to my layout (inside the onCreate-method of the activity) which adds a new view to the layout (this works). In the run-method of the activity i am using exactly the same code to add such a view, but the view does not show up. I don't know why...
Here is some code:
OnClickListener, which adds a new view to the layout and the view actually shows up. layout is the FrameLayout, obstacles is an object-list.
layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
obstacles.add(new Obstacle(getBaseContext(), 0, (int) ((scale * 570) / 8), scale));
layout.addView(obstacles.get(obstacles.size() - 1).view);
}
});
if statement IS true, the new view is not being displayed, but the size of obstacles and the layout's childcount increase both by 1
if(checkForNew()){
obstacles.add(new Obstacle(getBaseContext(), 0, (int) ((scale * 570) / 8), scale));
layout.addView(obstacles.get(obstacles.size() - 1).view);
}
Constructor of Obstacle
public Obstacle(Context con, int left, int top, float scale) {
view = new ImageView(con);
view.setBackgroundColor(Color.rgb(0, 0, 0));
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams((int)(scale*150), (int)(scale*20));
params.setMargins(left,top,0,0);
view.setLayoutParams(params);
view.setMaxWidth(view.getLayoutParams().width);
view.setMaxHeight(view.getLayoutParams().height);
}
This is moving the views:
public void Move( View view, float speed, float scale){
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(view.getWidth(), view.getHeight());
params.setMargins(view.getLeft(), view.getTop() - (int) (scale * -speed), 0, 0);
view.setLayoutParams(params);
}
This is a FrameLayout and views will be on top of each other.
You do not seem to be changing the scale anywhere, and your margin is always fixed with 0, (scale * someValue)
You need to apply a different scale and/or use different top/left parameters for the views you add.
I have a RecyclerView with Expandable Child Views, when the child ViewGroup is clicked it inflates an amount of views animating the ViewGroup height from 0 to the measured viewgroup height, like the following gif:
The problem is: I'm calling smoothScrollToPosition on recyclerView, it smooth scroll to the view position, but it considers the current view height, which is still not expanded, in the above gif i'm touching on the under view of the recyclerview, which dont scroll to position because the view is already visible, but when i touch again (calling the smoothscrolltoposition again) it scroll the view to the correct position, because the view is already expanded.
Is there any approach to scroll the view to the top of screen or just scroll to make content visible?
For references:
This is the method called to inflate the views:
collapsible_content.removeAllViews();
for(int i = 0; i < 5; i++) {
View link_view = getLayoutInflater().inflate(R.layout.list_item_timeline_step_link, collapsible_content, false);
TextView text = (TextView) link_view.findViewById(R.id.step_link_text);
text.setText("Test");
collapsible_content.addView(link_view);
}
And this is my method to expand:
public void toggle() {
collapsible_content.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Animation a;
if (mExpanded) {
a = new ExpandAnimation(collapsible_content.getLayoutParams().height, 0);
} else {
a = new ExpandAnimation(collapsible_content.getLayoutParams().height, getMeasuredHeight());
}
a.setDuration(mAnimationDuration);
collapsible_content.startAnimation(a);
mExpanded = !mExpanded;
}
And the animation:
private class ExpandAnimation extends Animation {
private final int mStartHeight;
private final int mDeltaHeight;
public ExpandAnimation(int startHeight, int endHeight) {
mStartHeight = startHeight;
mDeltaHeight = endHeight - startHeight;
}
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
final int newHeight = (int) (mStartHeight + mDeltaHeight *
interpolatedTime);
collapsible_content getLayoutParams().height = newHeight;
if (newHeight <= 0) {
collapsible_content setVisibility(View.GONE);
} else {
collapsible_content setVisibility(View.VISIBLE);
}
collapsible_content requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
}
My solution was to constant check for view bottom within applyTransformation method, and compare it with RecyclerView height, if the bottom get higher than the RV height, i scroll by the diff values:
final int bottom = collapsible_content.getBottom();
final int listViewHeight = mRecyclerView.getHeight();
if (bottom > listViewHeight) {
final int top = collapsible_content.getTop();
if (top > 0) {
mRecyclerView.smoothScrollBy(0, Math.min(bottom - listViewHeight + mRecyclerView.getPaddingBottom(), top));
}
}
The trick was to use Math.min to get the view top, so it don't scroll up making the top not visible.
Solution based on ListViewAnimations
Add an animationlistener and start the scrolling of the recyclerview after the expanding animation is finished.
I am adding an adMob banner to my app successfully. When banner appears I need to get its height in order to resize all layout elements. I am using the event onReceivedAd, that is properly fired. However, alturaBanner is = 0. Then, how to get its height? thank you.
/** Called when an ad is received. */
#Override
public void onReceiveAd(Ad ad)
{
adView.setVisibility(View.VISIBLE);
int alturaBanner = adView.getHeight();
RelativeLayout.LayoutParams params1 = (android.widget.RelativeLayout.LayoutParams) browse2
.getLayoutParams();
params1.setMargins(0, alturaBanner, 0, 0);
Log.d(LOG_TAG, "onReceiveAd");
Toast.makeText(this, "onReceiveAd", Toast.LENGTH_SHORT).show();
}
You can get the height of any type of banner before it is even added to the layout.
int heightPixels = AdSize.SMART_BANNER.getHeightInPixels(this);
or
int heightPixels = AdSize.FULL_BANNER.getHeightInPixels(myContext);
or for DIP's
int heightDP = AdSize.BANNER.getHeight();
So for your need, you could do this:
/** Called when an ad is received. */
#Override
public void onReceiveAd(Ad ad)
{
adView.setVisibility(View.VISIBLE);
int alturaBanner = AdSize.BANNER.getHeight(); // This gets the adsize, even if the view is not inflated.
RelativeLayout.LayoutParams params1 = (android.widget.RelativeLayout.LayoutParams) browse2
.getLayoutParams();
params1.setMargins(0, alturaBanner, 0, 0);
Log.d(LOG_TAG, "onReceiveAd");
Toast.makeText(this, "onReceiveAd", Toast.LENGTH_SHORT).show();
}
Just change AdSize.BANNER to AdSize.SMART_BANNER or whatever banner type your using.
Add Sizes Get Height
getting the height of the view before it was prepared will always return you 0 .
use the next code in order to get its correct size , no matter which device/screen you have:
private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable)
{
final ViewTreeObserver vto = view.getViewTreeObserver();
final OnPreDrawListener preDrawListener = new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
runnable.run();
final ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeOnPreDrawListener(this);
return true;
}
};
vto.addOnPreDrawListener(preDrawListener);
}
inside the given runnable , you can query the real size of the view.
alternatively , you can use addOnGlobalLayoutListener instead of addOnPreDrawListener if you wish.
another approach is to use onWindowFocusChanged (and check that hasFocus==true) , but that's not always the best way ( only use for simple views-creation, not for dynamic creations)
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
I use the following method to get AdView's height:
adView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int height = adView.getHeight();
if (height > 0) {
// now the height is gotten, you can do things you want
}
}
});
onGlobalLayout() is triggered when the global layout state or the visibility of views within the view tree changes.
Actually, you don't need to wait for appearance of adview to get adMob banner height if you are using smart banner type for showing banner ads.
Use Smart banner as it automatically decides the height of ad based on device size. Use full width of screen to show the ad.
From android developers site:
Smart Banners are ad units that will render screen-wide banner ads on any screen size across different devices in either orientation. Smart Banners help deal with increasing screen fragmentation across different devices by "smartly" detecting the width of the phone in its current orientation, and making the ad view that size.
Three ad heights (in dp, density-independent pixel) are available:
32 - used when the screen height of a device is less than 400
50 - used when the screen height of a device is between 400 and 720
90 - used when the screen height of a device is greater than 720
Now, get the height of AdView and adjust the margin of the layout where you wish to place the banner ad. Once the ad is loaded (by overriding on onAdLoaded API), you know the height using below method:
public static int getAdViewHeightInDP(Activity activity) {
int adHeight = 0;
int screenHeightInDP = getScreenHeightInDP(activity);
if (screenHeightInDP < 400)
adHeight = 32;
else if (screenHeightInDP <= 720)
adHeight = 50;
else
adHeight = 90;
return adHeight;
}
public static int getScreenHeightInDP(Activity activity) {
DisplayMetrics displayMetrics = ((Context) activity).getResources().getDisplayMetrics();
float screenHeightInDP = displayMetrics.heightPixels / displayMetrics.density;
return Math.round(screenHeightInDP);
}
I had the same need to be able to display my own ads when AdMob fails to receive an ad or receives an empty ad (height=0).
I use the following code based on the fact that an AdView extends RelativeLayout:
mAdMobView = new AdView(pActivity, AdSize.SMART_BANNER, Constants.ADMOB_AD_UNIT_ID);
mAdMobView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
#Override
public void onLayoutChange(final View pV, final int pLeft, final int pTop, final int pRight, final int pBottom, final int pOldLeft, final int pOldTop, final int pOldRight, final int pOldBottom) {
final float lAdHeight = mAdMobView.getHeight();
if (lAdHeight == 0) {
Debug.i(LOG_TAG, "mAdMobView.onLayoutChange(...) mAdMobView.height='" + lAdHeight + "'. AdMob returned an empty ad !");
// Show custom ads
} else {
Debug.d(LOG_TAG, "mAdMobView.onLayoutChange(...) mAdMobView.height='" + lAdHeight + "'");
// Make AdView visible
}
}
});
mAdMobView.setAdListener(new AdListener() {
#Override public void onReceiveAd(final Ad pAd) {
Debug.d(LOG_TAG, "onReceiveAd(...) AdMob ad received (mAdMobView.visibility='" + mAdMobView.getVisibility() + "').");
}
#Override public void onPresentScreen(final Ad pAd) {
Debug.d(LOG_TAG, "onPresentScreen(...)");
}
#Override public void onLeaveApplication(final Ad pAd) {
Debug.d(LOG_TAG, "onLeaveApplication(...)");
}
#Override public void onDismissScreen(final Ad pAd) {
Debug.d(LOG_TAG, "onDismissScreen(...)");
}
#Override
public void onFailedToReceiveAd(final Ad pAd, final ErrorCode pErrorCode) {
Debug.i(LOG_TAG, "onFailedToReceiveAd(...) AdMob ad error (" + pErrorCode + ").");
// Show custom ads
}
});
The code in 'onLayoutChange' is executed every time Admob receives a new ad.
EDIT: My answer is not proper since this method was added with the API 11... I changed it for the use of onPreDraw() as explained in the previous answer.