Ostatnie kilka godzin pisania aplikacji poświęciłem na walce z dynamicznym tworzeniem elementów GUI w kodzie. Udało mi się w końcu wstępnie uzyskać efekt, który chciałem, i który jeszcze dopracuję. Problemem okazał się mały szczegół, o którym nie widziałem…

Problem, który chcę opisać, napotkałem przy tworzeniu ekranu wyświetlającego szczegóły dokumentu. Na tym ekranie powinien być widoczny tytuł dokumentu, tagi oraz wszystkie pliki wraz z ich opisami. I przy tym ostatnim elemencie powstały problemy.

Screenshot_2010-04-01-02-58-53

Powyżej wstawiłem efekt jaki bym chciał osiągnąć. Chodzi oczywiście o rozmieszczenie elementów – niebieskie linie oraz zielony kolor zostały dodane dla lepszej widoczności co się dzieję. Wracając do układu elementów – pliki chciałbym wyświetlić w formie tabelarycznej, gdzie w lewej kolumnie będzie miniaturka zdjęcia/pliku, a w prawej będzie dodany opis pliku.

Powyższy efekt udało się uzyskać przy pomocy następującego kodu:

<TableRow
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="110dp"
        android:layout_height="110dp">

        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="center_horizontal|center_vertical"
            android:src="@android:drawable/ic_dialog_map" />
    </FrameLayout>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="110dp"
        android:scrollHorizontally="false"
        android:text="Ut ac libero tincidunt, bibendum sem nec, pellentesque sem." />
</TableRow>

Czy tworzę TableRow i są w nim dwa elementy:

  • FrameLayout, który w sobie zawiera ImageView z obrazem
  • TextView, który zawiera opis pliku

Oczywiście kod ten w XMLu stworzyłem tylko po to, żeby zobaczyć, których elementów mam użyć, żeby uzyskać efekt jaki chcę. Sama lista plików z opisami będzie oczywiście tworzona dynamicznie w kodzie.

Jeden wiersz tworzyłem mniej więcej w poniższy sposób:

val filesLayout = findViewById(R.id.show_document_files_table).asInstanceOf[TableLayout]

val dim110 = (110 * this.getResources.getDisplayMetrics.density + 0.5f).toInt

val tableRow = new TableRow(this)
tableRow.setLayoutParams(new TableRow.LayoutParams(MATCH_PARENT, MATCH_PARENT))

val imageFrameLayout = new FrameLayout(this)
imageFrameLayout.setLayoutParams(new FrameLayout.LayoutParams(dim110, dim110))

val imageView: ImageView = new ImageView(this)
imageView.setImageBitmap(...)
val layoutParams = new LinearLayout.LayoutParams(thumbnail.getWidth, thumbnail.getHeight)
layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL
imageView.setLayoutParams(layoutParams)

imageFrameLayout.addView(imageView)

tableRow.addView(imageFrameLayout)

val fileDescription = new TextView(this)
fileDescription.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, dim110, 0.1f))
fileDescription.setText(...)

tableRow.addView(fileDescription)

filesLayout.addView(tableRow)

Tworząc elementy tabeli w kodzie starałem się tworzył dokładnie te same elementy z tymi samymi parametrami, które użyłem w XMLu. Niestety tworzenie wiersza tabeli w powyższy sposób powodował, że na ekranie nic się nie pojawiało – tabela zostawała pusta. Metodą prób i błędów próbowałem dojść do tego co jest nie tak i w końcu mi się udało.

Okazało się, że przy tworzeniu ustawień layouty, czyli tworząc obiekty LayoutParams musimy użyć LayoutParams z klasy rodzica danego elementu, a nie z samego elementu. Czyli w przypadku gdzie tworzyłem wiersz tabeli w poniższy sposób

val tableRow = new TableRow(this)
tableRow.setLayoutParams(new TableRow.LayoutParams(MATCH_PARENT, MATCH_PARENT))

wystarczyło zamienić na:

val tableRow = new TableRow(this)
tableRow.setLayoutParams(new TableLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))

Czyli użyć TableLayout.LayoutParams zamiast TableRow.LayoutParams, ponieważ TableLayout jest rodzicem naszego wiersza.

Po takich zmianach w kodzie ekran wyświelił elementy w taki sposób w jaki chciałem. Kod po modyfikacjach wyglada następująco:

val filesLayout = findViewById(R.id.show_document_files_table).asInstanceOf[TableLayout]

val dim110 = (110 * this.getResources.getDisplayMetrics.density + 0.5f).toInt

val tableRow = new TableRow(this)
tableRow.setLayoutParams(new TableLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))

val imageFrameLayout = new FrameLayout(this)
imageFrameLayout.setLayoutParams(new TableRow.LayoutParams(dim110, dim110))

val imageView: ImageView = new ImageView(this)
imageView.setImageBitmap(...)
val layoutParams = new FrameLayout.LayoutParams(thumbnail.getWidth, thumbnail.getHeight)
layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL
imageView.setLayoutParams(layoutParams)

imageFrameLayout.addView(imageView)

tableRow.addView(imageFrameLayout)

val fileDescription = new TextView(this)
fileDescription.setLayoutParams(new TableRow.LayoutParams(WRAP_CONTENT, dim110, 0.1f))
fileDescription.setText(...)

tableRow.addView(fileDescription)

filesLayout.addView(tableRow)