【问题标题】:custom view not getting drawn in custom viewgroup自定义视图未在自定义视图组中绘制
【发布时间】:2018-06-26 06:34:23
【问题描述】:

我有一个自定义视图类,它绘制一个矩形,我有一个自定义视图组,我试图在其中添加这些自定义视图,但不知何故,视图没有被正确绘制(有时只有一个视图被绘制)。 无法定位问题!

MainActivity.java

public class MainActivity extends Activity {

private CustomTaskView customTaskView;
private FrameLayout frameLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    customTaskView = new CustomTaskView(MainActivity.this);


    frameLayout = new FrameLayout(MainActivity.this);
    frameLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));


    Paint blackPaint = new Paint();
    blackPaint.setColor(Color.BLACK);
    blackPaint.setStyle(Paint.Style.STROKE);

    CustomView customView1 = new CustomView(MainActivity.this, 100, 50, 100, 300, blackPaint);

    customView1.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT));
    frameLayout.addView(customView1);


    Paint redPaint = new Paint();
    redPaint.setColor(Color.RED);
    redPaint.setStyle(Paint.Style.STROKE);

    CustomView customView2 = new CustomView(MainActivity.this, 200, 50, 300, 400, redPaint);
    customView2.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT));
    frameLayout.addView(customView2);



    setContentView(frameLayout);
  }

}

CustomView.java

public class CustomView extends View {

private Paint paint;

private float l, t, r, b;

public CustomView(Context context, float l, float t, float r, float b, Paint paint) {
    super(context);
    this.b = b;
    this.l = l;
    this.r = r;
    this.t = t;
    this.paint = paint;
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawRect(l, t, r, b, paint);
}
}

CustomTaskView.java(自定义视图组文件)

public class CustomTaskView extends ViewGroup implements LongPressGestureListener.HandleClicks {

int width, height;
private GestureDetectorCompat mGestureDetector;
private LongPressGestureListener longPressGestureListener;
private Map<Integer, List<Point>> map;


public CustomTaskView(Context context) {
    super(context);
    longPressGestureListener = new LongPressGestureListener(context, CustomTaskView.this);
    mGestureDetector = new GestureDetectorCompat(context, longPressGestureListener);
    map = new HashMap<>();
}

public CustomTaskView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

}


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    width = getMeasuredWidth();
    height = getMeasuredHeight();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    mGestureDetector.onTouchEvent(event);
    // Handle any other event here, if not long press.
    return true;
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
}

@Override
public void handleLongClick(MotionEvent event) {
    Log.e("ganesh", "handling long clicks!");
}

@Override
public void handleSingleClick(MotionEvent event) {

    float xCoordinate = event.getX();
    float yCoordinate = event.getY();

    Point tempPoint = new Point(xCoordinate, yCoordinate);

    int count = 0;

    for (Map.Entry<Integer, List<Point>> entry : map.entrySet()) {
        if (isPointInside(entry.getValue(), tempPoint))
            count++;
    }

    Log.e("ganesh", "handling single clicks!" + xCoordinate + " " + yCoordinate + " count: " + count);


}


public boolean isPointInside(List<Point> pointList, Point targetPoint) {
    return targetPoint.getxCoordinate() >= pointList.get(0).getxCoordinate() && targetPoint.getxCoordinate() <= pointList.get(1).getxCoordinate() && targetPoint.getyCoordinate() >= pointList.get(0).getyCoordinate() && targetPoint.getyCoordinate() <= pointList.get(2).getyCoordinate();
}

}

【问题讨论】:

  • CustomView#onDraw内记录ltrb的值,你在logcat上看到了什么?
  • 我目前正在努力提供 onLayout 和 onMeasure 的正确实现。据我了解,我必须在 onMeasure 中获取子视图的测量值,并且为了定位,将使用 onLayout

标签: android android-layout android-custom-view viewgroup android-viewgroup


【解决方案1】:

每当您对自定义视图进行任何更改时,您都需要重新构建项目以供 XML 预览解析器进行绘制。尝试Build->Rebuild Project 一次。

【讨论】:

  • 不工作!当我们运行应用程序时,项目不会被重建吗?为什么我们需要再次重建它?
  • 您的onLayout 方法没有调用super 检查一次。
  • super.onLayout(改变, l, t, r, b);添加此行后,我收到此错误:无法直接访问抽象方法
【解决方案2】:

如果您直接扩展 ViewGroup 类,则必须在自定义视图中提供 onLayoutonMeasure 方法的正确实现。你可以在这里找到更多关于这两种方法的信息Google Developers Guide Custom ViewGroup

这里是对您当前的 CustomTaskView.java 的编辑,以使您的代码正常工作。

 @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int count = getChildCount();
    for (int i = 0; i < count; i++) {
        CustomView customView = (CustomView) this.getChildAt(i);
        customView.layout(l, t, r, b);
    }
}

在自定义视图中,onLayout 方法负责在布局中定位所有子项。您需要在所有子节点上调用 layout 方法,并为它们提供计算出的 left、top、right、bottom 属性。

【讨论】: