0

我有一个自定义视图类(PinView),它将根据某些属性定位在自定义ViewGroup(RightAngleTriangleView)内。但是,当它放置在RelativeLayout中的另一个视图中时,PinView不会显示。请参见下面的代码:在自定义ViewGroup中的自定义视图在RelativeLayout中偏移时不显示

RightAngleTriangleView

public class RightAngleTriangleView extends ViewGroup { 
private static final String TAG = "RightAngleTriangleView"; 

private int pinOrientation; 
private boolean isRightFilled, diagonalIsTopRightBottomLeft; 
private Paint trianglePaint, pinPaint; 

private Path trianglePath, pinPath; 
private PointF triStart, triMiddle, triEnd; 

private PinView pinView; 
private float pinLengthDiff, pinThickness; 

public RightAngleTriangleView(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 
    setWillNotDraw(false); // remove default (non) drawing behaviour for ViewGroup 

    /*** extract XML attributes ***/ 
    TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RightAngleTriangleView); 
    int fillColour = a.getColor(R.styleable.RightAngleTriangleView_fillColour, 
      context.getResources().getColor(android.R.color.darker_gray)); 
    int fillPosition = a.getInt(R.styleable.RightAngleTriangleView_fillPosition, 
      context.getResources().getInteger(
        R.integer.RightAngleTriangleView_fillPosition_left)); 
    int diagonal = a.getInt(R.styleable.RightAngleTriangleView_diagonal, 
      context.getResources().getInteger(
        R.integer.RightAngleTriangleView_diagonal_topLeftToBottomRight)); 

    isRightFilled = fillPosition == context.getResources().getInteger(
      R.integer.RightAngleTriangleView_fillPosition_right); 

    diagonalIsTopRightBottomLeft = diagonal == getContext().getResources().getInteger(
      R.integer.RightAngleTriangleView_diagonal_topRightToBottomLeft); 

    pinOrientation = a.getInt(R.styleable.RightAngleTriangleView_pinOrientation, 
      context.getResources().getInteger(
        R.integer.RightAngleTriangleView_pinOrientation_none)); 
    a.recycle(); 

    /*** setup drawing related variables ***/ 
    trianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    trianglePaint.setStyle(Paint.Style.FILL_AND_STROKE); 
    trianglePaint.setColor(fillColour); 

    trianglePath = new Path(); 
    trianglePath.setFillType(Path.FillType.EVEN_ODD); 

    triStart = new PointF(); 
    triMiddle = new PointF(); 
    triEnd = new PointF(); 

    pinPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    pinPaint.setStyle(Paint.Style.FILL_AND_STROKE); 
    pinPaint.setColor(context.getResources().getColor(R.color.pin_color)); 

    pinPath = new Path(); 
    pinPath.setFillType(Path.FillType.EVEN_ODD); 

    // create pinView (if present) 
    if(pinOrientation != context.getResources().getInteger(
      R.integer.RightAngleTriangleView_pinOrientation_none)){ 
     pinView = new PinView(context, UiUtils.makeAttributeSet(context, getResourceId())); 
     addView(pinView); 
    } 
} 

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

    if(hasPin()){ 
     // measure child to obtain 'wrapped' valid dimension 
     measureChild(pinView, widthMeasureSpec, heightMeasureSpec); 
    } 
} 

@Override 
protected void onLayout (boolean changed, int left, int top, int right, int bottom){ 
    Log.d(TAG, "onLayout() changed = " + changed); 

    if(hasPin()){ 
     // use 'wrapped' valid dimension as pinThickness 
     if(pinView.isHorizontal()) { 
      pinThickness = pinView.getMeasuredHeight()/2; 
      pinLengthDiff = (pinThickness * getWidth())/getHeight(); 
     } 
     else{ 
      pinThickness = pinView.getMeasuredWidth()/2; 
      pinLengthDiff = (pinThickness * getHeight())/getWidth(); 
     } 
     placePinView(left, top, right, bottom); 
    } 
} 

@Override 
protected void onDraw(Canvas canvas){ 
    // draw pin 'edge' if applicable 
    if(hasPin()){ 
     pinPath.reset(); // remove any previously drawn paths 
     if(pinView.isHorizontal()){ 
      pinPath.addRect((getWidth() - pinLengthDiff)/2, (getHeight() - pinThickness)/2, 
        (getWidth() + pinLengthDiff)/2, (getHeight() + pinThickness)/2, 
        Path.Direction.CW); 
     } 
     else{ 
      pinPath.addRect((getWidth() - pinThickness)/2, (getHeight() - pinLengthDiff)/2, 
        (getWidth() + pinThickness)/2, (getHeight() + pinLengthDiff)/2, 
        Path.Direction.CW); 
     } 
     canvas.drawPath(pinPath, pinPaint); 
    } 

    // determine triangle vertices 
    if(diagonalIsTopRightBottomLeft){ 
     // draw diagonal 
     triStart.set(getWidth(), 0); 
     triMiddle.set(0, getHeight()); 

     // determine triEnd based on fill position 
     if(isRightFilled){ 
      triEnd.set(getWidth(), getHeight()); 
     } 
     else{ 
      triEnd.set(0, 0); 
     } 
    } 
    else{ 
     // draw diagonal 
     triStart.set(0, 0); 
     triMiddle.set(getWidth(), getHeight()); 

     // determine triEnd based on fill position 
     if(isRightFilled){ 
      triEnd.set(getWidth(), 0); 
     } 
     else{ 
      triEnd.set(0, getHeight()); 
     } 
    } 

    // draw triangle 
    trianglePath.reset(); // remove any previously drawn paths 
    trianglePath.moveTo(triStart.x, triStart.y); 
    trianglePath.lineTo(triMiddle.x, triMiddle.y); 
    trianglePath.lineTo(triEnd.x, triEnd.y); 
    trianglePath.close(); // automatically draw third side 

    canvas.drawPath(trianglePath, trianglePaint); 
} 

public boolean hasPin(){ 
    return pinView != null; 
} 

private void placePinView(int left, int top, int right, int bottom){ 
    int l, t, r, b, pinPosition; 

    int trbl = diagonalIsTopRightBottomLeft ? 1<<2 : 0; 
    int rightFilled = isRightFilled ? 1<<1 : 0; 
    int horizontal = pinView.isHorizontal() ? 1 : 0; 
    int result = trbl + rightFilled + horizontal; 
    // determine pin size and position 
    switch (result){ 
     case 0: // diagonal = top-left to bottom-right, left-filled, pin vertical 
      t = top; 
      b = t + (int) (getHeight() - pinLengthDiff)/2; 
      l = left + (int) (getWidth() - pinThickness)/2; 
      r = l + pinView.getMeasuredWidth(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_top); 
      break; 
     case 1: // diagonal = top-left to bottom-right, left-filled, pin horizontal 
      l = left + (int) (getWidth() + pinLengthDiff)/2; 
      r = right; 
      b = top + (int) (getHeight() + pinThickness)/ 2; 
      t = b - pinView.getMeasuredHeight(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_right); 
      break; 
     case 2: // diagonal = top-left to bottom-right, right-filled, pin vertical 
      t = top + (int) (getHeight() + pinLengthDiff)/2; 
      b = bottom; 
      r = left + (int) (getWidth() + pinThickness)/2; 
      l = r - pinView.getMeasuredWidth(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_bottom); 
      break; 
     case 3: // diagonal = top-left to bottom-right, right-filled, pin horizontal 
      l = left; 
      t = top + (int) (getHeight() - pinThickness)/ 2; 
      r = l + (int) (getWidth() - pinLengthDiff)/2; 
      b = t + pinView.getMeasuredHeight(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_left); 
      break; 
     case 4: // diagonal = top-right to bottom-left, left-filled, pin vertical 
      t = top + (int) (getHeight() + pinLengthDiff)/2; 
      b = bottom; 
      l = left + (int) (getWidth() - pinThickness)/2; 
      r = l + pinView.getMeasuredWidth(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_bottom); 
      break; 
     case 5: // diagonal = top-right to bottom-left, left-filled, pin horizontal 
      l = left + (int) (getWidth() + pinLengthDiff)/2; 
      t = top + (int) (getHeight() - pinThickness)/ 2; 
      r = right; 
      b = t + pinView.getMeasuredHeight(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_right); 
      break; 
     case 6: // diagonal = top-right to bottom-left, right-filled, pin vertical 
      t = top; 
      b = t + (int) (getHeight() - pinLengthDiff)/2; 
      r = left + (int) (getWidth() + pinThickness)/2; 
      l = r - pinView.getMeasuredWidth(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_top); 
      break; 
     case 7: // diagonal = top-right to bottom-left, right-filled, pin horizontal 
      l = left; 
      r = l + (int) (getWidth() - pinLengthDiff)/2; 
      b = top + (int) (getHeight() + pinThickness)/2; 
      t = b - pinView.getMeasuredHeight(); 
      pinPosition = getContext().getResources().getInteger(
        R.integer.PinView_position_left); 
      break; 
     default: 
      l = left; 
      t = top; 
      r = right; 
      b = bottom; 
      pinPosition = -1; 
      break; 
    } 

    // remeasure/resize pinView accounting for correct unspecified dimension 
    measureChild(pinView, MeasureSpec.makeMeasureSpec(r - l, MeasureSpec.EXACTLY), 
      MeasureSpec.makeMeasureSpec(b - t, MeasureSpec.EXACTLY)); 

    pinView.setPosition(pinPosition); // ensure that pinName is in correct position 
    pinView.layout(l, t, r, b); // position pinView 
} 

private int getResourceId(){ 
    int trbl = diagonalIsTopRightBottomLeft ? 1<<2 : 0; 
    int rightFilled = isRightFilled ? 1<<1 : 0; 
    int horizontal = pinOrientation == getContext().getResources().getInteger(
      R.integer.RightAngleTriangleView_pinOrientation_horizontal) ? 1 : 0; 

    int result = trbl + rightFilled + horizontal; 
    Log.d(TAG, "getResourceId(): result = " + result); 
    switch (result){ 
     case 0: // diagonal = top-left to bottom-right, left-filled, pin vertical 
      return R.xml.pinview_vertical_namebelow; 
     case 1: // diagonal = top-left to bottom-right, left-filled, pin horizontal 
      return R.xml.pinview_horizontal; 
     case 2: // diagonal = top-left to bottom-right, right-filled, pin vertical 
      return R.xml.pinview_vertical; 
     case 3: // diagonal = top-left to bottom-right, right-filled, pin horizontal 
      return R.xml.pinview_horizontal_namebelow; 
     case 4: // diagonal = top-right to bottom-left, left-filled, pin vertical 
      return R.xml.pinview_vertical_namebelow; 
     case 5: // diagonal = top-right to bottom-left, left-filled, pin horizontal 
      return R.xml.pinview_horizontal_namebelow; 
     case 6: // diagonal = top-right to bottom-left, right-filled, pin vertical 
      return R.xml.pinview_vertical; 
     case 7: // diagonal = top-right to bottom-left, right-filled, pin horizontal 
      return R.xml.pinview_horizontal; 
     default: 
      return -1; 
    } 
} 
} 

PinView

public class PinView extends RelativeLayout { 
private TextView pinNameView, signalTextView; 

private boolean isHorizontal; 

public PinView(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 

    /*** extract XML attributes ***/ 
    TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PinView); 
    boolean nameBelow = a.getBoolean(R.styleable.PinView_nameBelow, false); 
    int orientation = a.getInt(R.styleable.PinView_orientation, 0); 
    int position = a.getInt(R.styleable.PinView_position, -1); 
    isHorizontal = orientation == 0; 
    a.recycle(); 

    /*** create children ***/ 
    LinearLayout backgroundLayout = new LinearLayout(context); 
    backgroundLayout.setId(R.id.pinview_backgroundlayout); 
    backgroundLayout.setBackgroundColor(context.getResources().getColor(R.color.pin_color)); 

    if(isHorizontal){ 
     pinNameView = new TextView(context); 
     signalTextView = new TextView(context); 
    } 
    else{ 
     pinNameView = new VerticalTextView(context); 
     signalTextView = new VerticalTextView(context); 
    } 

    pinNameView.setId(R.id.pinview_pinname); 
    pinNameView.setTextColor(context.getResources().getColor(android.R.color.black)); 

    signalTextView.setId(R.id.pinview_signaltext); 
    signalTextView.setTextColor(context.getResources().getColor(android.R.color.black)); 
    signalTextView.setBackgroundColor(context.getResources().getColor(R.color.pin_sig_color)); 

    /*** determine children layouts and positions ***/ 
    LayoutParams lpSigText = new LayoutParams(
      LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 
    backgroundLayout.addView(signalTextView, lpSigText); // add signalView to backgroundLayout, NOT this view 

    LayoutParams lpBackgroundLayout; 
    if(isHorizontal){ 
     lpBackgroundLayout = new LayoutParams(
       LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 
    } 
    else{ 
     lpBackgroundLayout = new LayoutParams(
       LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); 
    } 

    LayoutParams lpPinName = new LayoutParams(
      LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 

    // place pin name accordingly 
    if(nameBelow){ 
     addView(backgroundLayout, lpBackgroundLayout); 

     if(isHorizontal){ 
      lpPinName.addRule(RelativeLayout.BELOW, backgroundLayout.getId()); 
     } 
     else{ 
      lpPinName.addRule(RelativeLayout.RIGHT_OF, backgroundLayout.getId()); 
     } 
     addView(pinNameView, lpPinName); 
     setPosition(position); 
    } 
    else{ 
     addView(pinNameView, lpPinName); 
     setPosition(position); 

     if(isHorizontal){ 
      lpBackgroundLayout.addRule(RelativeLayout.BELOW, pinNameView.getId()); 
     } 
     else{ 
      lpBackgroundLayout.addRule(RelativeLayout.RIGHT_OF, pinNameView.getId()); 
     } 
     addView(backgroundLayout, lpBackgroundLayout); 
    } 
} 

public void setPosition(int position){ 
    // align pin name according to pin position on device 
    LayoutParams params = (RelativeLayout.LayoutParams) pinNameView.getLayoutParams(); 
    switch(position){ // pin's position relative to parent device 
     case 2: // top 
      params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); 
      break; 
     case 3: // bottom 
      params.addRule(RelativeLayout.ALIGN_PARENT_TOP); 
      break; 
     case 0: // left 
      params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); 
      break; 
     case 1: // right 
      params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); 
      break; 
     default: 
      break; 
    } 
} 

public void setData(DevicePin pin){ 
    pinNameView.setText(pin.name); 
    signalTextView.setText(pin.data); 

    // always create new animation, otherwise it will need to be reset 
    Animation anim = null; 
    switch (pin.direction){ 
     case LEFT: 
      anim = AnimationUtils.loadAnimation(getContext(), R.anim.pin_transition_left); 
      break; 
     case RIGHT: 
      anim = AnimationUtils.loadAnimation(getContext(), R.anim.pin_transition_right); 
      break; 
     case UP: 
      anim = AnimationUtils.loadAnimation(getContext(), R.anim.pin_transition_up); 
      break; 
     case DOWN: 
      anim = AnimationUtils.loadAnimation(getContext(), R.anim.pin_transition_down); 
      break; 
    } 

    if(pin.startBehaviour == DevicePin.AnimStartBehaviour.DELAY){ 
     if(pin.animationDelay == -1){ 
      anim.setStartOffset(anim.getDuration()); 
     } 
     else{ 
      anim.setStartOffset(pin.animationDelay); 
     } 
    } 

    if(pin.action == DevicePin.PinAction.STATIONARY){ 
     anim.setDuration(0); 
    } 

    if(pin.animListener != null){ 
     anim.setAnimationListener(pin.animListener); 
    } 

    if(anim != null){ 
     signalTextView.setAnimation(anim); 
    } 
} 

public boolean isHorizontal(){ 
    return isHorizontal; 
} 
} 

活动XML

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:custom="http://schemas.android.com/apk/res-auto" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent"> 

    <view 
    class="org.ricts.abstractmachine.ui.device.RightAngleTriangleView" 
    android:id="@+id/edge" 
    android:layout_width="122px" 
    android:layout_height="150px" 
    custom:diagonal = "topLeftToBottomRight" 
    custom:fillPosition = "right" 
    custom:fillColour="@color/mux_fill_color" 
    custom:pinOrientation = "vertical" 
    /> 
<view 
    class="org.ricts.abstractmachine.ui.device.RightAngleTriangleView" 
    android:id="@+id/edge2" 
    android:layout_width="122px" 
    android:layout_height="150px" 
    android:layout_toRightOf="@+id/edge" 
    custom:diagonal = "topLeftToBottomRight" 
    custom:fillPosition = "right" 
    custom:fillColour="@color/mux_fill_color" 
    custom:pinOrientation = "vertical" 
    /> 
<view 
    class="org.ricts.abstractmachine.ui.device.RightAngleTriangleView" 
    android:id="@+id/edge3" 
    android:layout_width="122px" 
    android:layout_height="150px" 
    android:layout_below="@+id/edge" 
    custom:diagonal = "topLeftToBottomRight" 
    custom:fillPosition = "right" 
    custom:fillColour="@color/mux_fill_color" 
    custom:pinOrientation = "vertical" 
    /> 
<view 
    class="org.ricts.abstractmachine.ui.device.PinView" 
    android:id="@+id/pin" 
    android:layout_width="58px" 
    android:layout_height="64px" 
    android:layout_below="@+id/edge" 
    android:layout_toRightOf="@+id/edge3" 
    custom:orientation="vertical" 
    custom:position="top" 
    /> 

</RelativeLayout> 

上面的代码是一个活动突出问题的XML。我也采取了这个问题的截图(如下)。顶部角落中的三角形很好,并显示内部PinView。但是,其他两个(第一个的右下方)并不显示PinView,即使代码完全相同,并且我已经通过Log确认PinView的位置看起来没问题。

Problem Screenshot

我在做什么错?

回答

0

事实证明,当调用View.layout(l,t,r,b)而不是相对的时候,我使用了绝对值。我现在使用相对值,它的工作原理!