【问题标题】:Why are the subviews out of the bounds of my custom View not drawn?为什么没有绘制超出我的自定义视图范围的子视图?
【发布时间】:2017-11-04 11:10:02
【问题描述】:

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

但是有一个问题:只有最初在边界(第一个)中的单元格被渲染,边界之外的单元格没有被绘制,所以当我动画线性布局滚动时,线性布局在旋转,但只有第一个单元格可见,其他单元格是空格。这是我自定义视图的全部代码:

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()的屏幕内容截图,截图中的单元格是可见的!

【问题讨论】:

  • 你试过使用LayoutParams lparams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  • @AmitVaghela 是的,试过了。
  • 您是否尝试过使用LinearLayout.LayoutParams?使用特定于容器的LayoutParams 子类。
  • 您是否在自定义 ViewGroup 中覆盖了 onLayout / onMeasure
  • @pskink 我重写了 onLayout 但忘记调用 super.onLayout()... 但是当我这样做时,不会绘制超出范围的子视图。为什么?

标签: android android-layout textview


【解决方案1】:

是的!当我们获得某个视图的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);
            }
        });

【讨论】:

    【解决方案2】:

    回答我自己的问题:

    当覆盖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));
    }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-07
        • 2018-07-30
        • 1970-01-01
        • 1970-01-01
        • 2011-09-08
        • 2016-04-12
        • 2021-09-26
        相关资源
        最近更新 更多