What is the wrong with this code?
row_height gives a value outside onLayoutChange* but gives zero inside CreateEvent
public class MainActivity extends AppCompatActivity {
private int row_height;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = (TextView) findViewById(R.id.textView33);
textView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
row_height = bottom - top;
textView.setText("" + row_height); //this show a correct value
}
});
float Start_time = (float) 6;
int Event_duration_in_min = 120;
int day_number = 3;
CreateEvent(Event_duration_in_min, Start_time, day_number - 1);
}
private void CreateEvent(final int event_duration_in_min, final float start_time, final int day) {
TextView textView = (TextView) findViewById(R.id.textView27);
textView.setText("" + row_height); // this show 0 !!
}
}
how to fix this ?
private void CreateEvent(final int event_duration_in_min, final float start_time, final int day) {
TextView textView = (TextView) findViewById(R.id.textView27);//any textview from the horizontal grid
textView.setText("" + row_height);
//Now creating events here
textView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
RelativeLayout RL = (RelativeLayout) findViewById(R.id.RL);
final int width = right - left;
float scale = getApplicationContext().getResources().getDisplayMetrics().density;//const code line
int pixels_h = (int) (event_duration_in_min * scale + 0.5f);//const code line
TextView tv = new TextView(getApplication().getApplicationContext());
if(row_height == 120) {
RelativeLayout.LayoutParams Params1 = new RelativeLayout.LayoutParams(width, pixels_h * 2);//const code line
tv.setLayoutParams(Params1);
}
else {
RelativeLayout.LayoutParams Params1 = new RelativeLayout.LayoutParams(width, pixels_h);//const code line
tv.setLayoutParams(Params1);
}
tv.setBackgroundResource(R.color.Red);
tv.setText("attttttttef");
tv.setX(day * width);
tv.setY((float) (start_time * row_height));
RL.addView(tv);
}
});
}
The layout for your view has not occurred by the time you call CreateEvent(), so the height will be zero. If you want to call CreateEvent() once your row_height is determined, put it inside your OnLayoutChangeListener, and check that your row_height is greater than zero.
Based on your result, CreateEvent is called before the OnLyoutChangeListener is triggered. Therefore, textView27's text is set before the row_height is assigned in the onLayoutChange (default 0).
Change the signature of your CreateEvent method to receive the row_height as a parameter and change this line to:
CreateEvent(Event_duration_in_min, Start_time, day_number - 1, row_height);
Related
I want to make a method that computes the margin values based on the parent's height and width. The code below outputs -1 for both height and width. How can I get the parent's height and width properly?
#RequiresApi(api = Build.VERSION_CODES.R)
public void setMarginsPercentages(Context context, #NotNull ConstraintLayout parent, #NotNull Object object, double leftPercentage, double topPercentage, double rightPercentage, double bottomPercentage) {
FrontEndObject item = getObjectType(object);
ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(parent.getLayoutParams());
int height = params.height; //outputs -1
int width = params.width; //outputs -1
int left = (int) (leftPercentage * width);
int right = (int) (rightPercentage * width);
int top = (int) (topPercentage * height);
int bottom = (int) (bottomPercentage * height);
item.setMargins(parent, object, left, top, right, bottom);
}
There are couple problems here, the parms.height will give you this value (https://developer.android.com/reference/android/view/ViewGroup.LayoutParams#MATCH_PARENT), which is -1. Not the constraint layout's height in pixel you are looking for.
Also, You cannot use the width/height/getMeasuredWidth/getMeasuredHeight on a View before the system renders it (typically from onCreate/onResume). But you could set a listener, to get the value after the system calculate the pixel for the MATCH_PARENT.
Try this at your fragment's onViewCreated()
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
myView = view.findViewById(R.id.rtt); # your constraint layout
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
int height = bottom - top;
int width = right - left;
Log.i("LOG", "height is" + height);
Log.i("LOG", "width is " + width);
}
});
}
Anyway, notice you only get this value after the view is rendered. At this point the view is already rendered, you might not be able to edit the margin. You might want to rethink your approach.
So I have a layout with adapterViewFlipper inside. And I set progress bar at the top of main layout view with addView() method, so when layout of adapterViewFlipper is ex: 4 then my progress bar is also 4. But instead every layout changes according to each progress bar, my progress bar run 4 of them at the same time. Here is the result of my code look like.
Here is my MainActivity:
public class MainActivity extends AppCompatActivity {
private AdapterViewFlipper mViewFlipper;
private MyAdapter mAdapter;
private LinearLayout mLayout;
private ProgressBar progressBar;
private int childCount;
private String[] values = {"satu", "dua", "tiga", "empat"};
private int[] colors = {Color.BLUE, Color.GRAY, Color.GREEN, Color.RED};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewFlipper = findViewById(R.id.viewflipper);
mLayout = findViewById(R.id.layoutParent);
mAdapter = new MyAdapter(values, colors);
mViewFlipper.setAdapter(mAdapter);
mViewFlipper.setFlipInterval(2500);
childCount = mViewFlipper.getCount();
mViewFlipper.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
if (mViewFlipper.getDisplayedChild() == childCount - 1) {
mViewFlipper.stopFlipping();
}
}
});
mViewFlipper.startFlipping();
for (int i = 0; i < childCount; i++) {
View view = LayoutInflater.from(this).inflate(R.layout.progress, null);
view.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1));
progressBar = view.findViewById(R.id.bar);
setMargins(progressBar, 0, 0, 8, 0);
setProgressMax(progressBar, 100);
setProgressAnimate(progressBar, 100);
mLayout.addView(progressBar);
}
}
private void setProgressMax(ProgressBar pb, int max) {
pb.setMax(max * 1000);
}
private void setProgressAnimate(ProgressBar pb, int progressTo)
{
ObjectAnimator animation = ObjectAnimator.ofInt(pb, "progress", pb.getProgress(), progressTo * 1000);
animation.setDuration(2500);
animation.setInterpolator(new LinearInterpolator());
animation.start();
}
private void setMargins (View view, int left, int top, int right, int bottom) {
if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
p.setMargins(left, top, right, bottom);
view.requestLayout();
}
}
}
What I really want is each progreess bar for each layout. For example, when first progress bar is done then second progress bar started and so on just like my adapterViewFlipper.
How can I fix that? thanks.
You should show Progreess Bar in addOnLayoutChangeListener
mViewFlipper.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
if (mViewFlipper.getDisplayedChild() == childCount - 1) {
mViewFlipper.stopFlipping();
} else {
View view = LayoutInflater.from(this).inflate(R.layout.progress, null);
view.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1));
progressBar = view.findViewById(R.id.bar);
setMargins(progressBar, 0, 0, 8, 0);
setProgressMax(progressBar, 100);
setProgressAnimate(progressBar, 100);
mLayout.addView(progressBar);
}
}
});
I've finally solve this problem.
Here's my solution:
private ProgressBar[] progressBar1;
progressBar1 = new ProgressBar[childCount];
for (int e = 0; e < childCount; e++) {
progressBar1[e] = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
progressBar1[e].setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));
setMargins(progressBar1[e], 0, 0, 8, 0);
setProgressMax(progressBar1[e], 100);
viewGroup = findViewById(R.id.layoutParent);
viewGroup.addView(progressBar1[e]);
}
mViewFlipper.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
if (mViewFlipper.getDisplayedChild() == childCount - 1) {
mViewFlipper.stopFlipping();
}
setProgressAnimate(progressBar1[mViewFlipper.getDisplayedChild()], 100);
}
});
mViewFlipper.startFlipping();
I have following code :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<Integer> data = new ArrayList();
for(int i=0; i<5; i++) {
data.add(i);
}
MyRecyclerViewAdapter adapter = new MyRecyclerViewAdapter(data);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
RecyclerView list = findViewById(R.id.list);
list.setAdapter(adapter);
list.setLayoutManager(layoutManager);
if (layoutManager.findLastCompletelyVisibleItemPosition() == adapter.getItemCount() - 1) {
}
}
findLastCompletelyVisibleItemPosition returns -1. What is the reason of that and how to fix it?
This is happening because you are trying to get last visible item during onCreate.
During onCreate, No View was measured/positioned yet. So, it is impossible to get that information right at onCreate
Edit
You can add a layout listener to be invoked after the RecyclerView is measured.
Something like:
recyclerView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
#Override
public void onLayoutChange(final View v, final int left,
final int top,
final int right,
final int bottom,
final int oldLeft,
final int oldTop,
final int oldRight,
final int oldBottom) {
int lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition();
if(lastVisibleItem > 0 && adapter.getItemCount() - 1) {
Log.d("TEST", "LastItem: " + lastVisibleItem);
// You can call this after you get your information to remove the listener
// This way, it won't be called everytime there's a re-layout
//recyclerView.removeOnLayoutChangeListener(this);
}
}
});
I use a RecyclerView with some dividers between items. I use a DividerDecorator to draw them. But on older android versions (API 8) it draws the complete background in my divider color. Here is my code:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private final Drawable mDivider;
private final int mHeight;
private int mOrientation;
public DividerItemDecoration( final Context context, final int orientation) {
mDivider = new ColorDrawable( context.getResources().getColor( R.color.divider) );
mHeight = dpToPx( 1f, context.getResources() );
setOrientation(orientation);
}
private int dpToPx( final float dp, final Resources resource ) {
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resource.getDisplayMetrics() );
return Math.max( 1, (int) px );
}
public void setOrientation( final int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
#Override
public void onDraw( final Canvas c, final RecyclerView parent, final RecyclerView.State state) {
if( mOrientation == VERTICAL_LIST ) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical( final Canvas c, final RecyclerView parent ) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
View child;
RecyclerView.LayoutParams params;
int top;
int bottom;
for( int position = 0; position < childCount; position++) {
child = parent.getChildAt(position);
params = (RecyclerView.LayoutParams) child.getLayoutParams();
top = child.getBottom() + params.bottomMargin;
bottom = top + mHeight;
mDivider.setBounds( left, top, right, bottom );
mDivider.draw(c);
}
}
public void drawHorizontal( final Canvas c, final RecyclerView parent ) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
View child;
RecyclerView.LayoutParams params;
int left;
int right;
for( int position = 0; position < childCount; position++) {
child = parent.getChildAt( position );
params = (RecyclerView.LayoutParams) child.getLayoutParams();
left = child.getRight() + params.rightMargin;
right = left + mHeight;
mDivider.setBounds( left, top, right, bottom );
mDivider.draw( c );
}
}
#Override
public void getItemOffsets( final Rect outRect, final View view, final RecyclerView parent, final RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
outRect.set( 0 /*left*/, 0 /*top*/, 0 /*right*/, mHeight /*bottom*/ );
} else {
outRect.set( 0 /*left*/, 0 /*top*/, mHeight /*right*/, 0 /*bottom*/ );
}
}
}
Here is my log from divider coordinates and they look correct:
DividerItemDecoration﹕ divider=0, left=0, top=66, right=240, bottom=67
DividerItemDecoration﹕ divider=1, left=0, top=133, right=240, bottom=134
DividerItemDecoration﹕ divider=2, left=0, top=200, right=240, bottom=201
DividerItemDecoration﹕ divider=3, left=0, top=267, right=240, bottom=268
I have a FrameLayout, when I try to get its params like this:
ViewGroup.LayoutParams params = cameraPreviewFrameLayout.getLayoutParams();
int layoutHeight = params.height;
int layoutWidth = params.width;
Here, layoutHeight is -2 and layoutWidth is -1. This is my XML:
<FrameLayout
android:id="#+id/liveActivity_cameraPreviewFrameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
I am performing this action from onCreate, but also I tried to to it through onStart(), with the same result. Obviously the height and width of this layout is not these values.
How can I retrieve the size of the layout effectively?
The values returned are correct, because:
-2 stands for LayoutParams.WRAP_CONENT
-1 stands for LayoutParams.MATCH_PARENT
If you want to get exact values, you'll need to use the methods getHeight and getWidth. However those methods can only be used once the layout has been measured, so delay your call like this:
cameraPreviewFrameLayout.post(new Runnable() {
#Override
public void run() {
View v = cameraPreviewFrameLayout;
Log.e("TAG", v.getWidth() + ":" + v.getHeight());
}
});
Proper way to getting size of layout after or inside onLayoutChange method call
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final FrameLayout layout = (FrameLayout) findViewById(R.id.liveActivity_cameraPreviewFrameLayout);
layout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
int width = right - left;
int height = bottom - top;
Log.v("TAG", String.format("%d - %d", width, height));
//Or
Log.v("TAG", String.format("%d - %d", layout.getWidth(), layout.getHeight()));
//And after this method call, calling layout.getWidth() and layout.getHeight() will give right values
}
});
}