Android: como desenhar uma borda em um LinearLayout

197

Eu tenho três arquivos. O XML, a função de desenho e a Atividade principal. Eu tenho alguns LinearLayoutno meu arquivo XML.

<LinearLayout android:orientation="horizontal"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:layout_weight="1">
    <LinearLayout android:layout_width="fill_parent"
                  android:layout_height="fill_parent"
                  android:layout_weight="1"
                  android:background="#ef3"
                  android:id="@+id/img01"/>
    <LinearLayout android:layout_width="fill_parent"
                  android:layout_height="fill_parent"
                  android:layout_weight="1"
                  android:background="#E8A2B4"
                  android:id="@+id/img02"/>
</LinearLayout>

Esta é a função de desenho:

public class getBorder extends TextView {
    public getBorder(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();

        paint.setColor(android.graphics.Color.RED);

        canvas.drawLine(0, 0, this.getWidth() - 1, 0, paint);
        canvas.drawLine(0, 0, 0, this.getHeight() - 1, paint);
        canvas.drawLine(this.getWidth() - 1, 0, this.getWidth() - 1,
            this.getHeight() - 1, paint);
        canvas.drawLine(0, this.getHeight() - 1, this.getWidth() - 1,
            this.getHeight() - 1, paint);
    }
}

E esta é a principal atividade:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final getBorder getBorder = new getBorder(this);
    final LinearLayout img01 = (LinearLayout) findViewById(R.id.img01);
    img01.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            getBorder.setWidth(100);
            getBorder.setHeight(100);
            img01.addView(getBorder);
        }
    });       
}

O programa pode desenhar borda, mas o tamanho não se ajusta ao LinearLayout. E quando clico no botão LinearLayoutnovamente, o programa trava.

Outra coisa, eu quero desenhar dois círculos no centro do LinearLayout, mas como eu poderia descobrir as coordenadas do centro?

SPG
fonte

Respostas:

460

Você realmente precisa fazer isso programaticamente?

Apenas considerando o título: você pode usar um ShapeDrawable como android: background…

Por exemplo, vamos definir res/drawable/my_custom_background.xmlcomo:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
  <corners
      android:radius="2dp"
      android:topRightRadius="0dp"
      android:bottomRightRadius="0dp"
      android:bottomLeftRadius="0dp" />
  <stroke
      android:width="1dp"
      android:color="@android:color/white" />
</shape>

e defina android: background = "@ drawable / my_custom_background".

Eu não testei, mas deve funcionar.

Atualizar:

Eu acho que é melhor aproveitar o poder do recurso extraível da forma xml, se isso for adequado às suas necessidades. Com um projeto "do zero" (para android-8), defina res / layout / main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/border"
    android:padding="10dip" >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World, SOnich"
        />
    [... more TextView ...]
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World, SOnich"
        />
</LinearLayout>

e um res/drawable/border.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
   <stroke
        android:width="5dip"
        android:color="@android:color/white" />
</shape>

Relatado para trabalhar em um dispositivo de gengibre. Observe que você precisará relacionar android:paddingo LinearLayout ao valor da android:widthforma / traçado. Por favor, não use@android:color/white na sua aplicação final, mas uma cor definida pelo projeto.

Você pode aplicar android:background="@drawable/border" android:padding="10dip"a cada LinearLayout a partir da amostra fornecida.

Quanto às suas outras postagens relacionadas à exibição de alguns círculos como plano de fundo do LinearLayout, estou brincando com os recursos desenháveis ​​Inset / Scale / Layer ( consulte Recursos desenháveis para obter mais informações) para obter algo funcionando para exibir círculos perfeitos no fundo de um LinearLayout, mas falhou no momento…

Seu problema reside claramente no uso de getBorder.set{Width,Height}(100); . Por que você faz isso em um método onClick?

Preciso de mais informações para não perder o ponto: por que você faz isso programaticamente? Você precisa de um comportamento dinâmico? Seus drawables de entrada são png ou ShapeDrawable é aceitável? etc.

Para continuar (talvez amanhã e assim que você fornecer mais detalhes sobre o que deseja alcançar)…

Renaud
fonte
@Renaud, existe uma maneira de alterar a cor da borda com problemas?
user1940676
12
Não sei por que, mas quando o usei em um LinearLayoutshape<solid android:color="@android:color/transparent" />
formulário
2
Para mim, era o mesmo que eu tinha que adicionar um, <solid android:color="@color/lighter_gray" />caso contrário eu tinha um fundo preto
Panciz
1
Este método funciona perfeitamente; no entanto, ele cria duas camadas "Overdraw", o que é realmente ruim para o desempenho, mesmo se você definir o plano de fundo como "null" ou "transparent". Alguém pode sugerir uma maneira de corrigir esse problema?
Sam Ramezanli
16

Estenda o LinearLayout / RelativeLayout e use-o diretamente no XML

package com.pkg_name ;
...imports...
public class LinearLayoutOutlined extends LinearLayout {
    Paint paint;    

    public LinearLayoutOutlined(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        setWillNotDraw(false) ;
        paint = new Paint();
    }
    public LinearLayoutOutlined(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        setWillNotDraw(false) ;
        paint = new Paint();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        /*
        Paint fillPaint = paint;
        fillPaint.setARGB(255, 0, 255, 0);
        fillPaint.setStyle(Paint.Style.FILL);
        canvas.drawPaint(fillPaint) ;
        */

        Paint strokePaint = paint;
        strokePaint.setARGB(255, 255, 0, 0);
        strokePaint.setStyle(Paint.Style.STROKE);
        strokePaint.setStrokeWidth(2);  
        Rect r = canvas.getClipBounds() ;
        Rect outline = new Rect( 1,1,r.right-1, r.bottom-1) ;
        canvas.drawRect(outline, strokePaint) ;
    }

}

<?xml version="1.0" encoding="utf-8"?>

<com.pkg_name.LinearLayoutOutlined
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
    android:layout_width=...
    android:layout_height=...
   >
   ... your widgets here ...

</com.pkg_name.LinearLayoutOutlined>
Gabriel Riba
fonte
34
Por favor, não aloque memória no onDraw()método, crie seus objetos em um init()método, chamado pelo construtor e reutilize-os no onDraw()método. Alocar onDraw()(chamado 60 vezes por segundo) leva a um desempenho ruim, consumo de bateria, etc.
Louis CAD