为什么我的自定义视图范围之外的子视图未被绘制?

时间:2017-11-04 11:10:03

标签: android android-layout textview

我实现了自定义SpinNumberView:它是方形(例如40x40),它有一个垂直LinearLayout作为子视图,在这个线性布局中是一堆40x40垂直堆叠的单元格。我想通过更改LinearLayout的偏移量来动画单元格以垂直滚动。

但是有一个问题:只有最初在边界(第一个)的单元格被渲染,边界之外的单元格没有被绘制,因此当我为LinearLayout设置动画以进行滚动时,线性布局正在旋转,但只有第一个单元格可见,其他单元格为空格。以下是我自定义视图的完整代码:

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

为什么会这样?

顺便说一句:我用屏幕内容getDrawingCache()截取屏幕截图,屏幕截图中显示了单元格!

3 个答案:

答案 0 :(得分:0)

是的!当我们获得视图的heightwidth视图时,就会发生这种情况。因为我们在调用heightwidth时尚未完全呈现视图。

<强>解决方案:

使用此代码获取高度和宽度

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

答案 1 :(得分:0)

回答我自己的问题:

当覆盖onLayout()函数时,我需要自己布局子视图:

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

答案 2 :(得分:0)

很高兴你自己解决了,在iOS中,我们在这些场景中使用类似Redraw方法的东西。希望它能帮助您进一步优化代码。