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!
3 Answers
Answers 1
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); } }); Answers 2
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)); } Answers 3
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.
0 comments:
Post a Comment