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.
Related
I have the following code to generate button objects with a set text, onClick command (more or less), height, width, and margins
private Button generateButton(String text, char command, int height, int width, int left, int top){
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(getDP(width),getDP(height));
lp.setMargins(getDP(left),getDP(top),0,0);
Button button = new Button(this.getContext());
button.setText(text);
button.setLayoutParams(lp);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d("Command",""+ command);
}
private char command;
private View.OnClickListener init(char var){
command = var;
return this;
}
}.init(command));
return button;
}
Along with this method to get the dp
private int getDP(int size){
return (int) (size * this.getContext().getResources().getDisplayMetrics().density);
}
But when I run the app the button have the proper height and width, along with the proper text and onclick action, but they have no margins, they're all bunched up into one corner
The view hierchy according to the Layout Inspector in Android studio goes
DecorView
LinearLayout
FrameLayout
FitWindowLinearLayout
ContentFrameLayout
CoordinatorLayout
ViewPager
ConstraintLayout
ConstraintLayout
Buttons
And from what I've read online, LayoutParams has to be from the same class as the layout, as is LinearLayout.LayourParams, or ConstraintLayout.LayoutParams and I've tried all the layout types that made sense to me and still no margins
It might be worth noting this is in a fragment
For views that are children of ConstraintLayout, you need to add horizontal and vertical constraints. Otherwise they layout will not know how to place them, and places them at the top left corner.
You can use the ConstraintLayout.LayoutParams, and set the constraints by
lp.leftToLeft, lp.leftToRight and similar methods here
So for example if you want to place these buttons one after the other vertically, you will constraint each button horizontally to the parent, and vertically to the previous button.
public void setMargins (int left, int top, int right, int bottom)
This is the signature for setMargins method. You call the method with right = 0 and bottom = 0. So you will not have the margins on right and bottom. Try to set some values for right and bottom.
You should use LayoutParams to set your button margins:`
LayoutParams params = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT
);
params.setMargins(left, top, right, bottom);
yourbutton.setLayoutParams(params);
Depending on what layout you're using you should use RelativeLayout.LayoutParams or LinearLayout.LayoutParams.
And to convert your dp measure to pixel, try this:
Resources r = mContext.getResources();
int px = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
yourdpmeasure,
r.getDisplayMetrics()
);
`
I have a LinearLayout inside a LinearLayout and child linearlayout have its layout params where height and width are defined as
LinearLayout linearLayout=new LinearLayout(context);
LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(50,50);
linearLayout.setLayoutParams(lp);
now after view created i am trying to get height of a view from getHeight() and getMeasuredHeight() but both methods returning 0 . ?
int heightofView=linearLayout.getHeight();
int heightofview=linearLayout.getMeasuredHeight();
why both these methods are returning 0 height of a view ? when these methods return the actual result ? how to get height of my created view ?
Try this code.
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = imageView.getWidth();
int height = imageView.getHeight();
//you can add your code here on what you want to do to the height and width you can pass it as parameter or make width and height a global variable
imageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
Do as below -
linearLayout.post(new Runnable() {
#Override
public void run() {
int linearLayoutHeight = linearLayout.getHeight();
Log.e("MS", "linearLayout Ht = " + linearLayoutHeight);
}
});
I'd like to do something like the following:
rootLayout.getLayoutParams().height = 100;
Currently I have this line in my 'loadData' method. The problem is - 'layoutParams' seems to be null until sometime after 'loadData' has been called (but before it is displayed obviously).
Is there somewhere I can place this line where the layoutParams will have been instantiated, but still be before the view is shown for the first time?
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
rootLayout.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
} else {
rootLayout.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
rootLayout.getLayoutParams().height = 100;
rootLayout.requestLayout();
}
});
You should consider to set layoutParams of this view as
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, 100));
rootLayout.setLayoutParams(lp);
So You can set layoutParams height as 100 and assuming that your parent view as linear layout,if it's not, then you should use its layoutparams
on the docs you can check all the callbacks the view have that you can override https://developer.android.com/reference/android/view/View.html
if your view is inside an XML layout with the LayoutParams specified in there, you can/should put your code inside onFinishInflate
#Override
public void onFinishInflate() {
}
if you're doing all this programmatically, you can override the layout pass
#Override
public void onLayout (boolean changed, int left, int top, int right, int bottom){
// be carefull that this get's called several times
}
alternatively (a nicer approach in my opinion), you can trick the onMeasure of your view to have the size you want. For example, to have a view with a maxWidth
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// apply max width
int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
if (maxWidth > 0 && maxWidth < measuredWidth) {
int measureMode = MeasureSpec.getMode(widthMeasureSpec);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, measureMode);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
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 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.