How to get each view from addView() method dynamically? - android

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();

Related

Cannot start this animator on a detached view

Can anyone tell me whats wrong with this?
View view = findViewById(R.id.thumbnail_image_header);
// thumbnail_image_header is an imageView
int cx = (view.getLeft() + view.getRight()) / 2;
int cy = (view.getTop() + view.getBottom()) / 2;
// get the final radius for the clipping circle
int dx = Math.max(cx, view.getWidth() - cx);
int dy = Math.max(cy, view.getHeight() - cy);
float finalRadius = (float) Math.hypot(dx, dy);
// Android native animator
Animator animator =
ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(1500);
animator.start();
Add animation under addOnLayoutChangeListener of MainLayout .
mainView.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) {
view.removeOnLayoutChangeListener(this);
//Add circular revel animation on activity start
mainView.post(new Runnable() {
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public void run() {
//Your Animation Code
}
});
}
});
You can check if the view is currently attached or not. If not add an OnAttachStateChangeListener and start the animation as soon as the view is attached.
if (view.isAttachedToWindow()) {
// startAnimation..
} else {
view.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
v.removeOnAttachStateChangeListener(this);
// startAnimation..
}
#Override
public void onViewDetachedFromWindow(View v) {
}
});
}

Why are the subviews out of the bounds of my custom View not drawn?

I implement a custom SpinNumberView: it is square shaped (say 40x40), it has a vertical LinearLayout as a subview, within this linear layout are a bunch of 40x40 cells stacked vertically. I want to animate the cells to scroll vertically by changing offsetY of the LinearLayout.
But there is one problem: only the cell initially in bounds (the first) is rendered, the cells outside of the bounds are not drawn, so when I animate the LinearLayout to scroll, the linear layout is spinning, but only the first cell is visible, others are blank spaces. Here is my entire code for the custom View:
public class SpinNumberView extends RelativeLayout {
private int startNumber;
private int endNumber;
private int number;
private int gridsize;
private int index;
public static final double stepDuration = 0.1;
private boolean inAnimation = true;
ArrayList<Integer> numbers;
public LinearLayout container;
public SpinNumberView(Context context) {
super(context);
}
public SpinNumberView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void dispatchDraw(Canvas canvas) {
// draw the background black solid circle
float radius = (float)(this.gridsize);
Paint p = new Paint();
p.setStyle(Paint.Style.FILL);
p.setARGB(192, 0, 0, 0);
canvas.drawCircle(radius/2, radius/2, radius/2, p);
// draw 1px white border
Paint pp = new Paint();
pp.setStyle(Paint.Style.STROKE);
pp.setStrokeWidth(2.0f);
pp.setARGB(192, 255, 255, 255);
canvas.drawCircle(radius/2, radius/2, radius/2-1, pp);
// clip to the circle
Path path = new Path();
RectF r = new RectF((float)0.0, (float)0.0, radius, radius);
path.addRoundRect(r, radius/2, radius/2, Path.Direction.CW);
canvas.clipPath(path);
super.dispatchDraw(canvas);
}
#Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
super.onLayout(b, i, i1, i2, i3);
}
class AniListener implements Animator.AnimatorListener {
#Override
public void onAnimationStart(Animator animator) {}
#Override
public void onAnimationEnd(Animator animator) {
SpinNumberView.this.animateStep();
}
#Override
public void onAnimationCancel(Animator animator) {}
#Override
public void onAnimationRepeat(Animator animator) {}
}
public void animateStep() {
this.container.setTranslationY(0);
float offset;
TimeInterpolator inter;
if(this.inAnimation) {
offset = (float)this.gridsize * this.numbers.size();
inter = new LinearInterpolator();
} else {
offset = (float)this.gridsize * this.index;
inter = new DecelerateInterpolator();
}
long duration = (long)(SpinNumberView.stepDuration * this.numbers.size() * 1000);
ViewPropertyAnimator ani = this.container.animate().translationYBy(-offset).setDuration(duration);
ani.setInterpolator(inter);
if(this.inAnimation) {
ani.setListener(new AniListener());
} else {
ani.setListener(null);
}
ani.start();
}
public void stopAnimation() {
this.inAnimation = false;
}
public void startAnimation() {
this.inAnimation = true;
float offset = (float)this.gridsize * this.numbers.size();
long duration = (long)(SpinNumberView.stepDuration * this.numbers.size() * 1000);
ViewPropertyAnimator ani = this.container.animate().translationYBy(-offset).setDuration(duration);
TimeInterpolator inter = new AccelerateInterpolator();
ani.setInterpolator(inter);
ani.setListener(new AniListener());
ani.start();
}
public void setup(int number, int start, int end, int gridsize) {
this.setBackgroundColor(Color.TRANSPARENT);
this.setAlpha((float) 0.5);
this.setClipChildren(false);
this.number = number;
this.startNumber = start;
this.endNumber = end;
this.gridsize = gridsize;
this.numbers = new ArrayList<>();
for(int i=start; i<=end;i++) {
this.numbers.add(i);
}
Collections.shuffle(this.numbers);
// Find index of target number within shuffled array
this.index = this.numbers.indexOf(this.number);
this.container = new LinearLayout(this.getContext());
this.container.setOrientation(LinearLayout.VERTICAL);
this.container.setGravity(Gravity.CENTER_HORIZONTAL);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(this.gridsize, this.gridsize * (this.numbers.size()+1));
this.container.setLayoutParams(params);
this.addView(this.container);
int offsety = 0;
// setup all the number views
for(int k=0;k<this.numbers.size()+1;k++) {
String txt;
if(k==this.numbers.size()) {
txt = Integer.toString(this.numbers.get(0));
} else {
txt = Integer.toString(this.numbers.get(k));
}
TextView tv = new TextView(this.getContext());
tv.setLayoutParams(new LayoutParams(this.gridsize, this.gridsize));
tv.setText(txt);
tv.setTextSize(24.0f);
tv.setTextColor(Color.WHITE);
tv.setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER);
tv.setLines(1);
tv.setGravity(Gravity.CENTER_VERTICAL);
this.container.addView(tv);
offsety += this.gridsize;
}
this.invalidate();
}
}
Why is this happening?
BTW: I take a screenshot with getDrawingCache() of screen content, the cells are visible in the screenshot!
Yes! It happend when we get some view height or width of a view. Because didn't completely render the view when we call its height or width yet.
Solution:
Use this code to get Height and width
EditText edt = (EditText) findViewbyid(R.id.tv);
edt.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int height= edt.getHeight();
int width = edt.getHeight();
edt.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
To answer my own question:
When overriding onLayout() function, I need to layout the subviews myself like this:
#Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
super.onLayout(b, i, i1, i2, i3);
this.container.layout(0, 0, this.gridsize, this.gridsize * (this.endNumber-this.startNumber+2));
}
Glad you solved it by yourself, in iOS, we use something like Redraw method for these scenarios. Hopefully it will help you to further optimize your code.

Removing divider after a footer in a Recyclerview

Is there any way to removing divider after a footer in a Recyclerview.I am using item decoration to add divider to the adapter.I am adding footer in the adapter.divider is showing below the footer also.i want to remove it.
This is my code for Divider
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
#Override
public void getItemOffsets(Rect rect, View view, RecyclerView parent, RecyclerView.State state) {
if (Orientation == VERTICAL_LIST) {
rect.set(0, 0, 0, Divider.getIntrinsicHeight());
} else {
rect.set(0, 0, Divider.getIntrinsicWidth(), 0);
}}
You have to add a check on getItemOffsets() method:
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
// set the rect's size
}
}
You can find an example of this implementation on my RecyclerViewDivider library on Github
Or you can simply add it as a Gradle dependency and check the javadoc:
dependencies {
...
compile 'com.github.fondesa:recycler-view-divider:1.1.3'
}
And use:
RecyclerViewDivider.with(context)
.addTo(recyclerView)
.visibilityFactory(new VisibilityFactory() {
#Override
public boolean displayDividerForItem(int listSize, int position) {
return position != listSize - 1;
}
})
.build()
.attach()

on layout Change Lisnter error

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);

DividerDecoration drawn at full background

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

Categories

Resources