代码之家  ›  专栏  ›  技术社区  ›  NeoWang

为什么没有绘制超出自定义视图边界的子视图?

  •  0
  • NeoWang  · 技术社区  · 8 年前

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

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

    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 回复  |  直到 8 年前
        1
  •  0
  •   Abdur Rahman    8 年前

    对当我们看到一些风景的时候 height 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);
                }
            });
    
        2
  •  0
  •   NeoWang    8 年前

    回答我自己的问题:

    覆盖时 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
  •  0
  •   Naveed Abbas    8 年前

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