Ustaw bezwzględną pozycję widoku


180

Czy można ustawić bezwzględną pozycję widoku w Androidzie? (Wiem, że istnieje AbsoluteLayout, ale jest przestarzałe ...)

Na przykład, jeśli mam ekran o wymiarach 240 x 320 pikseli, jak mogę dodać obraz o ImageViewwymiarach 20 x 20 pikseli, tak aby jego środek znajdował się na pozycji (100 100)?


3
zobacz także view.setTranslationX()lubview.offsetLeftAndRight()
Drew LeSueur,

Właśnie wydałem bibliotekę, która mogła być tutaj interesująca. github.com/ManuelPeinado/ImageLayout
Manuel

1
Jest to tak trudne, ponieważ 99,9% czasu absolutnego pozycjonowania to zły pomysł na Androida. Jeśli piszesz aplikację, która będzie działać TYLKO na jednym urządzeniu fizycznym, może to działać, ale generalnie nie jest to bezpieczne założenie. Na przykład nie przesyłaj tego do Google Play. Działa dobrze na iOS, ponieważ jest tylko garść urządzeń sprzętowych, i możesz zbudować niestandardową scenorys dla każdego z nich.
edtthth

2
@edthethird, W mojej aplikacji na wiele platform otrzymuję rozmiar ekranu i wszystko na nim opieram. Właśnie przełączyłem się na „przestarzałe” AbsoluteLayout i działa dobrze.
William Jockusch

sprawiedliwie, ale właśnie to automatycznie zrobi dla ciebie Układ względny lub Układ liniowy.
edthethth

Odpowiedzi:


271

Możesz użyć RelativeLayout. Powiedzmy, że chciałeś mieć ImageView 30x40 w pozycji (50,60) wewnątrz swojego układu. Gdzieś w twojej działalności:

// 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);

Więcej przykładów:

Umieszcza dwa 30x40 ImageViews (jeden żółty, jeden czerwony) odpowiednio w (50,60) i (80,90):

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);

Umieszcza jeden 30x40 żółty ImageView w (50,60), a drugi 30x40 czerwony ImageView <80,90> względem żółtego ImageView:

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);

1
Spróbuję dziś wieczorem, to całkiem niezły pomysł, nie wiem, dlaczego o tym nie pomyślałem. Jak mam kilka ImageViewdo dodania, czy nie lepiej byłoby użyć FrameLayout?
Sephy

Rzeczywiście wydaje się, że to działa, ale działa tylko wtedy, gdy dodaj jedno zdjęcie w ten sposób. jeśli spróbuję dodać drugi, pierwszy znika po prostu ...
Sephy

1
Dwa obrazy znajdują się jeden na drugim. Dodam trochę kodu do powyższego rozwiązania w celu wyjaśnienia.
Andy Zhang,

2
Tak, rzeczywiście, odkryłem to również wczoraj, samodzielnie wykopując twoje rozwiązanie. Próbuję też rzeczy z FrameLayout. Mój faktyczny problem polega na tym, że mam 5 zdjęć z każdą losową (x, y) pozycją, więc nie mogę użyć RelativeLayout.RIGHT_OF ani czegoś takiego. Dziwne jest to, że mogę poprawnie umieścić 3 obrazy, ale 2 z nich nie działają ... Nie rozumiem ... Dzisiaj zaktualizuję mój post z zrzutami ekranu i kodem.
Sephy

9
Dlaczego użycie tej metody byłoby lepsze niż użycie AbsoluteLayout? Tylko dlatego, że AbsoluteLayout jest przestarzały?
Nathan

66

Zasadniczo można dodać widok w określonej pozycji za pomocą FrameLayout jako kontenera, określając atrybuty leftMargin i topMargin .

Poniższy przykład umieści ImageView 20x20px w pozycji (100,200) przy użyciu FrameLayout jako kontenera pełnoekranowego:

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>

Aktywność / fragment / widok niestandardowy

//...
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);
//...

To załatwi sprawę, ponieważ marginesów można użyć jako współrzędnych bezwzględnych (X, Y) bez RelativeLayout:

wprowadź opis zdjęcia tutaj


2
Znakomity! Warto 500! +1
Lisa Anne

16

Aby dodać do powyższej odpowiedzi Andy Zhanga, jeśli chcesz, możesz podać parametr rl.addView, a następnie wprowadzić zmiany w późniejszym czasie, aby:

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

Równie dobrze można zapisać jako:

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

Więc jeśli zachowasz zmienną params, możesz zmienić układ iv w dowolnym momencie po dodaniu go do rl.


uwielbiałem tę próbkę. Czy możesz mi zasugerować, jak mogę ustawić oś x, y, gdy mam obraz w układzie xml. (Próbuję zmienić rozmiar obrazu i muszę ustawić obraz w pewnej pozycji)
G_S

Obawiam się, że nie rozumiem o co pytasz. Czy próbujesz uzyskać dostęp do obiektu LayoutParams powiązanego z obiektem, który został ustawiony za pomocą układu XML? Nie jestem pewien, jak to się robi. Może warto postawić nowe pytanie, aby uzyskać na nie odpowiedź, jeśli nie możesz znaleźć odpowiedzi w innym miejscu.
Excrubulent


5

Bardziej przejrzysty i dynamiczny sposób bez twardego kodowania wartości pikseli w kodzie.

Chciałem ustawić okno dialogowe (które nadmuchuję w locie) dokładnie pod klikniętym przyciskiem.

i rozwiązałem to w ten sposób:

    // 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;
    }

Musisz jednak upewnić się, że układ nadrzędny myViewjest instancją RelativeLayout.

pełniejszy kod:

    // 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;
    }

W ten sposób nadal możesz oczekiwać, że widok docelowy dostosuje się do dowolnych parametrów układu ustawionych za pomocą plików XML układu, zamiast kodować te piksele / dps w kodzie Java.


1

Sprawdź zrzut ekranu

Umieść dowolny widok na pożądanym punkcie X i Y

plik układu

<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>

Klasa 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);
    }
}

Gotowe


0

Na wszelki wypadek może komuś pomóc, możesz również wypróbować tego animatora ViewPropertyAnimator, jak poniżej

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

myView.animate().translateX(pixelInScreen) 

Uwaga: Ten piksel nie jest związany z widokiem. Ten piksel to pozycja piksela na ekranie.

kredyty do odpowiedzi bpr10


-1

Wypróbuj poniższy kod, aby ustawić widok w określonej lokalizacji: -

            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);

-1

Mój kod dla Xamarin , używam w tym celu FrameLayout, a oto mój kod:

               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;
                    }

            }

        }

Przydało mi się to, ponieważ potrzebowałem, aby właściwość widoku nakładała się na siebie na podstawie ich wyglądu, np. Widoki były ułożone jeden na drugim .

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.