Ubah Margin yang Tepat dari Tampilan Secara Programatis?

173

Bisakah atribut ini diubah secara dinamis dalam kode Java?

android:layout_marginRight

Saya punya TextView, yang harus mengubah posisinya beberapa piksel ke kiri secara dinamis.

Bagaimana cara melakukannya secara terprogram?

NullPointerException
sumber

Jawaban:

463

EDIT: Cara yang lebih umum untuk melakukan ini yang tidak bergantung pada tipe tata letak (selain itu itu adalah tipe tata letak yang mendukung margin):

public static void setMargins (View v, int l, int t, int r, int b) {
    if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
        ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
        p.setMargins(l, t, r, b);
        v.requestLayout();
    }
}

Anda harus memeriksa dokumen untuk TextView . Pada dasarnya, Anda ingin mendapatkan objek LayoutParams TextView, dan memodifikasi margin, lalu mengaturnya kembali ke TextView. Dengan asumsi itu ada dalam LinearLayout, coba sesuatu seperti ini:

TextView tv = (TextView)findViewById(R.id.my_text_view);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)tv.getLayoutParams();
params.setMargins(0, 0, 10, 0); //substitute parameters for left, top, right, bottom
tv.setLayoutParams(params);

Saya tidak bisa mengujinya sekarang, jadi casting saya mungkin tidak aktif sedikit, tetapi LayoutParams adalah apa yang perlu dimodifikasi untuk mengubah margin.

CATATAN

Jangan lupa bahwa jika TextView Anda ada di dalam, misalnya, RelativeLayout , orang harus menggunakan RelativeLayout.LayoutParams alih-alih LinearLayout.LayoutParams

Kevin Coppock
sumber
39
Hanya untuk menguraikan jawaban Anda jika orang lain mencari ini. Jika Anda membuat TextView secara terprogram maka Anda perlu membuat objek LinearLayout.LayoutParams yang baru juga, alih-alih mencoba mengeluarkannya melalui .getLayoutParams ();
Ares
Ini berfungsi luar biasa, dan sepertinya itu mengatur margin dalam piksel. Apakah mungkin untuk mengaturnya dalam dp?
SirRupertIII
3
@KKendall: Konversikan DP Anda ke PX terlebih dahulu.
Kevin Coppock
Sesuai CATATAN Anda: Saya ragu. Jika tampilan teks atau tombol di dalam baris tabel? Lalu apa yang harus saya gunakan daripada Relatif Layout, Table Layout?
tejas
1
Terkait dengan catatan tersebut, Anda bisa menggunakan tv.getLayoutParams () ke ViewGroup.MarginLayoutParams. Dengan cara ini, tidak masalah tata letak apa yang digunakan oleh orang tua tersebut, asalkan menerapkan margin
Radu Simionescu
17

Gunakan LayoutParams (seperti yang sudah dijelaskan). Namun hati-hati LayoutParams mana yang harus dipilih. Menurut https://stackoverflow.com/a/11971553/3184778 "Anda harus menggunakan yang terkait dengan ORANGTUA dari tampilan yang sedang Anda kerjakan, bukan tampilan yang sebenarnya"

Jika misalnya TextView ada di dalam TableRow, maka Anda perlu menggunakan TableRow.LayoutParams alih-alih RelativeLayout atau LinearLayout

kouretinho
sumber
Terima kasih telah mengklarifikasi!
Scott Biggs
1
benar-benar menyebalkan bahwa Anda harus mengetahui tujuan kotak teks untuk mengatur margin ... Anda akan berpikir akan ada solusi yang independen terhadap tujuan.
TatiOverflow
10

Gunakan fungsi ini untuk mengatur semua jenis margin

      public void setViewMargins(Context con, ViewGroup.LayoutParams params,      
       int left, int top , int right, int bottom, View view) {

    final float scale = con.getResources().getDisplayMetrics().density;
    // convert the DP into pixel
    int pixel_left = (int) (left * scale + 0.5f);
    int pixel_top = (int) (top * scale + 0.5f);
    int pixel_right = (int) (right * scale + 0.5f);
    int pixel_bottom = (int) (bottom * scale + 0.5f);

    ViewGroup.MarginLayoutParams s = (ViewGroup.MarginLayoutParams) params;
    s.setMargins(pixel_left, pixel_top, pixel_right, pixel_bottom);

    view.setLayoutParams(params);
}
Rohit Rathore
sumber
2
Mengapa 0,5f tambahan?
behelit
2
@ Behelit Saya tahu ini terlambat tetapi bagi siapa pun yang melihat ... ketika Anda melemparkan pelampung ke int, pelampung putaran. 0,0 -> 0,4 ​​putaran ke 0 sementara 0,5 -> 0,9 putaran ke 1. Ini dapat menciptakan inkonsistensi dalam int final. Untuk mencegah hal ini, menambahkan 0,5 memastikan semua pembulatan adalah ke 1. Mengapa? katakan float Anda adalah 0,3: 0,3 + 0,5 = 0,8 yang membulatkan ke 1. Katakanlah float Anda adalah 0,8: 0,8 + 0,5 = 1,3 yang membulat BAWAH ke 1. Sekarang Anda bisa aman dalam pengetahuan Anda tentang pembulatan akhir (SIDE NOTE: kami menambahkan 0,5 daripada mengurangi untuk menghindari int negatif)
Psest328
7

Pembaruan: Android KTX

The modul inti KTX menyediakan ekstensi untuk perpustakaan umum yang merupakan bagian dari kerangka Android, androidx.core.viewdi antara mereka.

dependencies {
    implementation "androidx.core:core-ktx:{latest-version}"
}

Fungsi ekstensi berikut berguna untuk menangani margin:

Mengatur margin semua sumbu di ViewGroup's MarginLayoutParams. (Dimensi harus disediakan dalam piksel, lihat bagian terakhir jika Anda ingin bekerja dengan dp)

inline fun MarginLayoutParams.setMargins(@Px size: Int): Unit
// E.g. 16px margins
val params = (myView.layoutParams as ViewGroup.MarginLayoutParams)
params.setMargins(16)

Update margin di ViewGroup's ViewGroup.MarginLayoutParams.

inline fun MarginLayoutParams.updateMargins(
    @Px left: Int = leftMargin, 
    @Px top: Int = topMargin, 
    @Px right: Int = rightMargin, 
    @Px bottom: Int = bottomMargin
): Unit
// Example: 8px left margin 
params.updateMargins(left = 8)

Memperbarui margin relatif ViewGroupdi MarginLayoutParams(mulai / akhir bukan kiri / kanan).

inline fun MarginLayoutParams.updateMarginsRelative(
    @Px start: Int = marginStart, 
    @Px top: Int = topMargin, 
    @Px end: Int = marginEnd, 
    @Px bottom: Int = bottomMargin
): Unit
// E.g: 8px start margin 
params.updateMargins(start = 8)

Properti ekstensi berikut berguna untuk mendapatkan margin saat ini:

inline val View.marginBottom: Int
inline val View.marginEnd: Int
inline val View.marginLeft: Int
inline val View.marginRight: Int
inline val View.marginStart: Int
inline val View.marginTop: Int
// E.g: get margin bottom
val bottomPx = myView1.marginBottom
  • Menggunakan dpbukanpx :

Jika Anda ingin bekerja dengan dp(piksel yang tidak tergantung kepadatan) px, Anda harus mengonversinya terlebih dahulu. Anda dapat dengan mudah melakukannya dengan properti ekstensi berikut:

val Int.px: Int
    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

Kemudian Anda dapat memanggil fungsi ekstensi sebelumnya seperti:

params.updateMargins(start = 16.px, end = 16.px, top = 8.px, bottom = 8.px)
val bottomDp = myView1.marginBottom.dp

Jawaban lama:

Di Kotlin Anda dapat mendeklarasikan fungsi ekstensi seperti:

fun View.setMargins(
    leftMarginDp: Int? = null,
    topMarginDp: Int? = null,
    rightMarginDp: Int? = null,
    bottomMarginDp: Int? = null
) {
    if (layoutParams is ViewGroup.MarginLayoutParams) {
        val params = layoutParams as ViewGroup.MarginLayoutParams
        leftMarginDp?.run { params.leftMargin = this.dpToPx(context) }
        topMarginDp?.run { params.topMargin = this.dpToPx(context) }
        rightMarginDp?.run { params.rightMargin = this.dpToPx(context) }
        bottomMarginDp?.run { params.bottomMargin = this.dpToPx(context) }
        requestLayout()
    }
}

fun Int.dpToPx(context: Context): Int {
    val metrics = context.resources.displayMetrics
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), metrics).toInt()
}

Maka Anda bisa menyebutnya seperti:

myView1.setMargins(8, 16, 34, 42)

Atau:

myView2.setMargins(topMarginDp = 8) 
David Miguel
sumber