Defina a posição absoluta de uma vista

180

É possível definir a posição absoluta de uma visualização no Android? (Eu sei que existe umAbsoluteLayout , mas está obsoleto ...)

Por exemplo, se eu tiver uma tela de 240x320px, como posso adicionar uma ImageViewque seja 20x20px, de modo que seu centro esteja na posição (100,100)?

Sephy
fonte
3
também ver view.setTranslationX()ouview.offsetLeftAndRight()
de Drew LeSueur
Acabei de lançar uma biblioteca que pode ter interesse aqui. github.com/ManuelPeinado/ImageLayout
Manuel
1
Isso é tão difícil porque 99,9% das vezes, o posicionamento absoluto é uma má ideia no Android. Se você estiver escrevendo um aplicativo que será executado em um dispositivo físico, isso poderá funcionar, mas geralmente não é uma suposição segura a ser feita. Por exemplo, não faça o upload para o Google Play. Funciona bem no iOS porque existem apenas alguns dispositivos de hardware e você pode criar um storyboard personalizado para cada um.
Edthethird 6/01/15
2
@edthethird, No meu aplicativo de plataforma cruzada, obtenho o tamanho da tela e baseio tudo nisso. Acabei de mudar para o AbsoluteLayout "obsoleto" e funciona bem.
William Jockusch
justo o suficiente, mas é isso que um layout relativo ou um LinearLayout fará automaticamente por você.
Edthethird 15/01

Respostas:

271

Você pode usar RelativeLayout. Digamos que você queira um ImageView 30x40 na posição (50,60) dentro do seu layout. Em algum lugar da sua atividade:

// Some existing RelativeLayout from your layout xml
RelativeLayout rl = (RelativeLayout) findViewById(R.id.my_relative_layout);

ImageView iv = new ImageView(this);

RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(30, 40);
params.leftMargin = 50;
params.topMargin = 60;
rl.addView(iv, params);

Mais exemplos:

Coloca dois ImageViews 30x40 (um amarelo, um vermelho) em (50,60) e (80,90), respectivamente:

RelativeLayout rl = (RelativeLayout) findViewById(R.id.my_relative_layout);
ImageView iv;
RelativeLayout.LayoutParams params;

iv = new ImageView(this);
iv.setBackgroundColor(Color.YELLOW);
params = new RelativeLayout.LayoutParams(30, 40);
params.leftMargin = 50;
params.topMargin = 60;
rl.addView(iv, params);

iv = new ImageView(this);
iv.setBackgroundColor(Color.RED);
params = new RelativeLayout.LayoutParams(30, 40);
params.leftMargin = 80;
params.topMargin = 90;
rl.addView(iv, params);

Coloca um ImageView amarelo 30x40 em (50,60) e outro ImageView vermelho 30x40 <80,90> em relação ao ImageView amarelo:

RelativeLayout rl = (RelativeLayout) findViewById(R.id.my_relative_layout);
ImageView iv;
RelativeLayout.LayoutParams params;

int yellow_iv_id = 123; // Some arbitrary ID value.

iv = new ImageView(this);
iv.setId(yellow_iv_id);
iv.setBackgroundColor(Color.YELLOW);
params = new RelativeLayout.LayoutParams(30, 40);
params.leftMargin = 50;
params.topMargin = 60;
rl.addView(iv, params);

iv = new ImageView(this);
iv.setBackgroundColor(Color.RED);
params = new RelativeLayout.LayoutParams(30, 40);
params.leftMargin = 80;
params.topMargin = 90;

// This line defines how params.leftMargin and params.topMargin are interpreted.
// In this case, "<80,90>" means <80,90> to the right of the yellow ImageView.
params.addRule(RelativeLayout.RIGHT_OF, yellow_iv_id);

rl.addView(iv, params);
Andy Zhang
fonte
1
Vou tentar isso hoje à noite, é uma boa idéia, não sei por que não pensei nisso. Como tenho vários ImageViewpara colocar, não seria melhor usar um FrameLayout?
Sephy
De fato, isso parece funcionar, no entanto, só funciona quando uma imagem é adicionada dessa maneira. se eu tentar adicionar uma segunda um, o primeiro simplesmente desaparece ...
Sephy
1
As duas imagens estão umas sobre as outras. Vou adicionar algum código à minha solução acima para explicar.
Andy Zhang
2
Sim, de fato, eu descobri isso ontem também escavando sua solução por conta própria. Também tento coisas com o FrameLayout. Meu problema real é que tenho 5 imagens com uma posição aleatória (x, y), por isso não posso usar RelativeLayout.RIGHT_OF ou algo assim. O estranho é que eu posso colocar 3 imagens para serem colocadas corretamente, mas 2 delas não estão funcionando ... eu não entendo ... vou atualizar minha postagem com algumas capturas de tela hoje à noite e algum código.
Sephy
9
Por que usar esse método seria melhor do que usar AbsoluteLayout? Só porque o AbsoluteLayout está obsoleto?
Nathan
66

Em geral, você pode adicionar uma View em uma posição específica usando um FrameLayout como contêiner, especificando os atributos leftMargin e topMargin .

O exemplo a seguir colocará um ImageView de 20x20px na posição (100.200) usando um FrameLayout como contêiner em tela cheia:

XML

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:background="#33AAFF"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</FrameLayout>

Atividade / Fragmento / Visualização personalizada

//...
FrameLayout root = (FrameLayout)findViewById(R.id.root);
ImageView img = new ImageView(this);
img.setBackgroundColor(Color.RED);
//..load something inside the ImageView, we just set the background color

FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(20, 20);
params.leftMargin = 100;
params.topMargin  = 200;
root.addView(img, params);
//...

Isso funcionará porque as margens podem ser usadas como coordenadas absolutas (X, Y) sem um RelativeLayout:

insira a descrição da imagem aqui

bonnyz
fonte
2
Brilhante! Vale os 500! 1
Lisa Anne
16

Apenas para adicionar à resposta de Andy Zhang acima, se você quiser, pode dar o parâmetro para rl.addView e fazer alterações posteriormente, para:

params = new RelativeLayout.LayoutParams(30, 40);
params.leftMargin = 50;
params.topMargin = 60;
rl.addView(iv, params);

Igualmente bem poderia ser escrito como:

params = new RelativeLayout.LayoutParams(30, 40);
rl.addView(iv, params);
params.leftMargin = 50;
params.topMargin = 60;

Portanto, se você reter a variável params, poderá alterar o layout de iv a qualquer momento após adicioná-la a rl.

Excrubulento
fonte
amei esta amostra. Você pode me sugerir como posso definir o eixo x, y quando estou tendo a imagem no layout xml. (Estou tentando redimensionar a imagem e preciso defini-la em alguma posição)
G_S
Receio não entender bem o que você está perguntando. Você está tentando acessar o objeto LayoutParams associado a um objeto que foi posicionado usando o layout XML? Não tenho certeza de como isso é feito. Talvez valha a pena configurar uma nova pergunta para que isso seja respondido, se você não encontrar a resposta em outro lugar.
Excrubulent
Por favor, dê uma olhada nesta pergunta stackoverflow.com/questions/12028404/…
G_S
5

Uma maneira mais limpa e dinâmica sem codificar nenhum valor de pixel no código.

Eu queria posicionar uma caixa de diálogo (que infla rapidamente) exatamente abaixo de um botão clicado.

e resolveu assim:

    // get the yoffset of the position where your View has to be placed 
    final int yoffset = < calculate the position of the view >

    // position using top margin
    if(myView.getLayoutParams() instanceof MarginLayoutParams) {
        ((MarginLayoutParams) myView.getLayoutParams()).topMargin = yOffset;
    }

No entanto, você deve garantir que o layout pai de myViewseja uma instância de RelativeLayout.

código mais completo:

    // identify the button
    final Button clickedButton = <... code to find the button here ...>

    // inflate the dialog - the following style preserves xml layout params
    final View floatingDialog = 
        this.getLayoutInflater().inflate(R.layout.floating_dialog,
            this.floatingDialogContainer, false);

    this.floatingDialogContainer.addView(floatingDialog);

    // get the buttons position
    final int[] buttonPos = new int[2];
    clickedButton.getLocationOnScreen(buttonPos);        
    final int yOffset =  buttonPos[1] + clickedButton.getHeight();

    // position using top margin
    if(floatingDialog.getLayoutParams() instanceof MarginLayoutParams) {
        ((MarginLayoutParams) floatingDialog.getLayoutParams()).topMargin = yOffset;
    }

Dessa forma, você ainda pode esperar que a visualização de destino se ajuste a qualquer parâmetro de layout definido usando arquivos XML de layout, em vez de codificar esses pixels / dps no código Java.

Hari Krishna Ganji
fonte
1

Verificar captura de tela

Coloque qualquer visualização no seu ponto X e Y desejado

arquivo de layout

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.test.MainActivity" >

    <AbsoluteLayout
        android:id="@+id/absolute"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <RelativeLayout
            android:id="@+id/rlParent"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <ImageView
                android:id="@+id/img"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/btn_blue_matte" />
        </RelativeLayout>
    </AbsoluteLayout>

</RelativeLayout>

Classe Java

public class MainActivity extends Activity {

    private RelativeLayout rlParent;
    private int width = 100, height = 150, x = 20, y= 50; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AbsoluteLayout.LayoutParams param = new AbsoluteLayout.LayoutParams(width, height, x, y);
        rlParent = (RelativeLayout)findViewById(R.id.rlParent);
        rlParent.setLayoutParams(param);
    }
}

Feito

Hiren Patel
fonte
0

Caso isso ajude alguém, você também pode experimentar este animador ViewPropertyAnimator como abaixo

myView.animate().x(50f).y(100f);

myView.animate().translateX(pixelInScreen) 

Nota: Este pixel não é relativo à visualização. Este pixel é a posição do pixel na tela.

créditos para resposta bpr10

praveenb
fonte
-1

Experimente o código abaixo para definir a visualização em um local específico: -

            TextView textView = new TextView(getActivity());
            textView.setId(R.id.overflowCount);
            textView.setText(count + "");
            textView.setGravity(Gravity.CENTER);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
            textView.setTextColor(getActivity().getResources().getColor(R.color.white));
            textView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    // to handle click 
                }
            });
            // set background 
            textView.setBackgroundResource(R.drawable.overflow_menu_badge_bg);

            // set apear

            textView.animate()
                    .scaleXBy(.15f)
                    .scaleYBy(.15f)
                    .setDuration(700)
                    .alpha(1)
                    .setInterpolator(new BounceInterpolator()).start();
            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                    FrameLayout.LayoutParams.WRAP_CONTENT,
                    FrameLayout.LayoutParams.WRAP_CONTENT);
            layoutParams.topMargin = 100; // margin in pixels, not dps
            layoutParams.leftMargin = 100; // margin in pixels, not dps
            textView.setLayoutParams(layoutParams);

            // add into my parent view
            mainFrameLaout.addView(textView);
duggu
fonte
-1

Meu código para o Xamarin , estou usando o FrameLayout para essa finalidade e a seguir está o meu código:

               List<object> content = new List<object>();

        object aWebView = new {ContentType="web",Width="300", Height = "300",X="10",Y="30",ContentUrl="http://www.google.com" };
        content.Add(aWebView);
        object aWebView2 = new { ContentType = "image", Width = "300", Height = "300", X = "20", Y = "40", ContentUrl = "https://www.nasa.gov/sites/default/files/styles/image_card_4x3_ratio/public/thumbnails/image/leisa_christmas_false_color.png?itok=Jxf0IlS4" };
        content.Add(aWebView2);
        FrameLayout myLayout = (FrameLayout)FindViewById(Resource.Id.frameLayout1);
        foreach (object item in content)
        {

            string contentType = item.GetType().GetProperty("ContentType").GetValue(item, null).ToString();
            FrameLayout.LayoutParams param = new FrameLayout.LayoutParams(Convert.ToInt32(item.GetType().GetProperty("Width").GetValue(item, null).ToString()), Convert.ToInt32(item.GetType().GetProperty("Height").GetValue(item, null).ToString()));
            param.LeftMargin = Convert.ToInt32(item.GetType().GetProperty("X").GetValue(item, null).ToString());
            param.TopMargin = Convert.ToInt32(item.GetType().GetProperty("Y").GetValue(item, null).ToString());

            switch (contentType) {
                case "web":{
                        WebView webview = new WebView(this);

                        //webview.hei;
                        myLayout.AddView(webview, param);
                        webview.SetWebViewClient(new WebViewClient());
                        webview.LoadUrl(item.GetType().GetProperty("ContentUrl").GetValue(item, null).ToString());

                        break;
                    }
                case "image":
                    {
                        ImageView imageview = new ImageView(this);

                        //webview.hei;
                        myLayout.AddView(imageview, param);
                        var imageBitmap =  GetImageBitmapFromUrl("https://www.nasa.gov/sites/default/files/styles/image_card_4x3_ratio/public/thumbnails/image/leisa_christmas_false_color.png?itok=Jxf0IlS4");
                        imageview.SetImageBitmap(imageBitmap);


                        break;
                    }

            }

        }

Isso foi útil para mim, porque eu precisava que a propriedade de visão se sobrepusesse com base em sua aparência, por exemplo, as visualizações são empilhadas uma sobre a outra .

Technacron
fonte